diff --git a/src/components/screens-component/chat-screen/components/chat-input.tsx b/src/components/screens-component/chat-screen/components/chat-input.tsx index b725071..b7de706 100644 --- a/src/components/screens-component/chat-screen/components/chat-input.tsx +++ b/src/components/screens-component/chat-screen/components/chat-input.tsx @@ -22,6 +22,7 @@ import type { Suggestion } from "../api/suggestions-api"; import { useLanguage } from "@/components/LanguageProvider"; import { useAuth } from "@/contexts/AuthContext"; import { useChatStore } from "@/hooks/store/chat"; +import { useIsMobile } from "@/hooks/use-mobile"; import { environment } from "@/lib/config/environment"; const MAX_CLIENT_IMAGE_SIZE_BYTES = 10 * 1024 * 1024; @@ -71,6 +72,7 @@ export function ChatInput({ const { t } = useLanguage(); const { user } = useAuth(); const setToast = useChatStore((s) => s.setToast); + const isMobile = useIsMobile(); const isUnauthenticated = !user; const [isPestDialogOpen, setIsPestDialogOpen] = useState(false); const [pestImage, setPestImage] = useState(null); @@ -331,70 +333,84 @@ export function ChatInput({ return (
- + + {/* Warm amber header — softer than before */} -
- {t("pestApi.title") as string} - +
+
+ + + {t("pestApi.title") as string} + +
+ {t("pestApi.description") as string}
-
-
+ +
+ {/* Refined amber note with icon */} +
+ + + {t("pestApi.note") as string}
-
-

+ {/* Photo section */} +

+

+ {t("pestApi.photoLabel") as string}

+ {pestImagePreview ? ( -
+
{pestImage?.name - +
) : ( -
-
- +
+
+ {isMobile && ( + + )}
-

+

{t("imageUpload.imageFormatHint") as string}

@@ -417,11 +433,23 @@ export function ChatInput({ onChange={onPestFilePicked} />
- - - @@ -546,20 +574,19 @@ export function ChatInput({ {charCount}/{maxLength} )} -
+
diff --git a/src/hooks/store/chat/index.ts b/src/hooks/store/chat/index.ts index 664eb92..cb7c4ee 100644 --- a/src/hooks/store/chat/index.ts +++ b/src/hooks/store/chat/index.ts @@ -391,8 +391,8 @@ export const useChatStore = create((set, get) => ({ }; } }); - }, - () => set({ isInputLocked: false }) + } + // Note: input stays locked until sendUserQuery fully resolves (after all stream chunks) ); set({ isAssistantTyping: false, isInputLocked: false }); @@ -581,8 +581,8 @@ export const useChatStore = create((set, get) => ({ }; } }); - }, - () => set({ isInputLocked: false }) + } + // Note: input stays locked until sendImageQuery fully resolves (after all stream chunks) ); set({ isAssistantTyping: false, isInputLocked: false }); diff --git a/src/layouts/chat-layout/index.tsx b/src/layouts/chat-layout/index.tsx index 76943eb..e797b5a 100644 --- a/src/layouts/chat-layout/index.tsx +++ b/src/layouts/chat-layout/index.tsx @@ -31,23 +31,36 @@ function ChatLayout() { const [settingsOpen, setSettingsOpen] = useState(false); useEffect(() => { - if (typeof window === "undefined" || !navigator.geolocation || !navigator.permissions?.query) { + if (typeof window === "undefined" || !navigator.geolocation) { return; } let cancelled = false; - // Only auto-fetch when the browser already granted location permission. - void navigator.permissions - .query({ name: "geolocation" as PermissionName }) - .then((status) => { - if (!cancelled && status.state === "granted") { - fetchLocation(t); - } - }) - .catch(() => { - // Skip auto-request when the permission state cannot be checked. - }); + const requestLocation = () => { + if (!cancelled) fetchLocation(t); + }; + + // If the Permissions API is available, check the current state first. + // - "granted" → fetch silently (no prompt shown to user) + // - "prompt" → actively request so the browser shows the permission dialog + // - "denied" → skip (user has blocked it; don't bother) + if (navigator.permissions?.query) { + void navigator.permissions + .query({ name: "geolocation" as PermissionName }) + .then((status) => { + if (status.state === "granted" || status.state === "prompt") { + requestLocation(); + } + }) + .catch(() => { + // Fallback: just try — the browser will show its own prompt. + requestLocation(); + }); + } else { + // Permissions API not available — request directly. + requestLocation(); + } return () => { cancelled = true; diff --git a/translations/en.json b/translations/en.json index 3dfb6ab..dc1b257 100644 --- a/translations/en.json +++ b/translations/en.json @@ -286,10 +286,10 @@ "processingFailed": "Could not process the image. Please try another photo." }, "pestApi": { - "trigger": "Pest API", - "title": "Use Pest API", - "description": "Upload a crop image only for pest or disease identification.", - "note": "This is only for the Pest API flow. Use a clear photo of the affected crop, leaf, stem, fruit, or visible pest.", + "trigger": "Pest & Disease Detection", + "title": "Pest & Disease Detection", + "description": "Upload a crop image to identify pests or diseases affecting your crop.", + "note": "Use a clear photo of the affected crop, leaf, stem, fruit, or visible pest for best results.", "photoLabel": "Plant/Pest Image", "continue": "Continue", "submit": "Analyze & Submit", diff --git a/translations/gu.json b/translations/gu.json index 36498cb..82fb085 100644 --- a/translations/gu.json +++ b/translations/gu.json @@ -81,10 +81,10 @@ "processingFailed": "ફોટો પ્રોસેસ થઈ શક્યો નથી. કૃપા કરીને બીજો ફોટો અજમાવો." }, "pestApi": { - "trigger": "પેસ્ટ API", - "title": "પેસ્ટ API વાપરો", - "description": "પાકની છબી ફક્ત જીવાત અથવા રોગ ઓળખ માટે અપલોડ કરો.", - "note": "આ ફક્ત Pest API ફ્લો માટે છે. અસરગ્રસ્ત પાન, ડાંટી, ફળ, પાક અથવા દેખાતી જીવાતનો સ્પષ્ટ ફોટો લો.", + "trigger": "જીવાત અને રોગ ઓળખ", + "title": "જીવાત અને રોગ ઓળખ", + "description": "પાક પર જીવાત અથવા રોગ ઓળખવા માટે છબી અપલોડ કરો.", + "note": "સારા પરિણામ માટે અસરગ્રસ્ત પાન, ડાંટી, ફળ, પાક અથવા દેખાતી જીવાતનો સ્પષ્ટ ફોટો લો.", "photoLabel": "પાક/જીવાત ફોટો", "continue": "આગળ વધો", "submit": "વિશ્લેષણ કરો અને મોકલો", diff --git a/translations/hi.json b/translations/hi.json index 32d88b7..d9f852a 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -287,10 +287,10 @@ "processingFailed": "तस्वीर प्रोसेस नहीं हो सकी। कृपया दूसरी फोटो आज़माएँ।" }, "pestApi": { - "trigger": "पेस्ट API", - "title": "पेस्ट API इस्तेमाल करें", - "description": "फसल की तस्वीर केवल कीट या रोग पहचान के लिए अपलोड करें।", - "note": "यह केवल Pest API फ्लो के लिए है। प्रभावित पत्ती, तना, फल, फसल या दिखाई देने वाले कीट की साफ तस्वीर लें।", + "trigger": "कीट और रोग पहचान", + "title": "कीट और रोग पहचान", + "description": "फसल पर कीट या रोग की पहचान के लिए तस्वीर अपलोड करें।", + "note": "बेहतर परिणाम के लिए प्रभावित पत्ती, तना, फल, फसल या दिखाई देने वाले कीट की साफ तस्वीर लें।", "photoLabel": "पौधा/कीट तस्वीर", "continue": "जारी रखें", "submit": "विश्लेषण करें और भेजें", diff --git a/translations/mr.json b/translations/mr.json index 7c88837..b501c8c 100644 --- a/translations/mr.json +++ b/translations/mr.json @@ -120,10 +120,10 @@ "processingFailed": "फोटो प्रोसेस करता आला नाही. कृपया दुसरा फोटो वापरून पाहा." }, "pestApi": { - "trigger": "पेस्ट API", - "title": "पेस्ट API वापरा", - "description": "पीक प्रतिमा फक्त कीड किंवा रोग ओळखीसाठी अपलोड करा.", - "note": "हे फक्त Pest API फ्लोसाठी आहे. प्रभावित पान, खोड, फळ, पीक किंवा दिसणाऱ्या किडीचा स्पष्ट फोटो वापरा.", + "trigger": "कीड आणि रोग ओळख", + "title": "कीड आणि रोग ओळख", + "description": "पिकावरील कीड किंवा रोग ओळखण्यासाठी प्रतिमा अपलोड करा.", + "note": "चांगल्या परिणामासाठी प्रभावित पान, खोड, फळ, पीक किंवा दिसणाऱ्या किडीचा स्पष्ट फोटो वापरा.", "photoLabel": "पीक/कीड फोटो", "continue": "पुढे जा", "submit": "विश्लेषण करून पाठवा",