294 lines
10 KiB
Svelte
294 lines
10 KiB
Svelte
<script lang="ts">
|
|
import { page } from '$app/stores'
|
|
import dayjs from 'dayjs'
|
|
import advancedFormat from 'dayjs/plugin/advancedFormat.js'
|
|
import relativeTime from 'dayjs/plugin/relativeTime.js'
|
|
import { getAssetUrlKey } from '$utils/helpers'
|
|
// Components
|
|
import Metas from '$components/Metas.svelte'
|
|
import Button from '$components/atoms/Button.svelte'
|
|
import IconEarth from '$components/atoms/IconEarth.svelte'
|
|
import Image from '$components/atoms/Image.svelte'
|
|
import Newsletter from '$components/organisms/Newsletter.svelte'
|
|
|
|
export let location: any
|
|
export let photos: any[]
|
|
export let lastUpdated: string
|
|
export let totalPhotos: number
|
|
|
|
dayjs.extend(advancedFormat)
|
|
dayjs.extend(relativeTime)
|
|
|
|
const { params } = $page
|
|
let descriptionEl: HTMLElement
|
|
let currentPage: number = 1
|
|
let ended: boolean
|
|
let currentPhotosAmount: number
|
|
$: latestPhoto = photos[0]
|
|
$: currentPhotosAmount = photos.length
|
|
$: ended = currentPhotosAmount === totalPhotos
|
|
|
|
|
|
/**
|
|
* Load photos
|
|
*/
|
|
// Load more photos from CTA
|
|
const loadMorePhotos = async () => {
|
|
// Append more photos from API
|
|
const newPhotos: any = await loadPhotos(currentPage + 1)
|
|
|
|
if (newPhotos) {
|
|
photos = [...photos, ...newPhotos]
|
|
|
|
// Define actions if the number of new photos is the expected ones
|
|
if (newPhotos.length === Number(import.meta.env.VITE_LIST_INCREMENT)) {
|
|
// Increment the current page
|
|
currentPage++
|
|
}
|
|
|
|
// Increment the currently visible amount of photos
|
|
currentPhotosAmount += newPhotos.length
|
|
}
|
|
}
|
|
|
|
// [function] Load photos helper
|
|
const loadPhotos = async (page?: number) => {
|
|
const res = fetchAPI(`
|
|
query {
|
|
photos: photo (
|
|
filter: {
|
|
location: { slug: { _eq: "${params.location}" }},
|
|
status: { _eq: "published" },
|
|
},
|
|
sort: "-date_created",
|
|
limit: ${import.meta.env.VITE_LIST_INCREMENT},
|
|
page: ${page},
|
|
) {
|
|
title
|
|
slug
|
|
image {
|
|
id
|
|
title
|
|
}
|
|
date_taken
|
|
date_created
|
|
}
|
|
}
|
|
`)
|
|
const { data: { photos }} = await res
|
|
|
|
if (photos) {
|
|
// Return new photos
|
|
return photos
|
|
} else {
|
|
throw new Error('Error while loading new photos')
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<Metas
|
|
title="Houses Of {location.name}"
|
|
description="Discover {totalPhotos} beautiful homes from {location.name}, {location.country.name}"
|
|
image={getAssetUrlKey(latestPhoto.image.id, 'share-image')}
|
|
/>
|
|
|
|
|
|
<main class="location-page">
|
|
<section class="location-page__intro grid"
|
|
style="
|
|
--illus-desktop: url({getAssetUrlKey(location.illustration_desktop.id, 'illustration-desktop-1x')});
|
|
--illus-desktop-2x: url({getAssetUrlKey(location.illustration_desktop_2x.id, 'illustration-desktop-2x')});
|
|
--illus-mobile: url({getAssetUrlKey(location.illustration_mobile.id, 'illustration-mobile')});
|
|
"
|
|
>
|
|
<h1 class="title">
|
|
<span class="housesof">
|
|
<strong>Houses</strong>
|
|
<span>of</span>
|
|
</span>
|
|
<strong class="city">
|
|
{location.name}
|
|
</strong>
|
|
</h1>
|
|
|
|
<div class="location-page__description grid" bind:this={descriptionEl}>
|
|
<div class="wrap">
|
|
<div class="text-medium">
|
|
Houses of {location.name} {location.description}
|
|
</div>
|
|
<div class="info">
|
|
<p class="text-label">
|
|
Photos by
|
|
{#each location.credits as { credit_id: { name, website }}}
|
|
{#if website}
|
|
<a href={website} target="_blank" rel="noopener external">
|
|
{name}
|
|
</a>
|
|
{:else}
|
|
<span>{name}</span>
|
|
{/if}
|
|
{/each}
|
|
</p>
|
|
{#if latestPhoto}
|
|
·
|
|
<p class="text-label" title={dayjs(latestPhoto.date_created).format('DD/MM/YYYY, hh:mm')}>
|
|
Updated <time datetime={dayjs(latestPhoto.date_created).format('YYYY-MM-DD')}>
|
|
{dayjs().to(dayjs(latestPhoto.date_created))}
|
|
</time>
|
|
</p>
|
|
{/if}
|
|
</div>
|
|
|
|
<div class="ctas">
|
|
<Button url="/locations" text="Change location" class="shadow-small">
|
|
<IconEarth />
|
|
</Button>
|
|
<Button url="/shop" text="Buy the poster" color="pink" class="shadow-small">
|
|
<!-- <IconEarth /> -->
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="location-page__illustration" />
|
|
</section>
|
|
|
|
{#if photos.length}
|
|
<section class="location-page__houses grid">
|
|
{#each photos as { title, image: { id, title: alt }, slug, date_taken }, index}
|
|
<div class="house grid">
|
|
<div class="house__info">
|
|
<a href="/{params.country}/{params.location}/{slug}">
|
|
<h2 class="title-image">
|
|
{title}
|
|
</h2>
|
|
<time class="text-date" datetime={dayjs(date_taken).format('YYYY-MM-DD')}>
|
|
{dayjs(date_taken).format('MMMM, Do YYYY')}
|
|
</time>
|
|
</a>
|
|
</div>
|
|
<figure class="house__photo">
|
|
<a href="/{params.country}/{params.location}/{slug}">
|
|
<Image
|
|
id={id}
|
|
sizeKey="photo-list"
|
|
sizes={{
|
|
small: { width: 500 },
|
|
medium: { width: 850 },
|
|
large: { width: 1280 },
|
|
}}
|
|
ratio={1.5}
|
|
alt="{alt}"
|
|
class="photo shadow-photo"
|
|
/>
|
|
<span class="house__index title-index">
|
|
{(totalPhotos - index < 10) ? '0' : ''}{totalPhotos - index}
|
|
</span>
|
|
</a>
|
|
</figure>
|
|
</div>
|
|
{/each}
|
|
</section>
|
|
|
|
<section class="location-page__next">
|
|
<div class="container">
|
|
<div class="pagination" role="button" on:click={!ended && loadMorePhotos} disabled={ended ? ended : undefined}>
|
|
<div class="pagination__progress">
|
|
<span class="current">{currentPhotosAmount}</span>
|
|
<span>/</span>
|
|
<span class="total">{totalPhotos}</span>
|
|
|
|
{#if !ended}
|
|
<p class="pagination__more">See more photos</p>
|
|
{:else}
|
|
<p class="pagination__message">You've seen it all!</p>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
|
|
{#if ended}
|
|
<Newsletter theme="light" />
|
|
{/if}
|
|
</div>
|
|
</section>
|
|
{:else}
|
|
<div class="location-page__message">
|
|
<p>
|
|
No photos available for {location.name}.<br>
|
|
Come back later!
|
|
</p>
|
|
</div>
|
|
{/if}
|
|
</main>
|
|
|
|
|
|
<script context="module" lang="ts">
|
|
import { fetchAPI } from '$utils/api'
|
|
|
|
export async function load ({ page, fetch, session, stuff }) {
|
|
const { location } = page.params
|
|
|
|
const res = await fetchAPI(`
|
|
query {
|
|
location (
|
|
filter: {
|
|
slug: { _eq: "${location}" },
|
|
status: { _eq: "published" },
|
|
}
|
|
) {
|
|
id
|
|
name
|
|
slug
|
|
description
|
|
date_updated
|
|
illustration_desktop { id }
|
|
illustration_desktop_2x { id }
|
|
illustration_mobile { id }
|
|
credits {
|
|
credit_id {
|
|
name
|
|
website
|
|
}
|
|
}
|
|
country { name }
|
|
}
|
|
|
|
photos: photo (
|
|
filter: {
|
|
location: { slug: { _eq: "${location}" }}
|
|
},
|
|
sort: "-date_created",
|
|
limit: ${import.meta.env.VITE_LIST_AMOUNT},
|
|
page: 1,
|
|
) {
|
|
title
|
|
slug
|
|
image {
|
|
id
|
|
title
|
|
}
|
|
date_taken
|
|
date_created
|
|
}
|
|
|
|
total_published: photo_aggregated (groupBy: "location") {
|
|
group
|
|
count { id }
|
|
}
|
|
}
|
|
`)
|
|
|
|
const { data } = res
|
|
const locationId = data.location[0].id
|
|
const locationPhotosCount = data.total_published.find(({ group }: any) => group.location === Number(locationId))
|
|
|
|
return {
|
|
props: {
|
|
location: data.location[0],
|
|
photos: data.photos,
|
|
totalPhotos: data.photos.length ? locationPhotosCount.count.id : 0,
|
|
lastUpdated: data.photos.length ? data.photos[0].date_created : undefined,
|
|
}
|
|
}
|
|
}
|
|
</script> |