feat: fetch location and content data from database

This commit is contained in:
log101 2024-01-26 20:32:32 +03:00
parent 38dd1eb2f1
commit 6e42f5d55e
10 changed files with 150 additions and 94 deletions

View File

@ -9,6 +9,6 @@ export default defineConfig({
integrations: [react(), tailwind({ integrations: [react(), tailwind({
applyBaseStyles: false applyBaseStyles: false
})], })],
output: "hybrid", output: "server",
adapter: vercel() adapter: vercel()
}); });

28
package-lock.json generated
View File

@ -25,6 +25,7 @@
"clsx": "^2.1.0", "clsx": "^2.1.0",
"kysely": "^0.26.0", "kysely": "^0.26.0",
"lucide-react": "^0.309.0", "lucide-react": "^0.309.0",
"nanoid": "^5.0.4",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"tailwind-merge": "^2.2.0", "tailwind-merge": "^2.2.0",
@ -5003,9 +5004,9 @@
} }
}, },
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.3.7", "version": "5.0.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.4.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "integrity": "sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==",
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@ -5013,10 +5014,10 @@
} }
], ],
"bin": { "bin": {
"nanoid": "bin/nanoid.cjs" "nanoid": "bin/nanoid.js"
}, },
"engines": { "engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" "node": "^18 || >=20"
} }
}, },
"node_modules/napi-build-utils": { "node_modules/napi-build-utils": {
@ -5689,6 +5690,23 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
}, },
"node_modules/postcss/node_modules/nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/postgres-array": { "node_modules/postgres-array": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",

View File

@ -27,6 +27,7 @@
"clsx": "^2.1.0", "clsx": "^2.1.0",
"kysely": "^0.26.0", "kysely": "^0.26.0",
"lucide-react": "^0.309.0", "lucide-react": "^0.309.0",
"nanoid": "^5.0.4",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"tailwind-merge": "^2.2.0", "tailwind-merge": "^2.2.0",

View File

@ -7,7 +7,7 @@ import { useEffect, useState } from "react"
import "../styles/locked-content.css" import "../styles/locked-content.css"
const LocationButton = () => { const LocationButton = ({ imageUrl = "#" }: { imageUrl?: string }) => {
const [atTarget, setAtTarget] = useState(false) const [atTarget, setAtTarget] = useState(false)
const [contentVisible, setContentVisible] = useState(false) const [contentVisible, setContentVisible] = useState(false)
const [hasPermission, setHasPermission] = useState(false) const [hasPermission, setHasPermission] = useState(false)
@ -47,48 +47,56 @@ const LocationButton = () => {
}, []) }, [])
if (contentVisible) { if (contentVisible) {
return <div className='module-unlocked w-full h-[450px] p-4'></div> return (
<div className='w-full h-[475px] p-4 flex justify-center'>
<img src={imageUrl} />
</div>
)
} else { } else {
return ( return (
<div className='module w-full h-[450px] p-4'> <div className='w-full h-[475px] p-4'>
{atTarget ? ( {atTarget ? (
<div className='module-inside flex flex-col justify-center items-center gap-2'> <div className='flex flex-col justify-center items-center image-wrapper'>
<div> <img src={imageUrl} className='blur-lg h-[450px]' />
<div className='flex flex-col justify-center gap-2 overlay'>
<Button size={"lg"} onClick={() => setContentVisible(true)}> <Button size={"lg"} onClick={() => setContentVisible(true)}>
<LockOpen1Icon className='mr-2 h-4 w-4' /> <LockOpen1Icon className='mr-2 h-4 w-4' />
İçeriğin Kilidi ıldı İçeriğin Kilidi ıldı
</Button> </Button>
</div>
<Card className='p-2'> <Card className='p-2'>
<CardContent className='pb-0 text-center'>İçeriği görmek için butona bas!</CardContent> <CardContent className='pb-0 text-center'>İçeriği görmek için butona bas!</CardContent>
</Card> </Card>
</div>
</div> </div>
) : ( ) : (
<div className='module-inside flex flex-col justify-center items-center gap-4'> <div className='flex flex-col justify-center items-center image-wrapper'>
<Button size={"lg"}> <img src={imageUrl} className='blur-lg h-[450px]' />
<LockClosedIcon className='mr-2 h-4 w-4' /> İçerik Kilitli <div className='flex flex-col justify-center gap-2 overlay'>
</Button> <Button size='lg'>
<LockClosedIcon className='mr-2 h-4 w-4' /> İçerik Kilitli
</Button>
<Card className='p-2'>
{hasPermission ? (
<CardContent className='pb-0 text-center'>İçeriği görmek için konuma gitmelisin!</CardContent>
) : (
<div className='flex flex-col gap-2'>
<CardContent className='pb-0 text-center'>
Ne kadar yaklaştığını görmek için aşağıdaki butona bas.
</CardContent>
<Card className='p-2'> <Button
{hasPermission ? ( size={"sm"}
<CardContent className='pb-0 text-center'>İçeriği görmek için konuma gitmelisin!</CardContent> className='bg-green-700 hover:bg-green-600'
) : ( onClick={() => startWatchingLocation()}
<div className='flex flex-col gap-2'> >
<CardContent className='pb-0 text-center'> Konum İzni Ver
Ne kadar yaklaştığını görmek için aşağıdaki butona bas. </Button>
</CardContent> </div>
)}
<Button </Card>
size={"sm"} </div>
className='bg-green-700 hover:bg-green-600'
onClick={() => startWatchingLocation()}
>
Konum İzni Ver
</Button>
</div>
)}
</Card>
</div> </div>
)} )}
</div> </div>

13
src/lib/db.ts Normal file
View File

@ -0,0 +1,13 @@
import type { Generated } from "kysely"
export interface Database {
contents: ContentTable
}
export interface ContentTable {
id: Generated<string>
url: string
blob_url: string
loc: string
description: string
}

View File

@ -15,6 +15,14 @@ import { CalendarIcon } from '@radix-ui/react-icons';
import '../styles/locked-page.css'; import '../styles/locked-page.css';
import LockedContent from '@/components/LockedContent'; import LockedContent from '@/components/LockedContent';
import { Separator } from '@/components/ui/separator'; import { Separator } from '@/components/ui/separator';
const { id } = Astro.params;
const res = await fetch(`http://localhost:4321/api/content?id=${id}`);
const data = await res.json();
console.log(data);
--- ---
<Layout> <Layout>
@ -39,10 +47,7 @@ import { Separator } from '@/components/ui/separator';
<CardTitle>Abdurrahman</CardTitle> <CardTitle>Abdurrahman</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<p> <p>{data.description}</p>
Hacı sana çok özel bir sürpriz hazırladık, aşağıdaki konuma muhakak
git.
</p>
</CardContent> </CardContent>
<CardFooter className="gap-2"> <CardFooter className="gap-2">
<CalendarIcon /> <CalendarIcon />
@ -50,9 +55,14 @@ import { Separator } from '@/components/ui/separator';
</CardFooter> </CardFooter>
</Card> </Card>
<LockedContent client:load /> <LockedContent imageUrl={data.blob_url} client:load />
<div id="map" class="w-full h-[450px] rounded"></div> <div
id="map"
class="w-full h-[450px] rounded"
data-target-location={data.loc}
>
</div>
<Button className="w-full">Paylaş</Button> <Button className="w-full">Paylaş</Button>
<div class="flex justify-center"> <div class="flex justify-center">

View File

@ -1,20 +1,9 @@
export const prerender = false
import { put } from "@vercel/blob" import { put } from "@vercel/blob"
import type { Generated } from "kysely"
import type { APIRoute } from "astro" import type { APIRoute } from "astro"
import { createKysely } from "@vercel/postgres-kysely" import { createKysely } from "@vercel/postgres-kysely"
import { customAlphabet } from "nanoid"
interface ContentTable { import type { Database } from "../../lib/db"
id: Generated<number>
blob_url: string
loc: string
description: string
}
interface Database {
contents: ContentTable
}
export const POST: APIRoute = async ({ request }) => { export const POST: APIRoute = async ({ request }) => {
const data = await request.formData() const data = await request.formData()
@ -43,9 +32,14 @@ export const POST: APIRoute = async ({ request }) => {
const db = createKysely<Database>({ connectionString: import.meta.env.POSTGRES_URL }) const db = createKysely<Database>({ connectionString: import.meta.env.POSTGRES_URL })
const nanoid = customAlphabet("abcdefghijklmnoprstuvyz", 10)
const newUrl = nanoid()
const res = await db const res = await db
.insertInto("contents") .insertInto("contents")
.values({ .values({
url: `${newUrl.slice(0, 3)}-${newUrl.slice(3, 7)}-${newUrl.slice(7)}`,
blob_url: blob.url, blob_url: blob.url,
description: description?.toString() ?? "", description: description?.toString() ?? "",
loc: `SRID=4326;POINT(${pos[0]} ${pos[1]})` loc: `SRID=4326;POINT(${pos[0]} ${pos[1]})`
@ -66,3 +60,39 @@ export const POST: APIRoute = async ({ request }) => {
}) })
} }
} }
export const GET: APIRoute = async ({ request }) => {
const contentId = new URL(request.url).searchParams.get("id")
if (!contentId) {
return new Response(null, {
status: 400,
statusText: "Content id is required"
})
}
const db = createKysely<Database>({ connectionString: import.meta.env.POSTGRES_URL })
try {
const content = await db
.selectFrom("contents")
.select(({ fn }) => ["blob_url", fn<string>("ST_AsGeoJSON", ["loc"]).as("loc"), "description"])
.where("url", "=", contentId)
.executeTakeFirst()
if (content) {
return new Response(JSON.stringify(content))
} else {
return new Response(null, {
status: 404,
statusText: "Content not found"
})
}
} catch (error) {
console.error("Error fetching content:", error)
return new Response(null, {
status: 500,
statusText: "Error while fetching content"
})
}
}

View File

@ -1,4 +1,6 @@
--- ---
export const prerender = true;
import '@/styles/globals.css'; import '@/styles/globals.css';
import Layout from '../layouts/Layout.astro'; import Layout from '../layouts/Layout.astro';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
@ -109,7 +111,7 @@ import { Textarea } from '@/components/ui/textarea';
const formData = new FormData(e.target as HTMLFormElement); const formData = new FormData(e.target as HTMLFormElement);
const response = await fetch(`/create-url`, { const response = await fetch(`/api/content`, {
method: 'POST', method: 'POST',
body: formData, body: formData,
}); });

View File

@ -1,4 +1,6 @@
const TARGET_LOCATION = [41.01907795861253, 29.01715377829709]; const data = JSON.parse(document.getElementById('map').dataset.targetLocation)
const TARGET_LOCATION = data.coordinates
var map = L.map('map').setView(TARGET_LOCATION, 13); var map = L.map('map').setView(TARGET_LOCATION, 13);

View File

@ -1,42 +1,14 @@
.module { .image-wrapper {
display: grid;
place-items: center;
position: relative; position: relative;
overflow: hidden;
} }
.module-unlocked { .overlay {
display: grid;
place-items: center;
position: relative;
overflow: hidden;
}
.module::before {
content: "";
top: 0;
left: 0;
width: 100%;
height: 100%;
position: absolute; position: absolute;
background-image: url("/sample-selfie.webp");
background-position: center;
background-repeat: no-repeat;
filter: blur(20px);
}
.module-unlocked::before { /* center overlay text */
content: ""; display: flex;
top: 0; align-items: center;
left: 0; justify-content: center;
width: 100%;
height: 100%;
position: absolute;
background-image: url("/sample-selfie.webp");
background-position: center;
background-repeat: no-repeat;
}
.module-inside { inset: 0;
position: relative;
} }