Skip to content

Commit ae1e8bd

Browse files
committed
drop duplicate listing coordinates
1 parent 74e913e commit ae1e8bd

6 files changed

Lines changed: 194 additions & 44 deletions

File tree

src/app/(forms)/profile/listings/[slug]/page.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export default async function EditListingPage({ params }) {
4343
.single();
4444

4545
const { data: listing } = await supabase
46-
.from("listings")
46+
.from("listings_private_data")
4747
.select()
4848
.eq("owner_id", user.id)
4949
.match({ slug })

src/components/ListingRead/ListingRead.jsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ const ListingRead = memo(function Listing({
7777
? listing.name
7878
: listing.owner_first_name
7979
: getListingDisplayName(listing, user);
80+
const coordinates = listing?.coordinates;
8081

8182
if (!listing && presentation !== "demo") {
8283
console.log("Listing not found");
@@ -158,15 +159,15 @@ const ListingRead = memo(function Listing({
158159
<MapThumbnail
159160
height="320px"
160161
initialViewState={{
161-
longitude: listing.longitude,
162-
latitude: listing.latitude,
162+
longitude: coordinates.longitude,
163+
latitude: coordinates.latitude,
163164
zoom: initialZoomLevel,
164165
}}
165166
interactive={false}
166167
>
167168
<Marker
168-
longitude={listing.longitude}
169-
latitude={listing.latitude}
169+
longitude={coordinates.longitude}
170+
latitude={coordinates.latitude}
170171
anchor="center"
171172
onClick={(event) => {
172173
event.originalEvent.stopPropagation();
@@ -216,7 +217,7 @@ const ListingRead = memo(function Listing({
216217
<Button
217218
variant="secondary"
218219
size="small"
219-
href={`https://maps.apple.com/?ll=${listing.latitude},${listing.longitude}&q=${encodeURIComponent(
220+
href={`https://maps.apple.com/?ll=${coordinates.latitude},${coordinates.longitude}&q=${encodeURIComponent(
220221
listing.name
221222
)}`}
222223
target="_blank"
@@ -226,7 +227,7 @@ const ListingRead = memo(function Listing({
226227
<Button
227228
variant="secondary"
228229
size="small"
229-
href={`https://maps.google.com/?q=${listing.latitude},${listing.longitude}`}
230+
href={`https://maps.google.com/?q=${coordinates.latitude},${coordinates.longitude}`}
230231
target="_blank"
231232
>
232233
{t("Actions.openInGoogleMaps")}

src/components/ListingWrite/ListingWrite.tsx

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,7 @@ export default function ListingWrite({
114114
);
115115

116116
const [coordinates, setCoordinates] = useState<Coordinates | null>(
117-
initialListing
118-
? {
119-
latitude: initialListing.latitude,
120-
longitude: initialListing.longitude,
121-
}
122-
: null
117+
initialListing?.coordinates ?? null
123118
);
124119

125120
const [areaName, setAreaName] = useState<string>(
@@ -264,10 +259,6 @@ export default function ListingWrite({
264259
...(listingType !== "residential" && { name: validatedName }),
265260
description,
266261
location: `POINT(${selectedCoordinates.longitude} ${selectedCoordinates.latitude})`,
267-
// Temporarily store the coordinates as longitude and latitude floats in the database as well
268-
// ...because I can't get the geometry type to convert to to long and lat dynamically if a user goes direct to a listing, e.g. http://localhost:3000/map?listing=9xvN9zxH0rzZ
269-
longitude: selectedCoordinates.longitude,
270-
latitude: selectedCoordinates.latitude,
271262
area_name: areaName,
272263
country_code: countryCode,
273264
accepted_items: acceptedItems.filter((item) => item.trim() !== ""),

src/components/MapImmersive/MapImmersive.jsx

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,20 @@ const DEFAULT_COORDINATES = {
5353
zoom: 9,
5454
};
5555

56+
function getListingCoordinates(listing) {
57+
return listing?.coordinates ?? null;
58+
}
59+
5660
function hasValidCoordinates(listing) {
61+
const coordinates = getListingCoordinates(listing);
62+
5763
return (
5864
listing &&
5965
!listing.error &&
60-
typeof listing.latitude === "number" &&
61-
typeof listing.longitude === "number" &&
62-
Number.isFinite(listing.latitude) &&
63-
Number.isFinite(listing.longitude)
66+
typeof coordinates?.latitude === "number" &&
67+
typeof coordinates?.longitude === "number" &&
68+
Number.isFinite(coordinates.latitude) &&
69+
Number.isFinite(coordinates.longitude)
6470
);
6571
}
6672

@@ -86,6 +92,7 @@ export default function MapImmersive({
8692
isDesktop,
8793
countryCode,
8894
}) {
95+
const selectedListingCoordinates = getListingCoordinates(selectedListing);
8996
const isFirstLoad = useRef(true);
9097
const [lastKnownPosition, setLastKnownPosition] = useState(null);
9198
const [isListingInView, setIsListingInView] = useState(true);
@@ -111,8 +118,10 @@ export default function MapImmersive({
111118

112119
// If there's a selected listing with valid coords, center on it instead of using IP location
113120
if (hasValidCoordinates(selectedListing)) {
121+
const coordinates = getListingCoordinates(selectedListing);
122+
114123
mapRef.current?.flyTo({
115-
center: [selectedListing.longitude, selectedListing.latitude],
124+
center: [coordinates.longitude, coordinates.latitude],
116125
zoom: 12,
117126
duration: 0,
118127
});
@@ -133,9 +142,10 @@ export default function MapImmersive({
133142

134143
// Check if selected listing is in view (only when it has valid coords)
135144
if (hasValidCoordinates(selectedListing)) {
145+
const coordinates = getListingCoordinates(selectedListing);
136146
const isInView = bounds.contains([
137-
selectedListing.longitude,
138-
selectedListing.latitude,
147+
coordinates.longitude,
148+
coordinates.latitude,
139149
]);
140150
setIsListingInView(isInView);
141151
}
@@ -144,8 +154,10 @@ export default function MapImmersive({
144154
const handleFlyToListing = useCallback(() => {
145155
if (!hasValidCoordinates(selectedListing) || !mapRef.current) return;
146156

157+
const coordinates = getListingCoordinates(selectedListing);
158+
147159
mapRef.current.flyTo({
148-
center: [selectedListing.longitude, selectedListing.latitude],
160+
center: [coordinates.longitude, coordinates.latitude],
149161
duration: 1500,
150162
});
151163
}, [selectedListing]);
@@ -160,8 +172,10 @@ export default function MapImmersive({
160172

161173
// If there's a selected listing in URL with valid coords, center on it
162174
if (hasValidCoordinates(selectedListing)) {
175+
const coordinates = getListingCoordinates(selectedListing);
176+
163177
mapRef.current?.flyTo({
164-
center: [selectedListing.longitude, selectedListing.latitude],
178+
center: [coordinates.longitude, coordinates.latitude],
165179
zoom: 12,
166180
duration: 0,
167181
});
@@ -193,9 +207,11 @@ export default function MapImmersive({
193207
// Update lastKnownPosition when we have a valid position
194208
useEffect(() => {
195209
if (hasValidCoordinates(selectedListing)) {
210+
const coordinates = getListingCoordinates(selectedListing);
211+
196212
setLastKnownPosition({
197-
latitude: selectedListing.latitude,
198-
longitude: selectedListing.longitude,
213+
latitude: coordinates.latitude,
214+
longitude: coordinates.longitude,
199215
});
200216
} else if (initialCoordinates && !lastKnownPosition) {
201217
setLastKnownPosition(initialCoordinates);
@@ -210,9 +226,10 @@ export default function MapImmersive({
210226
}
211227

212228
const bounds = mapRef.current.getMap().getBounds();
229+
const coordinates = getListingCoordinates(selectedListing);
213230
const isInView = bounds.contains([
214-
selectedListing.longitude,
215-
selectedListing.latitude,
231+
coordinates.longitude,
232+
coordinates.latitude,
216233
]);
217234
console.log("isInView", isInView);
218235
setIsListingInView(isInView);
@@ -269,13 +286,13 @@ export default function MapImmersive({
269286
initialViewState={{
270287
longitude:
271288
(hasValidCoordinates(selectedListing)
272-
? selectedListing.longitude
289+
? selectedListingCoordinates.longitude
273290
: null) ??
274291
initialCoordinates?.longitude ??
275292
DEFAULT_COORDINATES.longitude,
276293
latitude:
277294
(hasValidCoordinates(selectedListing)
278-
? selectedListing.latitude
295+
? selectedListingCoordinates.latitude
279296
: null) ??
280297
initialCoordinates?.latitude ??
281298
DEFAULT_COORDINATES.latitude,
@@ -308,8 +325,8 @@ export default function MapImmersive({
308325
.map((listing) => (
309326
<DrawerTrigger key={listing.id}>
310327
<Marker
311-
longitude={listing.longitude}
312-
latitude={listing.latitude}
328+
longitude={listing.coordinates.longitude}
329+
latitude={listing.coordinates.latitude}
313330
anchor="center"
314331
onClick={(event) => {
315332
event.originalEvent.stopPropagation();
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
do $$
2+
begin
3+
if exists (
4+
select 1
5+
from public.listings
6+
where abs(latitude - extensions.st_y(location::extensions.geometry)) > 0.0000001
7+
or abs(longitude - extensions.st_x(location::extensions.geometry)) > 0.0000001
8+
) then
9+
raise exception 'Cannot drop listings.latitude/listings.longitude because at least one row does not match listings.location.';
10+
end if;
11+
end
12+
$$;
13+
14+
drop function if exists public.listings_in_view(
15+
double precision,
16+
double precision,
17+
double precision,
18+
double precision
19+
);
20+
21+
drop view if exists public.listings_public_data;
22+
drop view if exists public.listings_private_data;
23+
24+
alter table public.listings
25+
alter column location type extensions.geography(Point, 4326)
26+
using extensions.st_setsrid(location::extensions.geometry, 4326)::extensions.geography(Point, 4326);
27+
28+
alter table public.listings
29+
drop column latitude,
30+
drop column longitude;
31+
32+
create or replace view public.listings_private_data with (security_invoker = 'on') as
33+
select
34+
listings.id,
35+
listings.owner_id,
36+
listings.name,
37+
listings.description,
38+
listings.accepted_items,
39+
listings.rejected_items,
40+
listings.photos,
41+
listings.links,
42+
listings.visibility,
43+
listings.type,
44+
listings.avatar,
45+
listings.slug,
46+
jsonb_build_object(
47+
'latitude', extensions.st_y(listings.location::extensions.geometry),
48+
'longitude', extensions.st_x(listings.location::extensions.geometry)
49+
) as coordinates,
50+
listings.country_code,
51+
listings.area_name,
52+
listings.is_stub,
53+
profiles.first_name as owner_first_name,
54+
profiles.avatar as owner_avatar,
55+
(
56+
select count(*) >= 2
57+
from public.listings as owner_listings
58+
where owner_listings.owner_id = listings.owner_id
59+
and owner_listings.type in ('community', 'business')
60+
) as owner_has_multiple_non_residential_listings
61+
from public.listings
62+
left join public.profiles on listings.owner_id = profiles.id;
63+
64+
alter view public.listings_private_data owner to postgres;
65+
66+
create or replace view public.listings_public_data with (security_invoker = 'on') as
67+
select
68+
id,
69+
created_at,
70+
name,
71+
description,
72+
accepted_items,
73+
rejected_items,
74+
case
75+
when type = any (array['business'::text, 'community'::text]) then photos
76+
else null::text[]
77+
end as photos,
78+
links,
79+
type,
80+
avatar,
81+
slug,
82+
jsonb_build_object(
83+
'latitude', extensions.st_y(location::extensions.geometry),
84+
'longitude', extensions.st_x(location::extensions.geometry)
85+
) as coordinates,
86+
country_code,
87+
area_name,
88+
is_stub,
89+
(
90+
select count(*) >= 2
91+
from public.listings as other_listings
92+
where other_listings.owner_id = listings.owner_id
93+
and other_listings.type in ('community', 'business')
94+
) as owner_has_multiple_non_residential_listings
95+
from public.listings
96+
where visibility = true;
97+
98+
alter view public.listings_public_data owner to postgres;
99+
100+
create or replace function public.listings_in_view(
101+
min_lat double precision,
102+
min_long double precision,
103+
max_lat double precision,
104+
max_long double precision
105+
) returns table(id bigint, type text, coordinates jsonb)
106+
language plpgsql
107+
security definer
108+
set search_path = ''
109+
as $$
110+
begin
111+
return query
112+
select
113+
listings.id,
114+
listings.type,
115+
jsonb_build_object(
116+
'latitude', extensions.st_y(listings.location::extensions.geometry),
117+
'longitude', extensions.st_x(listings.location::extensions.geometry)
118+
) as coordinates
119+
from public.listings
120+
where listings.visibility = true
121+
and listings.location OPERATOR(extensions.&&) extensions.st_makeenvelope(
122+
min_long,
123+
min_lat,
124+
max_long,
125+
max_lat,
126+
4326
127+
)::extensions.geography;
128+
end;
129+
$$;
130+
131+
alter function public.listings_in_view(
132+
double precision,
133+
double precision,
134+
double precision,
135+
double precision
136+
) owner to postgres;
137+
138+
grant all on function public.listings_in_view(
139+
double precision,
140+
double precision,
141+
double precision,
142+
double precision
143+
) to anon, authenticated, service_role;
144+
145+
grant select, insert, references, delete, trigger, truncate, update
146+
on table public.listings_private_data
147+
to anon, authenticated, service_role;
148+
149+
grant select, insert, references, delete, trigger, truncate, update
150+
on table public.listings_public_data
151+
to anon, authenticated, service_role;

0 commit comments

Comments
 (0)