watcher
This commit is contained in:
@@ -722,6 +722,8 @@ function App() {
|
||||
authorizedFetch={authorizedFetch}
|
||||
knownStores={stores}
|
||||
userLocation={preferences?.location || null}
|
||||
locationLoading={preferencesLoading}
|
||||
onRequestLocation={updateLocation}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -90,7 +90,13 @@ function persistWatchTableState(state) {
|
||||
}
|
||||
}
|
||||
|
||||
const StoreWatchPage = ({ authorizedFetch, knownStores = [], userLocation }) => {
|
||||
const StoreWatchPage = ({
|
||||
authorizedFetch,
|
||||
knownStores = [],
|
||||
userLocation,
|
||||
onRequestLocation,
|
||||
locationLoading = false
|
||||
}) => {
|
||||
const [regions, setRegions] = useState([]);
|
||||
const [selectedRegionId, setSelectedRegionId] = useState(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
@@ -114,6 +120,9 @@ const StoreWatchPage = ({ authorizedFetch, knownStores = [], userLocation }) =>
|
||||
const initialTableState = useMemo(() => readWatchTableState(), []);
|
||||
const [sorting, setSorting] = useState(initialTableState.sorting);
|
||||
const [columnFilters, setColumnFilters] = useState(initialTableState.columnFilters);
|
||||
const [locationPromptTriggered, setLocationPromptTriggered] = useState(false);
|
||||
const [locationPromptPending, setLocationPromptPending] = useState(false);
|
||||
const [locationPromptError, setLocationPromptError] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
@@ -130,6 +139,53 @@ const StoreWatchPage = ({ authorizedFetch, knownStores = [], userLocation }) =>
|
||||
persistWatchTableState({ sorting, columnFilters });
|
||||
}, [sorting, columnFilters]);
|
||||
|
||||
const requestLocation = useCallback(() => {
|
||||
if (!onRequestLocation) {
|
||||
return;
|
||||
}
|
||||
setLocationPromptTriggered(true);
|
||||
if (typeof window === 'undefined' || typeof navigator === 'undefined' || !navigator.geolocation) {
|
||||
setLocationPromptPending(false);
|
||||
setLocationPromptError('Standortbestimmung wird von diesem Browser nicht unterstützt.');
|
||||
return;
|
||||
}
|
||||
setLocationPromptPending(true);
|
||||
setLocationPromptError('');
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
async (position) => {
|
||||
setLocationPromptPending(false);
|
||||
try {
|
||||
await onRequestLocation({
|
||||
lat: position.coords.latitude,
|
||||
lon: position.coords.longitude
|
||||
});
|
||||
} catch {
|
||||
setLocationPromptError('Standort konnte nicht gespeichert werden.');
|
||||
}
|
||||
},
|
||||
(geoError) => {
|
||||
setLocationPromptPending(false);
|
||||
setLocationPromptError(
|
||||
geoError.code === geoError.PERMISSION_DENIED
|
||||
? 'Zugriff auf den Standort wurde verweigert. Bitte erlaube den Zugriff im Browser.'
|
||||
: 'Standort konnte nicht automatisch ermittelt werden.'
|
||||
);
|
||||
},
|
||||
{
|
||||
enableHighAccuracy: true,
|
||||
timeout: 10000,
|
||||
maximumAge: 0
|
||||
}
|
||||
);
|
||||
}, [onRequestLocation]);
|
||||
|
||||
useEffect(() => {
|
||||
if (userLocation || locationLoading || !onRequestLocation || locationPromptTriggered) {
|
||||
return;
|
||||
}
|
||||
requestLocation();
|
||||
}, [userLocation, locationLoading, onRequestLocation, locationPromptTriggered, requestLocation]);
|
||||
|
||||
const watchedIds = useMemo(
|
||||
() => new Set(watchList.map((entry) => String(entry.storeId))),
|
||||
[watchList]
|
||||
@@ -808,6 +864,28 @@ const StoreWatchPage = ({ authorizedFetch, knownStores = [], userLocation }) =>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!userLocation && !locationLoading && onRequestLocation && (
|
||||
<div className="mb-6 rounded-lg border border-blue-200 bg-blue-50 p-4 text-sm text-blue-900">
|
||||
{locationPromptPending ? (
|
||||
<p>Standort wird automatisch angefragt, um Entfernungen berechnen zu können...</p>
|
||||
) : locationPromptError ? (
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<p className="flex-1">{locationPromptError}</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={requestLocation}
|
||||
className="inline-flex items-center justify-center rounded-md bg-blue-600 px-4 py-2 text-sm font-semibold text-white hover:bg-blue-700 disabled:opacity-60"
|
||||
disabled={locationPromptPending}
|
||||
>
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<p>Bitte bestätige die Standortabfrage deines Browsers für Entfernungssortierung.</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mb-6 border border-gray-200 rounded-lg p-4 bg-gray-50">
|
||||
<div className="flex flex-col md:flex-row md:items-end gap-3">
|
||||
<div className="flex-1">
|
||||
|
||||
Reference in New Issue
Block a user