refactor: replace shadui components with standard html elements

feat: add new unlock button web component
This commit is contained in:
log101 2024-07-06 13:39:36 +03:00
parent 296083577a
commit 68750ad964
9 changed files with 1483 additions and 135 deletions

View File

@ -2,7 +2,7 @@ import { defineConfig } from 'astro/config';
import react from "@astrojs/react"; import react from "@astrojs/react";
import tailwind from "@astrojs/tailwind"; import tailwind from "@astrojs/tailwind";
import vercel from "@astrojs/vercel/serverless"; import node from "@astrojs/node";
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
@ -10,5 +10,7 @@ export default defineConfig({
applyBaseStyles: false applyBaseStyles: false
})], })],
output: "server", output: "server",
adapter: vercel() adapter: node({
mode: "standalone"
}),
}); });

BIN
bun.lockb

Binary file not shown.

View File

@ -11,6 +11,7 @@
}, },
"dependencies": { "dependencies": {
"@astrojs/check": "^0.4.1", "@astrojs/check": "^0.4.1",
"@astrojs/node": "^8.3.2",
"@astrojs/react": "^3.0.9", "@astrojs/react": "^3.0.9",
"@astrojs/tailwind": "^5.1.0", "@astrojs/tailwind": "^5.1.0",
"@astrojs/vercel": "^7.3.1", "@astrojs/vercel": "^7.3.1",
@ -19,12 +20,8 @@
"@radix-ui/react-label": "^2.0.2", "@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-slot": "^1.0.2",
"@supabase/supabase-js": "^2.39.7",
"@types/react": "^18.2.47", "@types/react": "^18.2.47",
"@types/react-dom": "^18.2.18", "@types/react-dom": "^18.2.18",
"@vercel/blob": "^0.19.0",
"@vercel/postgres-kysely": "^0.7.1",
"@vercel/speed-insights": "^1.0.9",
"astro": "^4.1.2", "astro": "^4.1.2",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.0", "clsx": "^2.1.0",

1276
public/css/tailwind.css Normal file

File diff suppressed because it is too large Load Diff

View File

@ -95,61 +95,65 @@ const LocationButton = ({
); );
} else { } else {
return ( return (
<div className="w-full h-[475px] overflow-hidden border border-zinc-200 shadow-sm p-4 rounded"> <>
{atTarget ? ( <div className="w-full h-[475px] overflow-hidden border border-zinc-200 shadow-sm p-4 rounded">
<div className="flex flex-col justify-center items-center image-wrapper"> {atTarget ? (
<img src={imageUrl} className="blur-2xl h-[450px]" /> <div className="flex flex-col justify-center items-center image-wrapper">
<img src={imageUrl} className="blur-2xl h-[450px]" />
<div className="flex flex-col justify-center gap-4 overlay"> <div className="flex flex-col justify-center gap-4 overlay">
<Button <Button
size="lg" size="lg"
className="text-lg p-6 animate-pulse bg-indigo-600 hover:bg-indigo-700 hover:animate-none border-2 border-indigo-800" className="text-lg p-6 animate-pulse bg-indigo-600 hover:bg-indigo-700 hover:animate-none border-2 border-indigo-800"
onClick={handleUnlock} onClick={handleUnlock}
> >
<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>
<Card className="p-2"> <Card className="p-2">
<CardContent className="pb-0 text-center">
İçeriği görmek için butona bas!
</CardContent>
</Card>
</div>
</div>
) : (
<div className="flex flex-col justify-center items-center image-wrapper">
<img src={imageUrl} className="blur-2xl h-[450px]" />
<div className="flex flex-col justify-center gap-4 overlay">
<Button size="lg" className="text-md">
<LockClosedIcon className="mr-2 h-4 w-4" /> İçerik Kilitli
</Button>
<Card className="p-2">
{hasPermission ? (
<CardContent className="pb-0 text-center"> <CardContent className="pb-0 text-center">
<p>İçeriği görmek için konuma gitmelisin!</p> İçeriği görmek için butona bas!
<p>{distanceRemain && `Kalan mesafe: ${distanceRemain}`}</p>
</CardContent> </CardContent>
) : ( </Card>
<div className="flex flex-col gap-2"> </div>
<CardContent className="pb-0 text-center">
Ne kadar yaklaştığını görmek için aşağıdaki butona bas.
</CardContent>
<Button
size="sm"
className="bg-green-700 hover:bg-green-600 text-md"
onClick={() => startWatchingLocation()}
>
Konum İzni Ver
</Button>
</div>
)}
</Card>
</div> </div>
</div> ) : (
)} <div className="flex flex-col justify-center items-center image-wrapper">
</div> <img src={imageUrl} className="blur-2xl h-[450px]" />
<div className="flex flex-col justify-center gap-4 overlay">
<Button size="lg" className="text-md">
<LockClosedIcon className="mr-2 h-4 w-4" /> İçerik Kilitli
</Button>
<Card className="p-2">
{hasPermission ? (
<CardContent className="pb-0 text-center">
<p>İçeriği görmek için konuma gitmelisin!</p>
<p>
{distanceRemain && `Kalan mesafe: ${distanceRemain}`}
</p>
</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>
<Button
size="sm"
className="bg-green-700 hover:bg-green-600 text-md"
onClick={() => startWatchingLocation()}
>
Konum İzni Ver
</Button>
</div>
)}
</Card>
</div>
</div>
)}
</div>
</>
); );
} }
}; };

View File

@ -0,0 +1,94 @@
---
interface Props {
contentId?: string;
imageUrl: string;
location?: string;
}
const { contentId = "", imageUrl = "#", location = "" } = Astro.props;
---
<template id="locked-content-template">
<link rel="stylesheet" href="css/tailwind.css" />
<style>
.image-wrapper {
position: relative;
}
.overlay {
position: absolute;
/* center overlay text */
display: flex;
align-items: center;
justify-content: center;
inset: 0;
}
</style>
<div
class="w-full h-[475px] overflow-hidden border border-zinc-200 shadow-sm p-4 rounded"
>
<div class="flex flex-col justify-center items-center image-wrapper">
<img src={imageUrl} class="blur-2xl h-[450px]" />
<div class="flex flex-col justify-center gap-4 overlay">
<button
id="unlock-content-button"
class="inline-flex items-center justify-center whitespace-nowrap font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 text-primary-foreground h-11 rounded-md text-lg p-6 animate-pulse bg-indigo-600 hover:bg-indigo-700 hover:animate-none border-2 border-indigo-800"
>
İçeriğin Kilidi Açıldı
</button>
<div
class="rounded-lg border bg-card text-card-foreground shadow-sm p-2"
>
<div class="pb-0 text-center">İçeriği görmek için butona bas!</div>
</div>
</div>
</div>
</div>
</template>
<locked-content contentId={contentId}></locked-content>
<script>
customElements.define(
"locked-content",
class extends HTMLElement {
constructor() {
super();
// Clone the template
let template = document.getElementById(
"locked-content-template"
) as HTMLTemplateElement;
let templateContent = template.content;
// Get attributes
const contentId = this.getAttribute("contentId");
// Attach cloned template to DOM
const shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.appendChild(templateContent.cloneNode(true));
// Add onclick listener to unlock content button
const unlockContentButton = shadowRoot.getElementById(
"unlock-content-button"
);
const incrementCounter = async (id: string) =>
await fetch(`http://localhost:3000/api/location/increment/${id}`, {
method: "PATCH",
});
if (unlockContentButton) {
unlockContentButton.addEventListener("click", () => {
if (contentId) {
incrementCounter(contentId);
}
});
}
}
}
);
</script>

View File

@ -1,7 +1,3 @@
---
import SpeedInsights from '@vercel/speed-insights/astro';
---
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
@ -41,7 +37,6 @@ import SpeedInsights from '@vercel/speed-insights/astro';
</head> </head>
<body class="container my-8"> <body class="container my-8">
<slot /> <slot />
<SpeedInsights />
</body> </body>
</html> </html>
<style is:global></style> <style is:global></style>

View File

@ -1,35 +1,34 @@
--- ---
import '@/styles/globals.css'; import "@/styles/globals.css";
import '../styles/locked-page.css'; import "../styles/locked-page.css";
import Layout from '../layouts/Layout.astro'; import Layout from "../layouts/Layout.astro";
import ShareButton from '../components/ShareButton'; import ShareButton from "../components/ShareButton";
import { import {
Card, Card,
CardContent, CardContent,
CardFooter, CardFooter,
CardHeader, CardHeader,
CardTitle, CardTitle,
} from '@/components/ui/card'; } from "@/components/ui/card";
import { CalendarIcon } from '@radix-ui/react-icons'; import LockedContent from "@/components/LockedContent";
import LockedContent from '@/components/LockedContent'; import WebComponentWrapper from "@/components/WebComponentWrapper.astro";
import { Separator } from '@/components/ui/separator'; import { CalendarIcon } from "@radix-ui/react-icons";
import type { ContentTable } from '@/lib/db'; import { Separator } from "@/components/ui/separator";
import dayjs from 'dayjs'; import type { ContentTable } from "@/lib/db";
import relativeTime from 'dayjs/plugin/relativeTime'; import dayjs from "dayjs";
import utc from 'dayjs/plugin/utc'; import relativeTime from "dayjs/plugin/relativeTime";
import utc from "dayjs/plugin/utc";
type Content = ContentTable; type Content = ContentTable;
const { id } = Astro.params; const { id } = Astro.params;
const res = await fetch( const res = await fetch(`http://localhost:3000/api/location/${id}`);
`http://localhost:3000/api/location/${id}`
);
const data: Content | null = res.status === 200 ? await res.json() : null; const data: Content | null = res.status === 200 ? await res.json() : null;
console.log(data) console.log(data);
dayjs.extend(relativeTime); dayjs.extend(relativeTime);
@ -68,7 +67,6 @@ const dateFromNow = dayjs.utc(data?.created_at).from(dayjs.utc());
<p>{dateFromNow}</p> <p>{dateFromNow}</p>
</CardFooter> </CardFooter>
</Card> </Card>
<LockedContent <LockedContent
contentId={data?.url} contentId={data?.url}
imageUrl={`http://localhost:3000/images/${data?.blob_url}`} imageUrl={`http://localhost:3000/images/${data?.blob_url}`}

View File

@ -1,15 +1,10 @@
--- ---
export const prerender = true; export const prerender = true;
import '@/styles/globals.css'; import "@/styles/globals.css";
import Layout from '../layouts/Layout.astro'; import "../styles/locked-page.css";
import { Button } from '@/components/ui/button';
import '../styles/locked-page.css'; import Layout from "../layouts/Layout.astro";
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { ReloadIcon } from '@radix-ui/react-icons';
import { Label } from '@/components/ui/label';
--- ---
<Layout> <Layout>
@ -44,13 +39,14 @@ import { Label } from '@/components/ui/label';
getirilecek. getirilecek.
</p> </p>
<div class="grid w-full max-w-sm items-center gap-1.5 mt-4"> <div class="grid w-full max-w-sm items-center gap-1.5 mt-4">
<Input <input
type="file" type="file"
name="selected-photo" name="selected-photo"
id="photo-selector" id="photo-selector"
class="p-2 border border-gray rounded-lg file:bg-inherit file:border-0"
required required
/> />
<p class="text-sm text-muted-foreground"> <p class="px-2 text-sm text-muted-foreground">
Galerinizden bir fotoğraf seçin. Galerinizden bir fotoğraf seçin.
</p> </p>
</div> </div>
@ -101,31 +97,27 @@ import { Label } from '@/components/ui/label';
</h2> </h2>
<div class="grid w-full max-w-md items-center gap-2 mt-4"> <div class="grid w-full max-w-md items-center gap-2 mt-4">
<div class="grid w-full items-center gap-1.5"> <div class="grid w-full items-center gap-1.5">
<Label htmlFor="location-author" className="lg:text-lg text-md"> <label for="location-author" class="lg:text-lg text-md">
Gönderici Gönderici
</Label> </label>
<Input <input
id="location-author" id="location-author"
name="author" name="author"
placeholder="İsminizi buraya yazınız." placeholder="İsminizi buraya yazınız."
className="lg:text-lg text-md" class="lg:text-lg text-md py-2 px-3 border border-gray rounded-lg placeholder:text-slate-400"
required required
/> />
</div> </div>
<div class="grid w-full items-center gap-1.5"> <div class="grid w-full items-center gap-1.5">
<Label <label for="location-description" class="lg:text-lg text-md">
htmlFor="location-description"
className="lg:text-lg text-md"
>
ıklama ıklama
</Label> </label>
<Textarea <textarea
placeholder="Açıklamanızı buraya yazınız." placeholder="Açıklamanızı buraya yazınız."
name="description" name="description"
id="location-description" id="location-description"
className="lg:text-lg text-md" class="lg:text-lg text-md py-2 px-3 border border-gray rounded-lg placeholder:text-slate-400 resize-none"
required required></textarea>
/>
<p class="text-sm text-muted-foreground"> <p class="text-sm text-muted-foreground">
Bir açıklama girin, yüklediğiniz içeriğin üzerine bu metin Bir açıklama girin, yüklediğiniz içeriğin üzerine bu metin
görünecek. görünecek.
@ -134,26 +126,21 @@ import { Label } from '@/components/ui/label';
</div> </div>
<div class="flex gap-2 my-6 items-center"> <div class="flex gap-2 my-6 items-center">
<input type="checkbox" id="kvkk" class="w-6 h-6" required /> <input type="checkbox" id="kvkk" class="w-6 h-6" required />
<Label <label
htmlFor="kvkk" for="kvkk"
className="text-lg font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" class="text-lg font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
> >
Konulu Konum'u KVKK kapsamında dava etmeyeceğimi onaylıyorum. Konulu Konum'u KVKK kapsamında dava etmeyeceğimi onaylıyorum.
</Label> </label>
</div> </div>
<Button <button
className="w-full text-lg" class="w-full text-lg bg-slate-900 text-white p-2 rounded-lg"
type="submit" type="submit"
id="submit-button" id="submit-button"
size="lg"
> >
<ReloadIcon
className="mr-2 h-4 w-4 animate-spin hidden"
id="submit-button-reload"
/>
Bağlantıyı Oluştur Bağlantıyı Oluştur
</Button> </button>
</div> </div>
</form> </form>
<script src="../scripts/initSelectionMap.js"></script> <script src="../scripts/initSelectionMap.js"></script>
@ -164,67 +151,62 @@ import { Label } from '@/components/ui/label';
e.preventDefault(); e.preventDefault();
const locationSelected = document.getElementById( const locationSelected = document.getElementById(
'location-selected-confirmation' "location-selected-confirmation"
)?.innerText; )?.innerText;
if (!locationSelected) { if (!locationSelected) {
const coordinatesText = document.getElementById('coordinates'); const coordinatesText = document.getElementById("coordinates");
const mapDiv = document.getElementById('map'); const mapDiv = document.getElementById("map");
mapDiv?.classList.add('border-slate-700'); mapDiv?.classList.add("border-slate-700");
coordinatesText?.classList.add('drop-shadow-xl'); coordinatesText?.classList.add("drop-shadow-xl");
setTimeout(() => { setTimeout(() => {
mapDiv?.classList.remove('border-slate-700'); mapDiv?.classList.remove("border-slate-700");
coordinatesText?.classList.remove('drop-shadow-xl'); coordinatesText?.classList.remove("drop-shadow-xl");
}, 500); }, 500);
return; return;
} }
const submitButton = document.getElementById( const submitButton = document.getElementById(
'submit-button' "submit-button"
) as HTMLButtonElement; ) as HTMLButtonElement;
const reloadIcon = document.getElementById(
'submit-button-reload'
) as HTMLElement;
submitButton.disabled = true; submitButton.disabled = true;
reloadIcon.classList.toggle('hidden');
const formData = new FormData(e.target as HTMLFormElement); const formData = new FormData(e.target as HTMLFormElement);
const res = await fetch(`http://127.0.0.1:3000/api/location`, { const res = await fetch(`http://127.0.0.1:3000/api/location`, {
method: 'POST', method: "POST",
body: formData, body: formData,
}); });
reloadIcon.classList.toggle('hidden');
submitButton.disabled = false; submitButton.disabled = false;
if (res.status === 200) { if (res.status === 200) {
const data = await res.json(); const data = await res.json();
if (data.url) location.assign('/' + data.url); if (data.url) location.assign("/" + data.url);
} else { } else {
// @ts-ignore // @ts-ignore
Toastify({ Toastify({
text: 'Konulu konum oluşturulamadı, lütfen tekrar deneyin.', text: "Konulu konum oluşturulamadı, lütfen tekrar deneyin.",
duration: 3000, duration: 3000,
gravity: 'top', // `top` or `bottom` gravity: "top", // `top` or `bottom`
position: 'center', // `left`, `center` or `right` position: "center", // `left`, `center` or `right`
stopOnFocus: true, // Prevents dismissing of toast on hover stopOnFocus: true, // Prevents dismissing of toast on hover
style: { style: {
background: 'black', background: "black",
borderRadius: '6px', borderRadius: "6px",
margin: '16px', margin: "16px",
}, },
onClick: function () {}, // Callback after click onClick: function () {}, // Callback after click
}).showToast(); }).showToast();
} }
}; };
document.getElementById('sample-form')!.onsubmit = handleSubmit; document.getElementById("sample-form")!.onsubmit = handleSubmit;
</script> </script>
</Layout> </Layout>