feat: show counter at the bottom of the page
This commit is contained in:
parent
3060afa59a
commit
75a403ddf2
|
@ -6,8 +6,18 @@ import { LockClosedIcon, LockOpen1Icon } from "@radix-ui/react-icons"
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
import "../styles/locked-content.css"
|
import "../styles/locked-content.css"
|
||||||
|
import type { Generated } from "kysely"
|
||||||
|
|
||||||
const LocationButton = ({ imageUrl = "#" }: { imageUrl?: string }) => {
|
const incrementCounter = async (id: string | Generated<string>) =>
|
||||||
|
await fetch(`${import.meta.env.PUBLIC_HOME_URL}/api/content/increment?id=${id}`)
|
||||||
|
|
||||||
|
const LocationButton = ({
|
||||||
|
contentId = "",
|
||||||
|
imageUrl = "#"
|
||||||
|
}: {
|
||||||
|
contentId?: string | Generated<string>
|
||||||
|
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)
|
||||||
|
@ -37,6 +47,11 @@ const LocationButton = ({ imageUrl = "#" }: { imageUrl?: string }) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleUnlock = async () => {
|
||||||
|
setContentVisible(true)
|
||||||
|
await incrementCounter(contentId)
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
navigator.permissions.query({ name: "geolocation" }).then(permissionStatus => {
|
navigator.permissions.query({ name: "geolocation" }).then(permissionStatus => {
|
||||||
if (permissionStatus.state === "granted") {
|
if (permissionStatus.state === "granted") {
|
||||||
|
@ -60,7 +75,7 @@ const LocationButton = ({ imageUrl = "#" }: { imageUrl?: string }) => {
|
||||||
<img src={imageUrl} className='blur-lg h-[450px]' />
|
<img src={imageUrl} className='blur-lg h-[450px]' />
|
||||||
|
|
||||||
<div className='flex flex-col justify-center gap-4 overlay'>
|
<div className='flex flex-col justify-center gap-4 overlay'>
|
||||||
<Button size='lg' className='text-md' onClick={() => setContentVisible(true)}>
|
<Button size='lg' className='text-md' onClick={handleUnlock}>
|
||||||
<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>
|
||||||
|
|
|
@ -12,4 +12,5 @@ export interface ContentTable {
|
||||||
author: string
|
author: string
|
||||||
description: string
|
description: string
|
||||||
created_at: Generated<string>
|
created_at: Generated<string>
|
||||||
|
unlocked_counter: Generated<number>
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,13 @@ import dayjs from 'dayjs';
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||||
import localeTR from 'dayjs/locale/tr';
|
import localeTR from 'dayjs/locale/tr';
|
||||||
|
|
||||||
type Content = Omit<ContentTable, 'id' | 'url'>;
|
type Content = Omit<ContentTable, 'url'>;
|
||||||
|
|
||||||
const { id } = Astro.params;
|
const { id } = Astro.params;
|
||||||
|
|
||||||
const res = await fetch(`${import.meta.env.HOME_URL}/api/content?id=${id}`);
|
const res = await fetch(
|
||||||
|
`${import.meta.env.PUBLIC_HOME_URL}/api/content?id=${id}`
|
||||||
|
);
|
||||||
|
|
||||||
const data: Content | null = res.status === 200 ? await res.json() : null;
|
const data: Content | null = res.status === 200 ? await res.json() : null;
|
||||||
|
|
||||||
|
@ -65,7 +67,7 @@ const dateFromNow = dayjs(data?.created_at).fromNow();
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<LockedContent imageUrl={data?.blob_url} client:load />
|
<LockedContent contentId={data?.id} imageUrl={data?.blob_url} client:load />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
id="map"
|
id="map"
|
||||||
|
@ -77,7 +79,7 @@ const dateFromNow = dayjs(data?.created_at).fromNow();
|
||||||
<Button className="w-full text-lg" size="lg">Paylaş</Button>
|
<Button className="w-full text-lg" size="lg">Paylaş</Button>
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<p class="text-muted-foreground">
|
<p class="text-muted-foreground">
|
||||||
Fotoğrafın kilidi şu ana dek <b>2</b> kez açıldı
|
Fotoğrafın kilidi şu ana dek <b>{data?.unlocked_counter}</b> kez açıldı
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
39
src/pages/api/content/increment.ts
Normal file
39
src/pages/api/content/increment.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import type { Database } from "@/lib/db"
|
||||||
|
import { createKysely } from "@vercel/postgres-kysely"
|
||||||
|
import type { APIRoute } from "astro"
|
||||||
|
|
||||||
|
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 result = await db
|
||||||
|
.updateTable("contents")
|
||||||
|
.set(eb => ({ unlocked_counter: eb("unlocked_counter", "+", 1) }))
|
||||||
|
.where("id", "=", contentId)
|
||||||
|
.executeTakeFirst()
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return new Response(JSON.stringify({ counter: Number(result) }))
|
||||||
|
} else {
|
||||||
|
return new Response(null, {
|
||||||
|
status: 404,
|
||||||
|
statusText: "Could not increment the counter"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching content:", error)
|
||||||
|
return new Response(null, {
|
||||||
|
status: 500,
|
||||||
|
statusText: "Error while fetching content"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ import type { APIRoute } from "astro"
|
||||||
import { createKysely } from "@vercel/postgres-kysely"
|
import { createKysely } from "@vercel/postgres-kysely"
|
||||||
import { customAlphabet } from "nanoid"
|
import { customAlphabet } from "nanoid"
|
||||||
|
|
||||||
import type { Database } from "../../lib/db"
|
import type { Database } from "@/lib/db"
|
||||||
|
|
||||||
export const POST: APIRoute = async ({ request }) => {
|
export const POST: APIRoute = async ({ request }) => {
|
||||||
const data = await request.formData()
|
const data = await request.formData()
|
||||||
|
@ -79,11 +79,13 @@ export const GET: APIRoute = async ({ request }) => {
|
||||||
const content = await db
|
const content = await db
|
||||||
.selectFrom("contents")
|
.selectFrom("contents")
|
||||||
.select(({ fn }) => [
|
.select(({ fn }) => [
|
||||||
|
"id",
|
||||||
"blob_url",
|
"blob_url",
|
||||||
fn<string>("ST_AsGeoJSON", ["loc"]).as("loc"),
|
fn<string>("ST_AsGeoJSON", ["loc"]).as("loc"),
|
||||||
"description",
|
"description",
|
||||||
"author",
|
"author",
|
||||||
"created_at"
|
"created_at",
|
||||||
|
"unlocked_counter"
|
||||||
])
|
])
|
||||||
.where("url", "=", contentId)
|
.where("url", "=", contentId)
|
||||||
.executeTakeFirst()
|
.executeTakeFirst()
|
Loading…
Reference in New Issue
Block a user