Make server the single source of truth for active location
Two devices on the same account each kept their own localStorage location and re-asserted it onto Feeld's single shared account-location slot on every Discover mount — so loading the site on device B would overwrite the location device A had just set (San Antonio → Dallas). - useLocation: on server sync, always adopt the server's current/custom location, overriding this device's local cache (was local-first via a `!location` guard). Devices now converge to the server value on load. Expose `locationReady`. - Discover: wait for `locationReady` before pushing DeviceLocationUpdate, and push whenever the authoritative location changes (deduped by coords) instead of a one-shot stale-cache assert. Device B now stamps the server's value onto Feeld, not its own old location. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -43,6 +43,10 @@ export interface LocationState {
|
||||
interface LocationContextType {
|
||||
location: LocationState | null;
|
||||
savedLocations: SavedLocation[];
|
||||
// True once the initial sync from the server (the source of truth) has completed.
|
||||
// Consumers that push the location onward (e.g. Discover → Feeld) should wait for this
|
||||
// so they assert the server's authoritative value, not this device's stale cache.
|
||||
locationReady: boolean;
|
||||
// Throws BannedCountryError if the LocationState has a banned country/countryCode.
|
||||
setLocation: (location: LocationState | null) => void;
|
||||
// Throws BannedCountryError if the country is on the Feeld-restricted list.
|
||||
@@ -106,18 +110,17 @@ export function LocationProvider({ children }: { children: ReactNode }) {
|
||||
});
|
||||
}
|
||||
|
||||
// Restore current location if we don't have one
|
||||
const cleanCurrent = sanitizeCurrent(serverData.currentLocation);
|
||||
if (cleanCurrent && !location) {
|
||||
setLocationState(cleanCurrent);
|
||||
localStorage.setItem(CURRENT_LOCATION_KEY, JSON.stringify(cleanCurrent));
|
||||
}
|
||||
|
||||
// Restore custom location if we don't have one
|
||||
const cleanCustom = sanitizeCurrent(serverData.customLocation);
|
||||
if (cleanCustom && !location) {
|
||||
setLocationState(cleanCustom);
|
||||
localStorage.setItem(CURRENT_LOCATION_KEY, JSON.stringify(cleanCustom));
|
||||
// The server is the single source of truth for the active location.
|
||||
// Adopt whatever it currently holds (prefer currentLocation, fall back to
|
||||
// customLocation), OVERRIDING this device's local cache — so two devices on
|
||||
// the same account always converge to the same place instead of each
|
||||
// re-asserting its own stale copy. Only keep local if the server has none.
|
||||
const serverActive =
|
||||
sanitizeCurrent(serverData.currentLocation) ||
|
||||
sanitizeCurrent(serverData.customLocation);
|
||||
if (serverActive) {
|
||||
setLocationState(serverActive);
|
||||
localStorage.setItem(CURRENT_LOCATION_KEY, JSON.stringify(serverActive));
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -199,6 +202,7 @@ export function LocationProvider({ children }: { children: ReactNode }) {
|
||||
value={{
|
||||
location,
|
||||
savedLocations,
|
||||
locationReady: hasSyncedFromServer,
|
||||
setLocation,
|
||||
saveLocation,
|
||||
deleteLocation,
|
||||
|
||||
+19
-14
@@ -291,24 +291,29 @@ export function DiscoverPage() {
|
||||
});
|
||||
const initialLoadDone = useRef(false);
|
||||
const savedLikedMeProfiles = useRef<Set<string>>(new Set());
|
||||
const { location } = useLocation();
|
||||
const { location, locationReady } = useLocation();
|
||||
const { isFavorite, toggleFavorite } = useFavorites();
|
||||
|
||||
// Set device location on Feeld when Discover page loads (emulates app open GPS)
|
||||
// Push the location to Feeld whenever the authoritative location changes.
|
||||
// Gate on `locationReady` so we wait for the server (the source of truth) to load
|
||||
// first — otherwise this device would stamp its own stale localStorage location onto
|
||||
// Feeld's single shared account-location slot, overriding what another device just set.
|
||||
// Dedupe by coordinates so we don't re-push the same place on every re-render.
|
||||
const [updateDeviceLocation] = useMutation(DEVICE_LOCATION_UPDATE_MUTATION);
|
||||
const locationSetRef = useRef(false);
|
||||
const lastPushedRef = useRef<string | null>(null);
|
||||
useEffect(() => {
|
||||
if (location && !locationSetRef.current) {
|
||||
locationSetRef.current = true;
|
||||
updateDeviceLocation({
|
||||
variables: { input: { latitude: location.latitude, longitude: location.longitude } },
|
||||
}).then(() => {
|
||||
console.log(`[Discover] Location set to ${location.name || `${location.latitude},${location.longitude}`}`);
|
||||
}).catch((err) => {
|
||||
console.error('[Discover] Failed to set location:', err);
|
||||
});
|
||||
}
|
||||
}, [location, updateDeviceLocation]);
|
||||
if (!locationReady || !location) return;
|
||||
const coordKey = `${location.latitude},${location.longitude}`;
|
||||
if (lastPushedRef.current === coordKey) return;
|
||||
lastPushedRef.current = coordKey;
|
||||
updateDeviceLocation({
|
||||
variables: { input: { latitude: location.latitude, longitude: location.longitude } },
|
||||
}).then(() => {
|
||||
console.log(`[Discover] Location set to ${location.name || coordKey}`);
|
||||
}).catch((err) => {
|
||||
console.error('[Discover] Failed to set location:', err);
|
||||
});
|
||||
}, [locationReady, location, updateDeviceLocation]);
|
||||
|
||||
// Fetch rotation location (what the cron actually set on Feeld)
|
||||
const [rotationLocation, setRotationLocation] = useState<string | null>(null);
|
||||
|
||||
Reference in New Issue
Block a user