This commit is contained in:
Sam 2025-08-12 21:44:53 +05:30 committed by GitHub
commit 1df103280c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 322 additions and 27 deletions

View file

@ -27,6 +27,11 @@ interface SettingsType {
customOpenaiApiKey: string; customOpenaiApiKey: string;
customOpenaiApiUrl: string; customOpenaiApiUrl: string;
customOpenaiModelName: string; customOpenaiModelName: string;
weatherWidgetEnabled?: boolean;
automaticWeatherLocation?: boolean;
weatherLatitude?: string;
weatherLongitude?: string;
weatherLocationName?: string;
} }
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> { interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
@ -151,6 +156,13 @@ const Page = () => {
const [measureUnit, setMeasureUnit] = useState<'Imperial' | 'Metric'>( const [measureUnit, setMeasureUnit] = useState<'Imperial' | 'Metric'>(
'Metric', 'Metric',
); );
const [weatherWidgetEnabled, setWeatherWidgetEnabled] =
useState<boolean>(true);
const [automaticWeatherLocation, setAutomaticWeatherLocation] =
useState<boolean>(true);
const [weatherLatitude, setWeatherLatitude] = useState<string>('');
const [weatherLongitude, setWeatherLongitude] = useState<string>('');
const [weatherLocationName, setWeatherLocationName] = useState<string>('');
const [savingStates, setSavingStates] = useState<Record<string, boolean>>({}); const [savingStates, setSavingStates] = useState<Record<string, boolean>>({});
useEffect(() => { useEffect(() => {
@ -217,6 +229,30 @@ const Page = () => {
localStorage.getItem('measureUnit')! as 'Imperial' | 'Metric', localStorage.getItem('measureUnit')! as 'Imperial' | 'Metric',
); );
setWeatherWidgetEnabled(
localStorage.getItem('weatherWidgetEnabled') === null
? true
: localStorage.getItem('weatherWidgetEnabled') === 'true',
);
setAutomaticWeatherLocation(
localStorage.getItem('automaticWeatherLocation') === null
? true
: localStorage.getItem('automaticWeatherLocation') === 'true',
);
setWeatherLatitude(
localStorage.getItem('weatherLatitude') ?? data.weatherLatitude ?? '',
);
setWeatherLongitude(
localStorage.getItem('weatherLongitude') ?? data.weatherLongitude ?? '',
);
setWeatherLocationName(
localStorage.getItem('weatherLocationName') ??
data.weatherLocationName ??
'',
);
setIsLoading(false); setIsLoading(false);
}; };
@ -377,6 +413,16 @@ const Page = () => {
localStorage.setItem('systemInstructions', value); localStorage.setItem('systemInstructions', value);
} else if (key === 'measureUnit') { } else if (key === 'measureUnit') {
localStorage.setItem('measureUnit', value.toString()); localStorage.setItem('measureUnit', value.toString());
} else if (key === 'weatherWidgetEnabled') {
localStorage.setItem('weatherWidgetEnabled', value.toString());
} else if (key === 'automaticWeatherLocation') {
localStorage.setItem('automaticWeatherLocation', value.toString());
} else if (key === 'weatherLatitude') {
localStorage.setItem('weatherLatitude', value.toString());
} else if (key === 'weatherLongitude') {
localStorage.setItem('weatherLongitude', value.toString());
} else if (key === 'weatherLocationName') {
localStorage.setItem('weatherLocationName', value.toString());
} }
} catch (err) { } catch (err) {
console.error('Failed to save:', err); console.error('Failed to save:', err);
@ -454,6 +500,200 @@ const Page = () => {
]} ]}
/> />
</div> </div>
<div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-sm">
Weather
</p>
<div className="flex items-center justify-between p-3 bg-light-secondary dark:bg-dark-secondary rounded-lg hover:bg-light-200 dark:hover:bg-dark-200 transition-colors">
<div className="flex items-center space-x-3">
<div className="p-2 bg-light-200 dark:bg-dark-200 rounded-lg">
<SettingsIcon
size={18}
className="text-black/70 dark:text-white/70"
/>
</div>
<div>
<p className="text-sm text-black/90 dark:text-white/90 font-medium">
Show Weather Widget
</p>
<p className="text-xs text-black/60 dark:text-white/60 mt-0.5">
Show the weather widget on the Home Page
</p>
</div>
</div>
<Switch
checked={weatherWidgetEnabled}
onChange={(checked) => {
setWeatherWidgetEnabled(checked);
saveConfig('weatherWidgetEnabled', checked);
}}
className={cn(
weatherWidgetEnabled
? 'bg-[#24A0ED]'
: 'bg-light-200 dark:bg-dark-200',
'relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none',
)}
>
<span
className={cn(
weatherWidgetEnabled
? 'translate-x-6'
: 'translate-x-1',
'inline-block h-4 w-4 transform rounded-full bg-white transition-transform',
)}
/>
</Switch>
</div>
</div>
{weatherWidgetEnabled && (
<div className="flex flex-col space-y-1">
<div className="flex items-center justify-between p-3 bg-light-secondary dark:bg-dark-secondary rounded-lg hover:bg-light-200 dark:hover:bg-dark-200 transition-colors">
<div className="flex items-center space-x-3">
<div className="p-2 bg-light-200 dark:bg-dark-200 rounded-lg">
<SettingsIcon
size={18}
className="text-black/70 dark:text-white/70"
/>
</div>
<div>
<p className="text-sm text-black/90 dark:text-white/90 font-medium">
Automatic Weather Location
</p>
<p className="text-xs text-black/60 dark:text-white/60 mt-0.5">
Use device geolocation or IP lookup to determine your
location
</p>
</div>
</div>
<Switch
checked={automaticWeatherLocation}
onChange={(checked) => {
setAutomaticWeatherLocation(checked);
// When enabling automatic mode: clear and persist manual fields.
if (checked) {
setWeatherLatitude('');
setWeatherLongitude('');
setWeatherLocationName('');
saveConfig('weatherLatitude', '');
saveConfig('weatherLongitude', '');
saveConfig('weatherLocationName', '');
saveConfig('automaticWeatherLocation', true);
} else {
const lat = (weatherLatitude ?? '').trim();
const lon = (weatherLongitude ?? '').trim();
const loc = (weatherLocationName ?? '').trim();
// Save manual mode only if all fields are filled
if (lat && lon && loc) {
saveConfig('automaticWeatherLocation', false);
}
}
}}
className={cn(
automaticWeatherLocation
? 'bg-[#24A0ED]'
: 'bg-light-200 dark:bg-dark-200',
'relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none',
)}
>
<span
className={cn(
automaticWeatherLocation
? 'translate-x-6'
: 'translate-x-1',
'inline-block h-4 w-4 transform rounded-full bg-white transition-transform',
)}
/>
</Switch>
</div>
{!automaticWeatherLocation && (
<>
<div className="grid grid-cols-2 gap-3 mt-2">
<div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-xs">
Latitude
</p>
<Input
type="text"
placeholder="e.g. 37.7749"
value={weatherLatitude ?? undefined}
isSaving={savingStates['weatherLatitude']}
onChange={(e) => setWeatherLatitude(e.target.value)}
onSave={(value) => {
const newLat = (value ?? '').trim();
const lon = (weatherLongitude ?? '').trim();
const loc = (weatherLocationName ?? '').trim();
// Save manual location only when all three fields are provided.
if (newLat && lon && loc) {
saveConfig('weatherLatitude', value);
saveConfig('weatherLongitude', lon);
saveConfig('weatherLocationName', loc);
setAutomaticWeatherLocation(false);
saveConfig('automaticWeatherLocation', false);
}
}}
/>
</div>
<div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-xs">
Longitude
</p>
<Input
type="text"
placeholder="e.g. -122.4194"
value={weatherLongitude ?? undefined}
isSaving={savingStates['weatherLongitude']}
onChange={(e) =>
setWeatherLongitude(e.target.value)
}
onSave={(value) => {
const lat = (weatherLatitude ?? '').trim();
const newLon = (value ?? '').trim();
const loc = (weatherLocationName ?? '').trim();
// Save manual location only when all three fields are provided.
if (lat && newLon && loc) {
saveConfig('weatherLatitude', lat);
saveConfig('weatherLongitude', value);
saveConfig('weatherLocationName', loc);
setAutomaticWeatherLocation(false);
saveConfig('automaticWeatherLocation', false);
}
}}
/>
</div>
</div>
<div className="flex flex-col space-y-1 mt-2">
<p className="text-black/70 dark:text-white/70 text-xs">
Location Name
</p>
<Input
type="text"
placeholder="e.g. Home, San Francisco"
value={weatherLocationName ?? undefined}
isSaving={savingStates['weatherLocationName']}
onChange={(e) =>
setWeatherLocationName(e.target.value)
}
onSave={(value) => {
const lat = (weatherLatitude ?? '').trim();
const lon = (weatherLongitude ?? '').trim();
const newLoc = (value ?? '').trim();
// Save manual location only when all three fields are provided.
if (lat && lon && newLoc) {
saveConfig('weatherLatitude', lat);
saveConfig('weatherLongitude', lon);
saveConfig('weatherLocationName', value);
setAutomaticWeatherLocation(false);
saveConfig('automaticWeatherLocation', false);
}
}}
/>
</div>
</>
)}
</div>
)}
</SettingsSection> </SettingsSection>
<SettingsSection title="Automatic Search"> <SettingsSection title="Automatic Search">

View file

@ -1,4 +1,6 @@
'use client';
import { Settings } from 'lucide-react'; import { Settings } from 'lucide-react';
import { useEffect, useState } from 'react';
import EmptyChatMessageInput from './EmptyChatMessageInput'; import EmptyChatMessageInput from './EmptyChatMessageInput';
import { File } from './ChatWindow'; import { File } from './ChatWindow';
import Link from 'next/link'; import Link from 'next/link';
@ -26,6 +28,11 @@ const EmptyChat = ({
files: File[]; files: File[];
setFiles: (files: File[]) => void; setFiles: (files: File[]) => void;
}) => { }) => {
const [weatherEnabled, setWeatherEnabled] = useState(true);
useEffect(() => {
const item = localStorage.getItem('weatherWidgetEnabled');
setWeatherEnabled(item === null ? true : item === 'true');
}, []);
return ( return (
<div className="relative"> <div className="relative">
<div className="absolute w-full flex flex-row items-center justify-end mr-5 mt-5"> <div className="absolute w-full flex flex-row items-center justify-end mr-5 mt-5">
@ -51,9 +58,11 @@ const EmptyChat = ({
/> />
</div> </div>
<div className="flex flex-col w-full gap-4 mt-2 sm:flex-row sm:justify-center"> <div className="flex flex-col w-full gap-4 mt-2 sm:flex-row sm:justify-center">
{weatherEnabled && (
<div className="flex-1 w-full"> <div className="flex-1 w-full">
<WeatherWidget /> <WeatherWidget />
</div> </div>
)}
<div className="flex-1 w-full"> <div className="flex-1 w-full">
<NewsArticleWidget /> <NewsArticleWidget />
</div> </div>

View file

@ -1,3 +1,4 @@
'use client';
import { Cloud, Sun, CloudRain, CloudSnow, Wind } from 'lucide-react'; import { Cloud, Sun, CloudRain, CloudSnow, Wind } from 'lucide-react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
@ -70,12 +71,20 @@ const WeatherWidget = () => {
} }
}; };
getLocation(async (location) => { const fetchWeatherForCoords = async (
lat: number,
lng: number,
city?: string,
) => {
try {
const res = await fetch(`/api/weather`, { const res = await fetch(`/api/weather`, {
method: 'POST', method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ body: JSON.stringify({
lat: location.latitude, lat,
lng: location.longitude, lng,
measureUnit: localStorage.getItem('measureUnit') ?? 'Metric', measureUnit: localStorage.getItem('measureUnit') ?? 'Metric',
}), }),
}); });
@ -91,15 +100,52 @@ const WeatherWidget = () => {
setData({ setData({
temperature: data.temperature, temperature: data.temperature,
condition: data.condition, condition: data.condition,
location: location.city, location: city ?? data.location ?? '',
humidity: data.humidity, humidity: data.humidity,
windSpeed: data.windSpeed, windSpeed: data.windSpeed,
icon: data.icon, icon: data.icon,
temperatureUnit: data.temperatureUnit, temperatureUnit: data.temperatureUnit,
windSpeedUnit: data.windSpeedUnit, windSpeedUnit: data.windSpeedUnit,
}); });
} catch (err) {
console.error('Error fetching weather data', err);
} finally {
setLoading(false); setLoading(false);
}
};
(async () => {
// Check automatic setting from localStorage (default true)
const automatic =
localStorage.getItem('automaticWeatherLocation') === null
? true
: localStorage.getItem('automaticWeatherLocation') === 'true';
if (!automatic) {
const latStr = localStorage.getItem('weatherLatitude') ?? '';
const lngStr = localStorage.getItem('weatherLongitude') ?? '';
const name = localStorage.getItem('weatherLocationName') ?? '';
const lat = parseFloat(latStr);
const lng = parseFloat(lngStr);
if (!isNaN(lat) && !isNaN(lng)) {
// Use provided coordinates; prefer user-provided name if available
const locName = name !== '' ? name : `${lat}, ${lng}`;
await fetchWeatherForCoords(lat, lng, locName);
return;
}
// If invalid or missing, fall through to normal location lookup
}
// Normal behavior: use geolocation or IP-based approximate location
await getLocation(async (location) => {
await fetchWeatherForCoords(
location.latitude,
location.longitude,
location.city,
);
}); });
})();
}, []); }, []);
return ( return (