feat: fetch location and content data from database
This commit is contained in:
parent
38dd1eb2f1
commit
6e42f5d55e
|
@ -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
28
package-lock.json
generated
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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 Açıldı
|
İçeriğin Kilidi Açı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
13
src/lib/db.ts
Normal 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
|
||||||
|
}
|
|
@ -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">
|
|
@ -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"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
});
|
});
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user