mirror of
https://github.com/soconnor0919/lewisburg-coffee.git
synced 2026-02-04 23:56:32 -05:00
feat: add PWA manifest and Apple icon, and enhance map with fly-to selected shop functionality.
This commit is contained in:
34
src/app/apple-icon.tsx
Normal file
34
src/app/apple-icon.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { ImageResponse } from 'next/og';
|
||||||
|
|
||||||
|
export const runtime = 'edge';
|
||||||
|
|
||||||
|
export const size = {
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
};
|
||||||
|
export const contentType = 'image/png';
|
||||||
|
|
||||||
|
export default function Icon() {
|
||||||
|
return new ImageResponse(
|
||||||
|
(
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: 24,
|
||||||
|
background: '#8B4513',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
color: 'white',
|
||||||
|
borderRadius: '20%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
☕
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
{
|
||||||
|
...size,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import "~/styles/globals.css";
|
import "~/styles/globals.css";
|
||||||
|
|
||||||
import { type Metadata } from "next";
|
import { type Metadata, type Viewport } from "next";
|
||||||
import { PT_Serif } from "next/font/google";
|
import { PT_Serif } from "next/font/google";
|
||||||
|
|
||||||
import { ThemeProvider } from "~/components/ThemeProvider";
|
import { ThemeProvider } from "~/components/ThemeProvider";
|
||||||
@@ -11,6 +11,14 @@ export const metadata: Metadata = {
|
|||||||
icons: [{ rel: "icon", url: "/favicon.ico" }],
|
icons: [{ rel: "icon", url: "/favicon.ico" }],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const viewport: Viewport = {
|
||||||
|
themeColor: "#8B4513",
|
||||||
|
width: "device-width",
|
||||||
|
initialScale: 1,
|
||||||
|
maximumScale: 1,
|
||||||
|
userScalable: false,
|
||||||
|
};
|
||||||
|
|
||||||
const ptSerif = PT_Serif({
|
const ptSerif = PT_Serif({
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
weight: ["400", "700"],
|
weight: ["400", "700"],
|
||||||
|
|||||||
25
src/app/manifest.ts
Normal file
25
src/app/manifest.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { type MetadataRoute } from 'next';
|
||||||
|
|
||||||
|
export default function manifest(): MetadataRoute.Manifest {
|
||||||
|
return {
|
||||||
|
name: 'Lewisburg Coffee Map',
|
||||||
|
short_name: 'Coffee Map',
|
||||||
|
description: 'Find the best coffee in Lewisburg, PA',
|
||||||
|
start_url: '/',
|
||||||
|
display: 'standalone',
|
||||||
|
background_color: '#ffffff',
|
||||||
|
theme_color: '#8B4513',
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
src: '/favicon.ico',
|
||||||
|
sizes: 'any',
|
||||||
|
type: 'image/x-icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: '/icon.svg',
|
||||||
|
sizes: 'any',
|
||||||
|
type: 'image/svg+xml',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ export default function HomePage() {
|
|||||||
<MapLoader
|
<MapLoader
|
||||||
shops={COFFEE_SHOPS}
|
shops={COFFEE_SHOPS}
|
||||||
onShopSelect={(shop: typeof COFFEE_SHOPS[0]) => setSelectedShop(shop)}
|
onShopSelect={(shop: typeof COFFEE_SHOPS[0]) => setSelectedShop(shop)}
|
||||||
|
selectedShop={selectedShop}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { MapContainer, TileLayer, Marker } from 'react-leaflet';
|
import { MapContainer, TileLayer, Marker, useMap } from 'react-leaflet';
|
||||||
import 'leaflet/dist/leaflet.css';
|
import 'leaflet/dist/leaflet.css';
|
||||||
import L from 'leaflet';
|
import L from 'leaflet';
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
@@ -24,9 +24,25 @@ interface CoffeeShop {
|
|||||||
interface MapProps {
|
interface MapProps {
|
||||||
shops: CoffeeShop[];
|
shops: CoffeeShop[];
|
||||||
onShopSelect: (shop: CoffeeShop) => void;
|
onShopSelect: (shop: CoffeeShop) => void;
|
||||||
|
selectedShop: CoffeeShop | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Map = ({ shops, onShopSelect }: MapProps) => {
|
const MapController = ({ selectedShop }: { selectedShop: CoffeeShop | null }) => {
|
||||||
|
const map = useMap();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedShop) {
|
||||||
|
map.flyTo([selectedShop.lat, selectedShop.lng], 16, {
|
||||||
|
duration: 1.5,
|
||||||
|
easeLinearity: 0.25,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [selectedShop, map]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Map = ({ shops, onShopSelect, selectedShop }: MapProps) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Fix for Leaflet default icon not found
|
// Fix for Leaflet default icon not found
|
||||||
// @ts-expect-error Fix for Leaflet default icon not found
|
// @ts-expect-error Fix for Leaflet default icon not found
|
||||||
@@ -107,6 +123,7 @@ const Map = ({ shops, onShopSelect }: MapProps) => {
|
|||||||
attributionControl={false}
|
attributionControl={false}
|
||||||
>
|
>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
|
<MapController selectedShop={selectedShop} />
|
||||||
<div className="absolute bottom-8 right-4 z-[1000] flex flex-col gap-2 items-end">
|
<div className="absolute bottom-8 right-4 z-[1000] flex flex-col gap-2 items-end">
|
||||||
<ZoomControls />
|
<ZoomControls />
|
||||||
<MapStyleControl currentStyle={mapStyle} onStyleChange={handleStyleChange} />
|
<MapStyleControl currentStyle={mapStyle} onStyleChange={handleStyleChange} />
|
||||||
|
|||||||
Reference in New Issue
Block a user