Skip to content

Commit

Permalink
test take picture on mobile
Browse files Browse the repository at this point in the history
  • Loading branch information
mingming-ma committed Nov 27, 2023
1 parent b4a283c commit 64894cd
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 32 deletions.
9 changes: 8 additions & 1 deletion src/components/PromptForm/ClipIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,14 @@ export default function ClipIcon({ isDisabled = false, onFileSelected }: ClipIco

return (
<>
<Input type="file" ref={fileInputRef} hidden onChange={handleFileChange} accept="image/*" />
<Input
type="file"
ref={fileInputRef}
hidden
onChange={handleFileChange}
accept="image/*"
capture="environment"
/>
<IconButton
onClick={handleClick}
isRound
Expand Down
124 changes: 93 additions & 31 deletions src/components/PromptForm/MobilePromptForm.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { FormEvent, KeyboardEvent, useEffect, useState, type RefObject } from "react";
import { Box, chakra, Flex } from "@chakra-ui/react";
import { Box, chakra, Flex, Image, CloseButton } from "@chakra-ui/react";
import AutoResizingTextarea from "../AutoResizingTextarea";

import { useSettings } from "../../hooks/use-settings";
import NewButton from "../NewButton";
import MicIcon from "./MicIcon";
import ClipIcon from "./ClipIcon";
import { isTranscriptionSupported } from "../../lib/speech-recognition";
import { useModels } from "../../hooks/use-models";
import PromptSendButton from "./PromptSendButton";
import AudioStatus from "./AudioStatus";
import { useKeyDownHandler } from "../../hooks/use-key-down-handler";

type MobilePromptFormProps = {
forkUrl: string;
onSendClick: (prompt: string) => void;
onSendClick: (prompt: string, image: string[]) => void;
inputPromptRef: RefObject<HTMLTextAreaElement>;
isLoading: boolean;
previousMessage?: string;
Expand All @@ -28,11 +30,14 @@ function MobilePromptForm({
const [prompt, setPrompt] = useState("");
// Has the user started typing?
const [isDirty, setIsDirty] = useState(false);
const { settings } = useSettings();
const { models } = useModels();
const { settings, setSettings } = useSettings();
const [isRecording, setIsRecording] = useState(false);
const [isTranscribing, setIsTranscribing] = useState(false);
const [recordingSeconds, setRecordingSeconds] = useState(0);
const inputType = isRecording || isTranscribing ? "audio" : "text";
// Base64 images
const [inputImages, setInputImages] = useState<string[]>([]);

// If the user clears the prompt, allow up-arrow again
useEffect(() => {
Expand Down Expand Up @@ -67,12 +72,25 @@ function MobilePromptForm({
};
}, [isRecording, recordingSeconds]);

// Update model to the supported model when inputImages is not empty
useEffect(() => {
if (inputImages && inputImages.length > 0) {
const modelSupportsImages = models.find((model) => model.supportsImages);
if (modelSupportsImages) {
setSettings({ ...settings, model: modelSupportsImages });
}
}
// Assuming models and setSettings are stable and don't need to be in the dependencies array
// eslint-disable-next-line
}, [inputImages]);

// Handle prompt form submission
const handlePromptSubmit = (e: FormEvent) => {
e.preventDefault();
const value = prompt.trim();
const textValue = prompt.trim();
setPrompt("");
onSendClick(value);
setInputImages([]);
onSendClick(textValue, inputImages);
};

const handleMetaEnter = useKeyDownHandler<HTMLTextAreaElement>({
Expand Down Expand Up @@ -133,40 +151,84 @@ function MobilePromptForm({
setIsTranscribing(false);

// Use this transcript as our prompt
onSendClick(transcription);
onSendClick(transcription, inputImages);
setInputImages([]);
};

const handleDeleteImage = (index: number) => {
const updatedImages = [...inputImages];
updatedImages.splice(index, 1);
setInputImages(updatedImages);
};

return (
<Box flex={1} w="100%" h="100%" px={1} pt={2} pb={4}>
<chakra.form onSubmit={handlePromptSubmit} h="100%">
<Flex mt={2} pb={2} px={1} alignItems="end" gap={2}>
<NewButton forkUrl={forkUrl} variant="outline" iconOnly />

<Box flex={1}>
{inputType === "audio" ? (
<Box py={2} px={1}>
<AudioStatus
isRecording={isRecording}
isTranscribing={isTranscribing}
recordingSeconds={recordingSeconds}
<Box flex={1}>
<Flex flexWrap="wrap">
{inputImages.map((image, index) => (
<Box
key={index}
position="relative"
height="70px"
display="flex"
alignItems="center"
m={2}
>
<Image
src={image}
alt={`Image# ${index}`}
style={{ height: "70px", objectFit: "cover" }}
cursor="pointer"
/>
<Box
key={`${index}-close`}
display="flex"
alignItems="center"
justifyContent="center"
top="0"
right="0"
backgroundColor="grey"
color="white"
height="70px"
>
<CloseButton onClick={() => handleDeleteImage(index)} />
</Box>
</Box>
) : (
<AutoResizingTextarea
ref={inputPromptRef}
onKeyDown={handleKeyDown}
isDisabled={isLoading}
autoFocus={true}
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
bg="white"
_dark={{ bg: "gray.700" }}
overflowY="auto"
placeholder="Ask a question"
mb={1}
))}
</Flex>
{inputType === "audio" ? (
<Box py={2} px={1}>
<AudioStatus
isRecording={isRecording}
isTranscribing={isTranscribing}
recordingSeconds={recordingSeconds}
/>
)}
</Box>
</Box>
) : (
<AutoResizingTextarea
ref={inputPromptRef}
onKeyDown={handleKeyDown}
isDisabled={isLoading}
autoFocus={true}
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
bg="white"
_dark={{ bg: "gray.700" }}
overflowY="auto"
placeholder="Ask a question"
mb={1}
/>
)}
</Box>
<Flex mt={2} pb={2} px={1} alignItems="end" gap={2} justify="space-evenly">
<NewButton forkUrl={forkUrl} variant="outline" iconOnly />
<ClipIcon
isDisabled={isLoading}
onFileSelected={(base64String) =>
setInputImages((prevImages) => [...prevImages, base64String])
}
/>

{isTranscriptionSupported() && (
<MicIcon
Expand Down

0 comments on commit 64894cd

Please sign in to comment.