Skip to content
This repository was archived by the owner on Aug 7, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions components/map/Clusters.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { Marker, useMap } from "react-leaflet";
import useSupercluster from "use-supercluster";
import UserMarker from "./UserMarker";
import styles from "./Clusters.module.css";
import EventMarker from "./EventMarker";

export default function Clusters({users}) {
export default function Clusters({points}) {
const map = useMap();
const mapB = map.getBounds();
const [bounds, setBounds] = useState([
Expand All @@ -32,15 +33,14 @@ export default function Clusters({users}) {
})

const { clusters, supercluster } = useSupercluster({
points: users,
points: points,
bounds,
zoom,
options: {
radius: zoom < 17 ? 75 : 50,
maxZoom: 18
}
});

const icons = {};
const fetchIcon = (count) => {
const size =
Expand All @@ -65,7 +65,8 @@ export default function Clusters({users}) {
const {
cluster: isCluster,
point_count: pointCount,
username
username,
name
} = cluster.properties;

// we have a cluster to render
Expand All @@ -91,7 +92,9 @@ export default function Clusters({users}) {
}

// we have a single point to render
return (
return cluster.properties.isEvent ? (
<EventMarker event={cluster} key={name} />
) : (
<UserMarker user={cluster} key={username} />
);
})}
Expand Down
58 changes: 58 additions & 0 deletions components/map/EventMarker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import L from "leaflet";
import { Marker, Popup } from "react-leaflet";
import { ReactMarkdown } from "react-markdown/lib/react-markdown";
import Link from "@components/Link";

export default function EventMarker({event}) {
// Custom component for rendering links within ReactMarkdown
const LinkRenderer = ({ href, children }) => (
<Link href={href}>
{children}
</Link>
);

return (
<Marker
icon={L.divIcon({
className: "rounded-full",
html: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M15 10.5a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1115 0z" />
</svg>
`,
popupAnchor: [0, -10],
iconSize: [40, 40],
iconAnchor: [20, 20],
})}
position={[event.geometry.coordinates[1], event.geometry.coordinates[0]]}
>
<Popup>
<div className="flex flex-col gap-[5px]">
<h1 className="font-[600]">
<Link href={event.properties.url}>
{event.properties.name}
</Link>
</h1>
<span>
{[
event.properties.location.city,
event.properties.location.state,
event.properties.location.country,
]
.filter((x) => x)
.join(", ")}
</span>
<span>
<ReactMarkdown components={{ a: LinkRenderer }}>
{event.properties.description}
</ReactMarkdown>
</span>
<span>
{`${new Date(event.properties.date.start).toLocaleDateString()} -
${new Date(event.properties.date.end).toLocaleDateString()}`}
</span>
</div>
</Popup>
</Marker>
)
}
4 changes: 2 additions & 2 deletions components/map/Map.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { MapContainer, TileLayer } from "react-leaflet";
import Clusters from "./Clusters";
import "leaflet/dist/leaflet.css";

export default function Map({ users }) {
export default function Map({ points }) {
const boundsMap = [
[-90, -180], // Southwest coordinates
[90, 180], // Northeast coordinates
Expand All @@ -23,7 +23,7 @@ export default function Map({ users }) {
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://b.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Clusters users={users} />
<Clusters points={points} />
</MapContainer>
);
}
8 changes: 8 additions & 0 deletions models/Profile/Event.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ const EventSchema = new Schema({
price: {
startingFrom: Number,
},
location: {
road: String,
city: String,
state: String,
country: String,
lat: Number,
lon: Number,
},
});

EventSchema.pre("save", () => {
Expand Down
18 changes: 17 additions & 1 deletion pages/api/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,29 @@ export default async function handler(req, res) {
return res.status(200).json(events);
}

export async function getEvents() {
export async function getEvents(withLocation = false) {
let events = [];
try {
events = await Profile.aggregate([
{ $project: { username: 1, events: 1, isEnabled: 1 } },
{ $match: { "events.date.start": { $gt: new Date() }, isEnabled: true } },
{ $unwind: "$events" },
{ $match: { "events.date.end": { $gt: new Date() } } },
...(withLocation
? [
{
$match: {
$and: [
{ "events.location": { $exists: true } },
{ "events.location.lat": { $exists: true } },
{ "events.location.lon": { $exists: true } },
{ "events.location.lat": { $ne: null } },
{ "events.location.lon": { $ne: null } },
],
},
},
]
: []),
{ $sort: { "events.date.start": 1 } },
{
$group: {
Expand All @@ -31,6 +46,7 @@ export async function getEvents() {
url: { $first: "$events.url" },
name: { $first: "$events.name" },
description: { $first: "$events.description" },
location: { $first: "$events.location" },
isEnabled: { $first: "$isEnabled" },
},
},
Expand Down
76 changes: 58 additions & 18 deletions pages/api/system/reload.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,28 +234,68 @@ export default async function handler(req, res) {
}

// - events
try {
if (profile.events) {
async function getCoordinates(city, state, country) {
let locationDb = {};
const provided = [city, state, country].filter((x) => x).join(",");
if (locationDb[provided]) {
return locationDb[provided];
}
try {
const location = await fetch(
`https://nominatim.openstreetmap.org/?addressdetails=1&q=
${encodeURIComponent(provided)}&format=json&limit=1`
);
const coordinates = await location.json();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For events that are added by forms, how do they get their location?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are fetching coordinates with city, state and country details. As soon as we hit the api to load data into MongoDB, it takes the events with startDate>today or EndDate>today and then fetches coordinates with city, state and country details.
This is what is happening, but I have not taken a look at how forms are working.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok thanks 👍 Forms will write directly to the db so will skip the reload script - I will look into this as I refactor the location fetching with users location

if (coordinates) {
const point = {
lat: coordinates[0].lat,
lon: coordinates[0].lon,
};
locationDb[provided] = point;
return point;
}
} catch (e) {
return null;
}
return null;
}

if (profile.events) {
try {
const events = await Promise.all(
profile.events.map(async (event, position) => {
let location = {};
if (event.location) {
location = {
location: { ...event.location },
};
if (new Date(event.date.start) > Date.now() || new Date(event.date.end) > Date.now()) {
const coordinates = await getCoordinates(
event.location.city,
event.location.state,
event.location.country
);
if (coordinates) {
location.location.lat = coordinates.lat;
location.location.lon = coordinates.lon;
}
}
}
return {
order: position,
...event,
...location,
};
})
);

await Profile.findOneAndUpdate(
{ username: profile.username },
{
events: profile.events.map((event) => ({
isVirtual: event.isVirtual,
color: event.color,
name: event.name,
description: event.description,
date: {
start: event.date.start,
end: event.date.end,
},
url: event.url,
price: event.price,
})),
}
{ events }
);
} catch (e) {
logger.error(e,`failed to update events for ${profile.username}`);
}
} catch (e) {
logger.error(e, `failed to update events for ${profile.username}`);
}
})
);
Expand Down
55 changes: 45 additions & 10 deletions pages/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Page from "@components/Page";
import Badge from "@components/Badge";
import { getTags } from "./api/discover/tags";
import { getUsers } from "./api/profiles";
import { getEvents } from "./api/events";
import config from "@config/app.json";

//this is required as leaflet is not compatible with SSR
Expand All @@ -21,6 +22,8 @@ export async function getStaticProps() {
let data = {
users: [],
tags: [],
events:[],
points: []
};
try {
data.users = await getUsers();
Expand Down Expand Up @@ -85,15 +88,47 @@ export async function getStaticProps() {
logger.error(e, "ERROR loading tags");
}

try {
data.events = await getEvents(true);
} catch (e) {
logger.error(e, "ERROR loading Events");
}

data.events = data.events.map((event, index) => {
const offset = Math.random() * 0.02; // ~2.2km
const offset2 = Math.random() * 0.02; // ~2.2km
return {
type: "Feature",
properties: {
cluster: false,
isEvent: true,
description: event.description,
name: event.name,
location: event.location,
date: event.date,
url: event.url || ''
},
geometry: {
type: "Point",
coordinates: adjustCoords(
[parseFloat(event.location.lon), parseFloat(event.location.lat)],
offset,
offset2,
index
),
},
};
});
data.points=[...data.users,...data.events]
return {
props: { data },
revalidate: pageConfig.revalidateSeconds,
};
}

export default function Map({ data }) {
let { users, tags } = data;
const [filteredUsers, setFilteredUsers] = useState([]);
let { tags, points } = data;
const [filteredPoints, setFilteredPoints] = useState([]);
const [selectedTags, setSelectedTags] = useState(new Set());

let results = [];
Expand All @@ -115,12 +150,12 @@ export default function Map({ data }) {
const valueLower = value.toLowerCase();
const terms = [...updateSelectedTagsFilter(value)];

results = users.filter((user) => {
if (user.properties.name.toLowerCase().includes(valueLower)) {
results = points.filter((point) => {
if (point.properties.name.toLowerCase().includes(valueLower)) {
return true;
}

let userTags = user.properties.tags?.map((tag) => tag.toLowerCase());
let userTags = point.properties.tags?.map((tag) => tag.toLowerCase());

if (terms.every((keyword) => userTags?.includes(keyword.toLowerCase()))) {
return true;
Expand All @@ -129,11 +164,11 @@ export default function Map({ data }) {
return false;
});

setFilteredUsers(results);
setFilteredPoints(results);
};

const resetFilter = () => {
setFilteredUsers([]);
setFilteredPoints([]);
setSelectedTags(new Set());
};

Expand Down Expand Up @@ -172,9 +207,9 @@ export default function Map({ data }) {
<Badge
disable={selectedTags.size == 0 ? true : false}
content={
filteredUsers.length > 0 ? filteredUsers.length : users.length
filteredPoints.length > 0 ? filteredPoints.length : points.length
}
>
>
<Button
onClick={resetFilter}
primary={true}
Expand All @@ -196,7 +231,7 @@ export default function Map({ data }) {
</div>
<div className="h-screen">
<DynamicMap
users={filteredUsers.length > 0 ? filteredUsers : users}
points={filteredPoints.length > 0 ? filteredPoints : points}
/>
</div>
</Page>
Expand Down
Binary file added public/placard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.