Skip to content

Commit 4f86676

Browse files
author
tfomkin
committed
fix: allow user to send new messages after generation stop
1 parent 146bffe commit 4f86676

4 files changed

Lines changed: 64 additions & 31 deletions

File tree

libs/mobile/chat/features/chat/src/lib/component.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,9 @@ export function Chat({ chatId, selectedModelId, isNewChat, resetToChatsList }: C
173173
attachedImages={attachedImages}
174174
onImageUploaded={handleImageUploaded}
175175
onDeleteImagePress={handleDeleteImage}
176-
chatId={chatId}
177176
modelId={selectedModelId}
178177
isResponseGenerating={isResponseGenerating}
178+
chat={chat}
179179
/>
180180
)}
181181
</View>

libs/mobile/chat/features/form-chat-input/src/lib/component.tsx

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { AppTextInput, AppInputProps, View, IconButton } from '@open-webui-react
1616
import {
1717
appConfigurationApi,
1818
ChatGenerationOption,
19+
ChatResponse,
1920
tasksApi,
2021
tasksService,
2122
} from '@open-webui-react-native/shared/data-access/api';
@@ -36,7 +37,7 @@ interface FormChatInputProps<T extends FieldValues> extends AppInputProps {
3637
attachedImages: Observable<Array<ImageData>>;
3738
onImageUploaded: (image: ImageData) => void;
3839
onDeleteImagePress: (fileName: string) => void;
39-
chatId?: string;
40+
chat?: ChatResponse;
4041
modelId?: string;
4142
onChatCreated?: (id: string) => void;
4243
isLoading?: boolean;
@@ -58,7 +59,7 @@ export function FormChatInput<T extends FieldValues>({
5859
attachedImages,
5960
onImageUploaded,
6061
onDeleteImagePress,
61-
chatId,
62+
chat,
6263
modelId,
6364
onChatCreated,
6465
isLoading,
@@ -96,7 +97,7 @@ export function FormChatInput<T extends FieldValues>({
9697
return ToastService.showError(translate('TEXT_MODEL_NOT_SELECTED'));
9798
}
9899
setIsMicrophonePreparing(true);
99-
await openVoiceModeModal({ chatId, modelId });
100+
await openVoiceModeModal({ chatId: chat?.id, modelId });
100101
setIsMicrophonePreparing(false);
101102
};
102103

@@ -119,13 +120,16 @@ export function FormChatInput<T extends FieldValues>({
119120
};
120121

121122
const onStopGenerationPress = async (): Promise<void> => {
122-
if (!chatId) return;
123+
if (!chat) return;
124+
125+
const chatId = chat.id;
126+
const lastMessageId = chat.chat.history.currentId;
123127

124128
const tasksData = await tasksService.getChatTasks(chatId);
125129
const taskId = tasksData?.tasksIds[0];
126130

127131
if (taskId) {
128-
stopTaskMutation.mutate(taskId);
132+
stopTaskMutation.mutate({ taskId, chatId, lastMessageId });
129133
}
130134
};
131135

@@ -163,6 +167,8 @@ export function FormChatInput<T extends FieldValues>({
163167
isSubmitDisabled={!isFeatureEnabled(FeatureID.VOICE_MODE) && isInputEmpty}
164168
onVoiceModePress={onVoiceModePress}
165169
isVoiceModeAvailable={isFeatureEnabled(FeatureID.VOICE_MODE) && isInputEmpty}
170+
onStopGenerationPress={onStopGenerationPress}
171+
isResponseGenerating={isResponseGenerating}
166172
isLoading={isLoading || isMicrophonePreparing}>
167173
<View className='flex-row flex-1 justify-between'>
168174
<View className='gap-16 flex-row '>
@@ -180,20 +186,12 @@ export function FormChatInput<T extends FieldValues>({
180186
/>
181187
)}
182188
</View>
183-
<View className={'flex-row gap-16 pr-16'}>
184-
{isResponseGenerating && (
185-
<IconButton
186-
iconName='stopCircle'
187-
className='p-0'
188-
onPress={onStopGenerationPress} />
189-
)}
190-
<IconButton
191-
disabled={isLoading}
192-
iconName='microphone'
193-
className='p-0'
194-
onPress={handleDictateModePress}
195-
/>
196-
</View>
189+
<IconButton
190+
disabled={isLoading}
191+
iconName='microphone'
192+
className='p-0 mr-16'
193+
onPress={handleDictateModePress}
194+
/>
197195
</View>
198196
</ChatInputBottomRow>
199197
}

libs/mobile/chat/features/form-chat-input/src/lib/components/chat-input-bottom-row/component.tsx

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { IconButton, View } from '@open-webui-react-native/mobile/shared/ui/ui-k
44
export interface ChatInputBottomRowProps extends PropsWithChildren {
55
onSubmit: () => void;
66
onVoiceModePress: () => void;
7+
onStopGenerationPress: () => void;
8+
isResponseGenerating?: boolean;
79
isVoiceModeAvailable?: boolean;
810
isSubmitDisabled?: boolean;
911
isLoading?: boolean;
@@ -16,18 +18,27 @@ export function ChatInputBottomRow({
1618
isLoading,
1719
children,
1820
isSubmitDisabled,
21+
isResponseGenerating,
22+
onStopGenerationPress,
1923
}: ChatInputBottomRowProps): ReactElement {
2024
return (
2125
<View className='flex-row justify-between items-center mt-12'>
2226
{children}
23-
<IconButton
24-
disabled={isSubmitDisabled}
25-
onPress={isVoiceModeAvailable ? onVoiceModePress : onSubmit}
26-
iconName={isVoiceModeAvailable ? 'headphones' : 'arrowUp'}
27-
className='rounded-full self-end bg-text-primary p-4'
28-
iconProps={{ className: 'color-background-primary' }}
29-
isLoading={isLoading}
30-
/>
27+
{isResponseGenerating ? (
28+
<IconButton
29+
iconName='stopCircle'
30+
className='p-0'
31+
onPress={onStopGenerationPress} />
32+
) : (
33+
<IconButton
34+
disabled={isSubmitDisabled}
35+
onPress={isVoiceModeAvailable ? onVoiceModePress : onSubmit}
36+
iconName={isVoiceModeAvailable ? 'headphones' : 'arrowUp'}
37+
className='rounded-full self-end bg-text-primary p-4'
38+
iconProps={{ className: 'color-background-primary' }}
39+
isLoading={isLoading}
40+
/>
41+
)}
3142
</View>
3243
);
3344
}

libs/shared/data-access/api/src/lib/tasks/api.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,38 @@
11
import { useMutation, UseMutationOptions, UseMutationResult } from '@tanstack/react-query';
22
import { AxiosError } from 'axios';
33
import { ApiErrorData } from '@open-webui-react-native/shared/data-access/api-client';
4+
import { Chat, patchChatQueryData } from '../chats';
45
import { StopTaskResponse } from './models';
56
import { tasksService } from './service';
67

8+
type StopTaskArgs = {
9+
taskId: string;
10+
chatId: string;
11+
lastMessageId: string;
12+
};
13+
714
function useStopTask(
8-
props?: UseMutationOptions<StopTaskResponse, AxiosError<ApiErrorData>, string>,
9-
): UseMutationResult<StopTaskResponse, AxiosError<ApiErrorData>, string> {
15+
props?: UseMutationOptions<StopTaskResponse, AxiosError<ApiErrorData>, StopTaskArgs>,
16+
): UseMutationResult<StopTaskResponse, AxiosError<ApiErrorData>, StopTaskArgs> {
1017
return useMutation({
11-
mutationFn: (taskId: string) => tasksService.stopTask(taskId),
18+
mutationFn: ({ taskId }) => tasksService.stopTask(taskId),
19+
20+
onSuccess: (_, { chatId, lastMessageId }) => {
21+
patchChatQueryData(chatId, {
22+
chat: {
23+
history: {
24+
messages: {
25+
[lastMessageId]: {
26+
done: true,
27+
},
28+
},
29+
},
30+
} as Chat,
31+
});
32+
33+
props?.onSuccess?.(_, { chatId, lastMessageId } as any, undefined);
34+
},
35+
1236
...props,
1337
});
1438
}

0 commit comments

Comments
 (0)