-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathPhotoDetailPage.js
110 lines (98 loc) · 3.54 KB
/
PhotoDetailPage.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import { MdOutlineLocationOn, MdOutlineToday } from "react-icons/md";
import { Link, useLoaderData } from "react-router-dom";
import styles from "./PhotoDetailPage.module.css";
import utilStyles from "../../App/utilStyles.module.css";
async function fetchPhotoData(slug) {
const res = await fetch(`${process.env.REACT_APP_API_BASE_URL}/photos/${slug}`);
if (!res.ok) {
if (res.status === 404) {
// https://reactrouter.com/en/main/route/error-element#throwing-manually
throw new Response("Photo not found", { status: 404, statusText: "Not Found" });
}
throw new Error("Photo data could not be retrieved.");
}
const photoData = await res.json();
return photoData;
}
async function fetchPhotoCollectionsData(photoId) {
const res = await fetch(`${process.env.REACT_APP_API_BASE_URL}/collections?photo_id=${photoId}`);
if (!res.ok) {
throw new Error("Collections data could not be retrieved.");
}
const collectionsData = await res.json();
return collectionsData;
}
export async function photoLoader({ params }) {
// https://reactrouter.com/en/main/route/loader
const photoData = await fetchPhotoData(params.slug);
let relatedCollections = null;
try {
const collectionsData = await fetchPhotoCollectionsData(photoData.id);
if (collectionsData.length > 0) {
relatedCollections = collectionsData;
}
return { photoData, relatedCollections };
} catch(err) {
// Return `null` collections if fetch unsuccessful rather than rendering error page
return { photoData, relatedCollections };
}
}
export function PhotoDetailPage() {
const { photoData, relatedCollections } = useLoaderData();
const { title, summary_text, detail_text, location, date_taken, filename } = photoData;
const largeFilePath = `/photo-images/large/${filename}`;
const smallFilePath = `/photo-images/small/${filename}`;
function getMonthYearString(rawString) {
const options = { year: "numeric", month: "long"};
return new Date(rawString).toLocaleString("en-GB", options);
}
function renderRelatedCollections() {
const links = relatedCollections.map((c, index) => {
return (
<li key={index}>
<Link
to={`/collections/${c.slug}`}
className={`${utilStyles.btn} ${styles.collectionLink}`}
>{c.name}</Link>
</li>
);
});
return (
<section>
<h2 className={utilStyles.h2}>Explore Related Collections</h2>
<ul className={styles.relatedCollections}>
{links}
</ul>
</section>
);
}
return (
<div className={`${utilStyles.pagePaddingNarrow} ${utilStyles.textCenter}`}>
<img
fetchpriority="high"
src={largeFilePath}
srcSet={`${largeFilePath} 1500w, ${smallFilePath} 600w`}
alt={title}
height="1000"
width="1500"
className={styles.image}
></img>
<h1 className={utilStyles.h1}>{title}</h1>
<div className={styles.keyDetails}>
<div className={styles.detailItem}>
<MdOutlineLocationOn size={24} className={styles.locationIcon} />
<span>{location}</span>
</div>
<div className={styles.detailItem}>
<MdOutlineToday size={24} className={styles.calendarIcon} />
<span>{getMonthYearString(date_taken)}</span>
</div>
</div>
<div className={styles.description}>
<p className={utilStyles.mb2rem}>{summary_text}</p>
<p>{detail_text}</p>
</div>
{relatedCollections ? renderRelatedCollections() : null}
</div>
);
}