Skip to content

Commit d367dbc

Browse files
authored
feat: add visits feature & allow multiple photos per pin (#120)
1 parent 54c7a03 commit d367dbc

File tree

10 files changed

+4671
-568
lines changed

10 files changed

+4671
-568
lines changed

HOWTO.md

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ Where `<branch>` is the name of your branch.
6060
- `city` & `country` - city and country where the image was taken
6161
- `coordinates` - you can use [**this website**](https://www.gps-coordinates.net/my-location)
6262
- `date` - date of when the image was taken (year-month-day format)
63-
- `photo` - path to your image file
63+
- `photos` - array containing the paths to your image files
6464

6565
**Understanding Pin Types:**
6666

@@ -80,7 +80,7 @@ Where `<branch>` is the name of your branch.
8080
country: 'Spain',
8181
coordinates: [41.39437640777792, 2.1750122226070197],
8282
date: '2022-03-21',
83-
photo: '/photos/feliciofilipe/barcelona.jpg'
83+
photos: ['/photos/feliciofilipe/barcelona.jpg']
8484
}
8585
```
8686

@@ -95,7 +95,7 @@ Where `<branch>` is the name of your branch.
9595
country: 'Croatia',
9696
coordinates: [45.33264760505596, 14.455986441966521],
9797
date: '2022-07-04',
98-
photo: '/photos/feliciofilipe,matildeopbravo/rijeka.jpg'
98+
photo: ['/photos/feliciofilipe,matildeopbravo/rijeka.jpg']
9999
}
100100
```
101101

@@ -105,6 +105,48 @@ Where `<branch>` is the name of your branch.
105105
> **Note**
106106
> You can get a local preview of your changes by running the project on your machine, follow the [Contributing Guide](CONTRIBUTING.md) to know more.
107107
108+
## 🚘 Registering a visit to an existing pin
109+
110+
**1.** Move to the `data` directory and open the `places.ts` file on your favorite editor
111+
112+
**2.** Look for the entry corresponding to the pin you visited
113+
114+
**3.** Check for a `visits` parameter on the entry for that pin, if it doesn't exist, create one. This parameter will be an array of objects.
115+
116+
**3.** Add a new object to the `visits` array, containing:
117+
118+
- `photo` - path to your image file
119+
- `date` - date of your visit (year-month-day format)
120+
- `visitors` - your first and last name(s)
121+
122+
> **Important**
123+
> `visitors` should be a string for a single visitor and an array for multiple.
124+
125+
**Check out this example:**
126+
127+
```ts
128+
{
129+
author: 'CeSIUM',
130+
username: 'cesium',
131+
type: EPinType.Special,
132+
city: 'Campo do Gerês',
133+
country: 'Portugal',
134+
coordinates: [41.751656376668926, -8.199108019172911],
135+
date: '2024-10-20',
136+
photos: ['/photos/cesium/geres.jpg'],
137+
visits: [
138+
{
139+
photo: '/photos/JulioPinto,SalomeFaria,GustavoPereira/geres-1.jpg',
140+
date: '2025-03-29',
141+
visitors: ['Júlio Pinto', 'Salomé Faria', 'Gustavo Pereira']
142+
}
143+
]
144+
},
145+
```
146+
147+
> **Note**
148+
> You can get a local preview of your changes by running the project on your machine, follow the [Contributing Guide](CONTRIBUTING.md) to know more.
149+
108150
## 🛫 Stage, commit and push your changes
109151

110152
**1.** Stage your changes:

components/Marker/Marker.tsx

Lines changed: 98 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,27 @@ import AuthorIcon from '../AuthorIcon';
77
import localStyles from './style.module.css';
88
import 'styles/globals.css';
99
import Image from 'next/image';
10+
import { Swiper, SwiperSlide } from 'swiper/react';
11+
import 'swiper/css';
12+
import 'swiper/css/pagination';
13+
import { Autoplay, Pagination } from 'swiper/modules';
1014

1115
const Marker = ({
1216
type,
1317
coordinates,
1418
city,
1519
country,
1620
author,
17-
photo,
21+
photos,
1822
date,
19-
orientation
23+
orientation,
24+
visits
2025
}: IPin) => {
2126
const icon = useMemo(() => getIcon(type), [type]);
2227
const name = useMemo(() => getNameString(author), [author]);
23-
28+
const [slide, setSlide] = useState(0);
29+
const [width, setWidth] = useState(0);
30+
const [height, setHeight] = useState(0);
2431
const [imageOrientation, setImageOrientation] = useState(
2532
orientation || 'vertical'
2633
);
@@ -29,27 +36,27 @@ const Marker = ({
2936
setImageOrientation(img.width > img.height ? 'horizontal' : 'vertical');
3037
};
3138

32-
const popupClassName = useMemo(() => {
39+
useEffect(() => {
40+
if (orientation || !photos) return;
41+
const img = new window.Image();
42+
if (visits) img.src = photos[slide] ?? visits[slide - photos.length].photo;
43+
else img.src = photos[slide];
44+
img.onload = () => handleImageLoad(img);
45+
}, [photos, orientation, slide, visits]);
46+
47+
useEffect(() => {
48+
let sizes = '';
3349
const isMobile = window.innerWidth <= 500;
3450

3551
if (isMobile) {
36-
return imageOrientation === 'horizontal' ? '401:250' : '351:550';
52+
sizes = imageOrientation === 'horizontal' ? '401:250' : '351:550';
3753
} else {
38-
return imageOrientation === 'horizontal' ? '651:400' : '301:470';
54+
sizes = imageOrientation === 'horizontal' ? '651:400' : '301:470';
3955
}
40-
}, [imageOrientation]);
4156

42-
useEffect(() => {
43-
if (orientation || !photo) return;
44-
const img = new window.Image();
45-
img.src = photo;
46-
img.onload = () => handleImageLoad(img);
47-
}, [photo, orientation]);
48-
49-
const [width, height] = useMemo(
50-
() => popupClassName.split(':').map(Number),
51-
[popupClassName]
52-
);
57+
setWidth(sizes.split(':').map(Number)[0]);
58+
setHeight(sizes.split(':').map(Number)[1]);
59+
}, [imageOrientation]);
5360

5461
return (
5562
<MarkerContainer
@@ -58,33 +65,79 @@ const Marker = ({
5865
title={`${name} at ${city}`}
5966
>
6067
<Popup className={localStyles.popup}>
61-
<div className={localStyles.imageContainer} style={{ width, height }}>
62-
<Image
63-
alt={`${name} at ${city}`}
64-
src={photo}
65-
width={width}
66-
height={height}
67-
className={localStyles.roundedImage}
68-
/>
69-
<div className={localStyles.textOverlay}>
70-
<h1 className={localStyles.title}>
71-
{city}, {country}
72-
</h1>
73-
<span className={localStyles.light}>
74-
<i className="bi bi-calendar"></i> {getFullDateString(date)} (
75-
{getRelativeTimeString(date)})
76-
</span>
77-
<br />
78-
<span className={localStyles.light}>
79-
<i className="bi bi-signpost-fill"></i>{' '}
80-
{Math.round(getDistance(coordinates))} km away
81-
</span>
82-
<br />
83-
<span>
84-
<AuthorIcon author={author} /> {name}
85-
</span>
86-
</div>
87-
</div>
68+
<Swiper
69+
slidesPerView={1}
70+
spaceBetween={15}
71+
pagination={{ clickable: true, type: 'bullets' }}
72+
modules={[Autoplay, Pagination]}
73+
centeredSlides
74+
style={{ width, height }}
75+
onSlideChange={(swiper) => setSlide(swiper.realIndex)}
76+
>
77+
{photos.map((photo, idx) => (
78+
<SwiperSlide key={idx}>
79+
<div className={localStyles.imageContainer}>
80+
<Image
81+
alt={`${name} at ${city}`}
82+
src={photo}
83+
width={width}
84+
height={height}
85+
className={localStyles.roundedImage}
86+
/>
87+
{idx === 0 && (
88+
<div className={localStyles.textOverlay}>
89+
<h1 className={localStyles.title}>
90+
{city}, {country}
91+
</h1>
92+
<span className={localStyles.light}>
93+
<i className="bi bi-calendar-fill"></i>{' '}
94+
{getRelativeTimeString(date)}{' '}
95+
{date.replace(/^(\d{4})-(\d{2})-(\d{2})$/, '$3/$2/$1')}
96+
</span>
97+
<br />
98+
<span className={localStyles.light}>
99+
<i className="bi bi-signpost-fill"></i>{' '}
100+
{Math.round(getDistance(coordinates))} km away
101+
</span>
102+
<br />
103+
<span>
104+
<AuthorIcon author={author} /> {name}
105+
</span>
106+
</div>
107+
)}
108+
</div>
109+
</SwiperSlide>
110+
))}
111+
{visits &&
112+
visits.map((visit, idx) => (
113+
<SwiperSlide key={idx}>
114+
<div className={localStyles.imageContainer}>
115+
<Image
116+
alt={`${visit.visitors} at ${city}`}
117+
src={visit.photo}
118+
width={width}
119+
height={height}
120+
className={localStyles.roundedImage}
121+
/>
122+
<div className={localStyles.textOverlay}>
123+
<span className={localStyles.light}>
124+
<i className="bi bi-car-front-fill"></i> Visited{' '}
125+
{getRelativeTimeString(visit.date)}{' '}
126+
{visit.date.replace(
127+
/^(\d{4})-(\d{2})-(\d{2})$/,
128+
'$3/$2/$1'
129+
)}
130+
</span>
131+
<br />
132+
<span>
133+
<AuthorIcon author={visit.visitors} />{' '}
134+
{getNameString(visit.visitors)}
135+
</span>
136+
</div>
137+
</div>
138+
</SwiperSlide>
139+
))}
140+
</Swiper>
88141
</Popup>
89142
</MarkerContainer>
90143
);

components/Marker/style.module.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
position: relative;
33
border-radius: 10px;
44
overflow: hidden;
5+
user-select: none;
56
}
67

78
.popup {

0 commit comments

Comments
 (0)