1
0
mirror of https://github.com/kremalicious/portfolio.git synced 2024-12-22 17:23:22 +01:00

location fetching tweaks

This commit is contained in:
Matthias Kretschmann 2024-02-04 11:41:11 +00:00
parent a43a3da292
commit 9705015d69
Signed by: m
GPG Key ID: 606EEEF3C479A91F
15 changed files with 128 additions and 108 deletions

View File

@ -1,7 +1,7 @@
import { Metadata, ResolvingMetadata } from 'next' import { Metadata, ResolvingMetadata } from 'next'
import { notFound } from 'next/navigation' import { notFound } from 'next/navigation'
import meta from '../../../_content/meta.json' import meta from '../../../_content/meta.json'
import Header from '../../components/Header' import Header from '../../components/Header/Header'
import Project from '../../components/Project' import Project from '../../components/Project'
import ProjectNav from '../../components/ProjectNav' import ProjectNav from '../../components/ProjectNav'
import { import {
@ -45,7 +45,7 @@ export default async function ProjectPage({ params }: Props) {
return ( return (
<> <>
<Header isSmall /> <Header />
<Project project={project} /> <Project project={project} />
<ProjectNav projects={projects} currentSlug={params.slug} /> <ProjectNav projects={projects} currentSlug={params.slug} />
</> </>

View File

@ -1,13 +1,16 @@
'use server' 'use server'
import { cache } from 'react'
import { revalidatePath } from 'next/cache' import { revalidatePath } from 'next/cache'
import { GiphyFetch } from '@giphy/js-fetch-api' import { GiphyFetch } from '@giphy/js-fetch-api'
export async function getLocation() { export const preloadLocation = () => {
void getLocation()
}
export const getLocation = cache(async () => {
try { try {
const response = await fetch('https://location.kretschmann.io', { const response = await fetch('https://location.kretschmann.io')
cache: 'no-store'
})
if (!response.ok) if (!response.ok)
throw new Error('Network response for location was not ok.') throw new Error('Network response for location was not ok.')
@ -16,7 +19,7 @@ export async function getLocation() {
} catch (error) { } catch (error) {
console.error(error.message) console.error(error.message)
} }
} })
export async function getRandomGif(tag: string, pathname?: string) { export async function getRandomGif(tag: string, pathname?: string) {
try { try {

View File

@ -1,18 +1,19 @@
import Header from '../components/Header' import Hero from '../components/Hero'
import Projects from '../components/Projects' import Projects from '../components/Projects'
import Repositories from '../components/Repositories' import Repositories from '../components/Repositories'
import { getAllProjects } from '../lib/content' import { getAllProjects } from '../lib/content'
import { getGithubRepos } from '../lib/github' import { getGithubRepos } from '../lib/github'
import { getLocation } from './actions' import { preloadLocation } from './actions'
export default async function IndexPage() { export default async function IndexPage() {
const projects = await getAllProjects(['title', 'images', 'slug']) const projects = await getAllProjects(['title', 'images', 'slug'])
const repos = await getGithubRepos() const repos = await getGithubRepos()
const location = await getLocation()
preloadLocation()
return ( return (
<> <>
<Header location={location} /> <Hero />
<Projects projects={projects} /> <Projects projects={projects} />
<Repositories repos={repos} /> <Repositories repos={repos} />
</> </>

View File

@ -0,0 +1,9 @@
.header {
position: relative;
padding-top: calc(var(--spacer) * 2);
margin-bottom: calc(var(--spacer) * 2);
text-align: center;
display: flex;
flex-direction: column;
justify-content: flex-start;
}

View File

@ -0,0 +1,10 @@
import { render, screen } from '@testing-library/react'
import Header from './Header'
describe('Header', () => {
it('renders correctly', async () => {
render(<Header />)
await screen.findByText('matthias kretschmann')
})
})

View File

@ -0,0 +1,10 @@
import LogoUnit from '../LogoUnit'
import styles from './Header.module.css'
export default function Header() {
return (
<header className={styles.header} data-testid="header">
<LogoUnit small />
</header>
)
}

View File

@ -1,18 +0,0 @@
import { render, screen } from '@testing-library/react'
import Header from '.'
import { dataLocation } from '../../../tests/__fixtures__/location'
describe('Header', () => {
it('renders correctly', async () => {
render(<Header location={dataLocation} />)
await screen.findByText('matthias kretschmann')
await screen.findAllByText(dataLocation.now.city)
})
it('renders small', async () => {
render(<Header isSmall />)
expect(await screen.findByTestId('header')).toHaveClass('small')
})
})

View File

@ -1,28 +1 @@
import Availability from '../Availability' export { default } from './Header'
import Location from '../Location'
import { UseLocation } from '../Location/types'
import LogoUnit from '../LogoUnit'
import Networks from '../Networks'
import styles from './index.module.css'
export default function Header({
location,
isSmall
}: {
location?: UseLocation
isSmall?: boolean
}) {
return (
<header
className={`${styles.header} ${isSmall ? styles.small : ''}`}
data-testid="header"
>
<LogoUnit small={isSmall} />
{!isSmall ? <Networks label="Networks" /> : null}
<div className={styles.meta}>
{location ? <Location location={location} /> : null}
{!isSmall ? <Availability /> : null}
</div>
</header>
)
}

View File

@ -1,4 +1,4 @@
.header { .hero {
position: relative; position: relative;
padding: calc(var(--spacer) / 2); padding: calc(var(--spacer) / 2);
padding-top: 25vh; padding-top: 25vh;
@ -14,9 +14,3 @@
.meta { .meta {
margin-top: auto; margin-top: auto;
} }
.small {
min-height: 0;
padding-top: calc(var(--spacer) * 2);
padding-bottom: 0;
}

View File

@ -0,0 +1,18 @@
import Availability from '../Availability'
import Location from '../Location/Location'
import LogoUnit from '../LogoUnit'
import Networks from '../Networks'
import styles from './Hero.module.css'
export default function Hero() {
return (
<header className={styles.hero} data-testid="header">
<LogoUnit />
<Networks label="Networks" />
<div className={styles.meta}>
<Location />
<Availability />
</div>
</header>
)
}

View File

@ -0,0 +1 @@
export { default } from './Hero'

View File

@ -1,4 +1,4 @@
import styles from './index.module.css' import styles from './Location.module.css'
type Props = { type Props = {
country: { country: {

View File

@ -0,0 +1,61 @@
'use client'
import { useEffect, useState } from 'react'
import RelativeTime from '@yaireo/relative-time'
import { getLocation } from '../../app/actions'
import { Flag } from './Flag'
import styles from './Location.module.css'
import { UseLocation } from './types'
export default function Location() {
const [location, setLocation] = useState<UseLocation | null>(null)
const isDifferentCountry = location?.now?.country !== location?.next?.country
const relativeTime = new RelativeTime({ locale: 'en' })
useEffect(() => {
const updateLocation = async () => {
const location = await getLocation()
setLocation(location)
}
updateLocation()
}, [])
return (
<div className={styles.wrapper}>
{location?.now?.city ? (
<section
aria-label="Location"
className={styles.location}
style={{ opacity: 1 }}
>
<Flag
country={{
code: location.now.country_code,
name: location.now.country
}}
/>
{location.now.city} <span>Now</span>
<div className={styles.next}>
{location?.next?.city && (
<>
{isDifferentCountry && (
<Flag
country={{
code: location.next.country_code,
name: location.next.country
}}
/>
)}
{location.next.city}{' '}
<span>
{relativeTime.from(new Date(location.next.date_start))}
</span>
</>
)}
</div>
</section>
) : null}
</div>
)
}

View File

@ -1,43 +1 @@
import RelativeTime from '@yaireo/relative-time' export * from './Location'
import { Flag } from './Flag'
import styles from './index.module.css'
import { UseLocation } from './types'
export default function Location({ location }: { location: UseLocation }) {
const isDifferentCountry = location?.now?.country !== location?.next?.country
const relativeTime = new RelativeTime({ locale: 'en' })
return (
<div className={styles.wrapper}>
{location?.now?.city ? (
<section aria-label="Location" className={styles.location}>
<Flag
country={{
code: location.now.country_code,
name: location.now.country
}}
/>
{location.now.city} <span>Now</span>
<div className={styles.next}>
{location?.next?.city && (
<>
{isDifferentCountry && (
<Flag
country={{
code: location.next.country_code,
name: location.next.country
}}
/>
)}
{location.next.city}{' '}
<span>
{relativeTime.from(new Date(location.next.date_start))}
</span>
</>
)}
</div>
</section>
) : null}
</div>
)
}