diff --git a/.prettierrc b/.prettierrc index d9cf6b8..768a00d 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,7 +1,7 @@ { "endOfLine": "lf", "printWidth": 80, - "arrowParens": "avoid", + "arrowParens": "always", "bracketSpacing": true, "htmlWhitespaceSensitivity": "css", "insertPragma": false, @@ -10,7 +10,7 @@ "proseWrap": "always", "quoteProps": "as-needed", "requirePragma": false, - "semi": true, + "semi": false, "singleQuote": false, "tabWidth": 2, "trailingComma": "es5", diff --git a/README.md b/README.md index 1db3fb3..1dc9d18 100644 --- a/README.md +++ b/README.md @@ -1,54 +1 @@ -# Astro Starter Kit: Basics - -```sh -npm create astro@latest -- --template basics -``` - -[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics) -[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/basics) -[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/basics/devcontainer.json) - -> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! - -![just-the-basics](https://github.com/withastro/astro/assets/2244813/a0a5533c-a856-4198-8470-2d67b1d7c554) - -## 🚀 Project Structure - -Inside of your Astro project, you'll see the following folders and files: - -```text -/ -├── public/ -│ └── favicon.svg -├── src/ -│ ├── components/ -│ │ └── Card.astro -│ ├── layouts/ -│ │ └── Layout.astro -│ └── pages/ -│ └── index.astro -└── package.json -``` - -Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name. - -There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components. - -Any static assets, like images, can be placed in the `public/` directory. - -## 🧞 Commands - -All commands are run from the root of the project, from a terminal: - -| Command | Action | -| :------------------------ | :----------------------------------------------- | -| `npm install` | Installs dependencies | -| `npm run dev` | Starts local dev server at `localhost:4321` | -| `npm run build` | Build your production site to `./dist/` | -| `npm run preview` | Preview your build locally, before deploying | -| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | -| `npm run astro -- --help` | Get help using the Astro CLI | - -## 👀 Want to learn more? - -Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat). +# KONULU KONUM diff --git a/bun.lockb b/bun.lockb index 40448cf..753d514 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index a128817..b146341 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,7 @@ "clsx": "^2.1.1", "dayjs": "^1.11.11", "htmx.org": "^1.9.12", - "kysely": "^0.26.3", "leaflet": "^1.9.4", - "lit": "^3.1.4", "lucide-react": "^0.309.0", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/src/components/Card.astro b/src/components/Card.astro deleted file mode 100644 index bd6d597..0000000 --- a/src/components/Card.astro +++ /dev/null @@ -1,61 +0,0 @@ ---- -interface Props { - title: string; - body: string; - href: string; -} - -const { href, title, body } = Astro.props; ---- - - - diff --git a/src/components/LockedContent/domUtils.ts b/src/components/LockedContent/domUtils.ts index edf8f96..2ca7932 100644 --- a/src/components/LockedContent/domUtils.ts +++ b/src/components/LockedContent/domUtils.ts @@ -1,45 +1,45 @@ -import { incrementUnlockCounter } from "./serverUtils"; +import { incrementUnlockCounter } from "./serverUtils" function updateText(elemId: string, text: string) { - const elem = document.getElementById(elemId); - if (elem) elem.innerText = text; - else console.error("Element could not be found!"); + const elem = document.getElementById(elemId) + if (elem) elem.innerText = text + else console.error("Element could not be found!") } function toggleClass(elemId: string, className: string) { - const elem = document.getElementById(elemId); - if (elem) elem.classList.toggle(className); - else console.error("Element could not be found!"); + const elem = document.getElementById(elemId) + if (elem) elem.classList.toggle(className) + else console.error("Element could not be found!") } function removeClasses(elemId: string, ...inputs: string[]) { - const elem = document.getElementById(elemId); - if (elem) elem.classList.remove(...inputs); - else console.error("Element could not be found!"); + const elem = document.getElementById(elemId) + if (elem) elem.classList.remove(...inputs) + else console.error("Element could not be found!") } function addClasses(elemId: string, ...inputs: string[]) { - const elem = document.getElementById(elemId); - if (elem) elem.classList.add(...inputs); - else console.error("Element could not be found!"); + const elem = document.getElementById(elemId) + if (elem) elem.classList.add(...inputs) + else console.error("Element could not be found!") } function removeElement(elemId: string) { - const elem = document.getElementById(elemId); - if (elem) elem.remove(); - else console.error("Element could not be found!"); + const elem = document.getElementById(elemId) + if (elem) elem.remove() + else console.error("Element could not be found!") } function addAttribute(elemId: string, attribute: string, value: string) { - const elem = document.getElementById(elemId); - if (elem) elem.setAttribute(attribute, value); - else console.error("Element could not be found!"); + const elem = document.getElementById(elemId) + if (elem) elem.setAttribute(attribute, value) + else console.error("Element could not be found!") } function revealContent() { - incrementUnlockCounter(document.URL.slice(-12)); - removeClasses("content", "blur-2xl"); - removeElement("unlock-button-container"); + incrementUnlockCounter(document.URL.slice(-12)) + removeClasses("content", "blur-2xl") + removeElement("unlock-button-container") } export { @@ -50,4 +50,4 @@ export { updateText, revealContent, addAttribute, -}; +} diff --git a/src/components/LockedContent/geolocation.ts b/src/components/LockedContent/geolocation.ts index f956477..8040505 100644 --- a/src/components/LockedContent/geolocation.ts +++ b/src/components/LockedContent/geolocation.ts @@ -1,5 +1,4 @@ -import Toastify from "toastify-js"; -import L, { type LatLngTuple } from "leaflet"; +import L, { type LatLngTuple } from "leaflet" import { addAttribute, addClasses, @@ -8,34 +7,36 @@ import { revealContent, toggleClass, updateText, -} from "./domUtils"; -import { mapLocationSuccessCallback } from "@/scripts/initMap"; +} from "./domUtils" +import { mapLocationSuccessCallback } from "@/scripts/initMap" +import { toast } from "@/lib/utils" // Update the elements according to distance remaining function locationSuccessCallback( position: GeolocationPosition, targetPosition: LatLngTuple ) { - const newPosition = position.coords; + removeClasses("current-location-control", "disabled-button") + const newPosition = position.coords // Calculate the distance remaining const distance = calculateDistance( [newPosition.latitude, newPosition.longitude], targetPosition - ); + ) // If user has arrived to destination if (distance < 100) { // Change the description texts - updateText("button-text", "İçeriği Göster"); - updateText("locked-content-description", "İçeriği görmek için butona bas!"); + updateText("button-text", "İçeriği Göster") + updateText("locked-content-description", "İçeriği görmek için butona bas!") // Swap the icon - toggleClass("lock-icon", "hidden"); - toggleClass("unlock-icon", "hidden"); + toggleClass("lock-icon", "hidden") + toggleClass("unlock-icon", "hidden") // Tansform the unlock button - removeClasses("unlock-content-button", "bg-primary", "hover:bg-primary/90"); + removeClasses("unlock-content-button", "bg-primary", "hover:bg-primary/90") addClasses( "unlock-content-button", "bg-indigo-600", @@ -43,71 +44,57 @@ function locationSuccessCallback( "hover:animate-none", "border-2", "border-indigo-800" - ); + ) // Wait for transition to finish then add animation setTimeout(() => { - removeClasses("unlock-content-button", "duration-1000"); - addClasses("unlock-content-button", "animate-pulse"); - }, 800); + removeClasses("unlock-content-button", "duration-1000") + addClasses("unlock-content-button", "animate-pulse") + }, 800) // Reveal image when the unlock button is clicked - const unlockButton = document.getElementById("unlock-content-button"); - unlockButton?.addEventListener("click", revealContent); + const unlockButton = document.getElementById("unlock-content-button") + unlockButton?.addEventListener("click", revealContent) } else { - const distanceText = generateDistanceText(distance); - updateText("locked-content-description", `Kalan mesafe: ${distanceText}`); + const distanceText = generateDistanceText(distance) + updateText("locked-content-description", `Kalan mesafe: ${distanceText}`) } - removeElement("location-permission-button"); + removeElement("location-permission-button") // Update leaflet controls - mapLocationSuccessCallback(position); + mapLocationSuccessCallback(position) } // This callback will be fired on geolocation error function errorCallback(err: GeolocationPositionError) { - let errorMessage; + let errorMessage // Show toast accoring to the error state switch (err.code) { case GeolocationPositionError.PERMISSION_DENIED: errorMessage = - "Konum izni alınamadı, lütfen tarayıcınızın ve cihazınızın gizlilik ayarlarını kontrol edin."; + "Konum izni alınamadı, lütfen tarayıcınızın ve cihazınızın gizlilik ayarlarını kontrol edin." updateText( "locked-content-description", "Konum izleme izni alınamadı. \nİçeriği görüntüleyebilmek için konum bilginiz gerekiyor." - ); - addAttribute("current-location-control", "disabled", "true"); - addClasses("current-location-control", "disabled-button"); - removeElement("location-permission-button"); - break; + ) + addAttribute("current-location-control", "disabled", "true") + addClasses("current-location-control", "disabled-button") + removeElement("location-permission-button") + break case GeolocationPositionError.POSITION_UNAVAILABLE: errorMessage = - "Konumunuz tespit edilemedi, lütfen biraz sonra tekrar deneyiniz."; - break; + "Konumunuz tespit edilemedi, lütfen biraz sonra tekrar deneyiniz." + break case GeolocationPositionError.TIMEOUT: - errorMessage = - "Konum isteği zaman aşımına uğradı, lütfen sayfayı yenileyip tekrar deneyiniz."; - break; + return default: errorMessage = - "Konum izni alınamadı, lütfen tarayıcınızın ve cihazınızın gizlilik ayarlarını kontrol edin."; - break; + "Konum izni alınamadı, lütfen tarayıcınızın ve cihazınızın gizlilik ayarlarını kontrol edin." + break } - Toastify({ - text: errorMessage, - duration: 3000, - gravity: "top", - position: "center", - stopOnFocus: true, - style: { - background: "black", - borderRadius: "6px", - margin: "16px", - }, - onClick: function () {}, - }).showToast(); + toast(errorMessage) } function calculateDistance( @@ -115,24 +102,24 @@ function calculateDistance( targetPosition: L.LatLngTuple ) { // Get target position in latitudes and longitudes - const targetLatLng = L.latLng(targetPosition); + const targetLatLng = L.latLng(targetPosition) // Get current position in latitudes and longitudes - const currentLatLng = L.latLng(currentPosition); + const currentLatLng = L.latLng(currentPosition) // Calculate the distance between target and current position in meters - const betweenMeters = currentLatLng.distanceTo(targetLatLng); + const betweenMeters = currentLatLng.distanceTo(targetLatLng) - return betweenMeters; + return betweenMeters } // Generates a human readable destination text according to // distance remaining function generateDistanceText(distance: number) { if (distance > 1000) { - return `${(distance / 1000).toFixed()} KM`; + return `${(distance / 1000).toFixed()} KM` } else if (distance > 100) { - return `${distance.toFixed(0)} M`; + return `${distance.toFixed(0)} M` } } @@ -141,4 +128,4 @@ export { locationSuccessCallback, calculateDistance, generateDistanceText, -}; +} diff --git a/src/components/LockedContent/serverUtils.ts b/src/components/LockedContent/serverUtils.ts index 2c00837..031dcfd 100644 --- a/src/components/LockedContent/serverUtils.ts +++ b/src/components/LockedContent/serverUtils.ts @@ -4,8 +4,8 @@ function incrementUnlockCounter(id: string | undefined) { if (id) { fetch(`http://localhost:3000/api/location/increment/${id}`, { method: "PATCH", - }); + }) } } -export { incrementUnlockCounter }; +export { incrementUnlockCounter } diff --git a/src/components/ShareButton.tsx b/src/components/ShareButton.tsx index 28ffbe5..3fc9c65 100644 --- a/src/components/ShareButton.tsx +++ b/src/components/ShareButton.tsx @@ -1,37 +1,25 @@ -import { Button } from "./ui/button" +import { toast } from "@/lib/utils"; +import { Button } from "./ui/button"; const ShareButton = () => { const shareLink = async () => { const shareData = { title: "Konulu Konum", text: "Sevdiklerinizi şaşırtın!", - url: document.URL - } + url: document.URL, + }; - await navigator.share(shareData) - } + await navigator.share(shareData); + }; const copyLink = async () => { try { - await navigator.clipboard.writeText(document.URL) - // @ts-ignore - Toastify({ - text: "Konulu konum kopyalandı.", - duration: 3000, - gravity: "top", // `top` or `bottom` - position: "center", // `left`, `center` or `right` - stopOnFocus: true, // Prevents dismissing of toast on hover - style: { - background: "black", - borderRadius: "6px", - margin: "16px" - }, - onClick: function () {} // Callback after click - }).showToast() + await navigator.clipboard.writeText(document.URL); + toast("Konulu konum kopyalandı."); } catch (err: any) { - console.error(err.message) + console.error(err.message); } - } + }; return (
@@ -48,7 +36,7 @@ const ShareButton = () => { ) }
- ) -} + ); +}; -export default ShareButton +export default ShareButton; diff --git a/src/env.d.ts b/src/env.d.ts index 9b73ba6..62cde52 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -5,12 +5,12 @@ export declare global { interface Window { - htmx: any; + htmx: any } namespace astroHTML.JSX { interface HTMLAttributes { - _?: any; + _?: any } } } diff --git a/src/lib/db.ts b/src/lib/db.ts index 9583650..a3beb06 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -1,16 +1,14 @@ -import type { Generated } from "kysely" - export interface Database { contents: ContentTable } export interface ContentTable { - id: Generated + id: string url: string blob_url: string loc: string author: string description: string - created_at: Generated - unlocked_counter: Generated + created_at: string + unlocked_counter: number } diff --git a/src/lib/error.ts b/src/lib/error.ts deleted file mode 100644 index 0896785..0000000 --- a/src/lib/error.ts +++ /dev/null @@ -1,31 +0,0 @@ -export function onLocationError(err: GeolocationPositionError) { - let errorMessage - switch (err.code) { - case 1: - errorMessage = "Konum izni alınamadı, lütfen tarayıcınızın ve cihazınızın gizlilik ayarlarını kontrol edin." - break - case 2: - errorMessage = "Konumunuz tespit edilemedi, lütfen biraz sonra tekrar deneyiniz." - break - case 3: - errorMessage = "Konum isteği zaman aşımına uğradı, lütfen sayfayı yenileyip tekrar deneyiniz." - break - default: - errorMessage = "Konum izni alınamadı, lütfen tarayıcınızın ve cihazınızın gizlilik ayarlarını kontrol edin." - break - } - // @ts-ignore - Toastify({ - text: errorMessage, - duration: 3000, - gravity: "top", // `top` or `bottom` - position: "center", // `left`, `center` or `right` - stopOnFocus: true, // Prevents dismissing of toast on hover - style: { - background: "black", - borderRadius: "6px", - margin: "16px" - }, - onClick: function () {} // Callback after click - }).showToast() -} diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 365058c..4267189 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,6 +1,23 @@ -import { type ClassValue, clsx } from "clsx"; -import { twMerge } from "tailwind-merge"; +import { type ClassValue, clsx } from "clsx" +import { twMerge } from "tailwind-merge" +import Toastify from "toastify-js" export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); + return twMerge(clsx(inputs)) +} + +export function toast(errorMessage: string) { + Toastify({ + text: errorMessage, + duration: 3000, + gravity: "top", + position: "center", + stopOnFocus: true, + style: { + background: "black", + borderRadius: "6px", + margin: "16px", + }, + onClick: function () {}, + }).showToast() } diff --git a/src/pages/[id].astro b/src/pages/[id].astro index 28cc2a1..42a61cf 100644 --- a/src/pages/[id].astro +++ b/src/pages/[id].astro @@ -1,42 +1,41 @@ --- // Styles -import "@/styles/globals.css"; -import "../styles/locked-page.css"; -import "../styles/locked-content.css"; +import "@/styles/globals.css" +import "../styles/locked-page.css" +import "../styles/locked-content.css" // Components -import Layout from "../layouts/Layout.astro"; -import ShareButton from "../components/ShareButton"; +import Layout from "../layouts/Layout.astro" +import ShareButton from "../components/ShareButton" import { Card, CardContent, CardFooter, CardHeader, CardTitle, -} from "@/components/ui/card"; -import { CalendarIcon } from "@radix-ui/react-icons"; -import { Separator } from "@/components/ui/separator"; -import type { ContentTable } from "@/lib/db"; +} from "@/components/ui/card" +import { CalendarIcon } from "@radix-ui/react-icons" +import { Separator } from "@/components/ui/separator" +import type { ContentTable } from "@/lib/db" // Dayjs -import dayjs from "dayjs"; -import relativeTime from "dayjs/plugin/relativeTime"; -import utc from "dayjs/plugin/utc"; +import dayjs from "dayjs" +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(`http://localhost:3000/api/location/${id}`); +const res = await fetch(`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 -dayjs.extend(relativeTime); +dayjs.extend(relativeTime) -dayjs.extend(utc); +dayjs.extend(utc) -// @ts-expect-error Generated is string -const dateFromNow = dayjs.utc(data?.created_at).from(dayjs.utc()); +const dateFromNow = dayjs.utc(data?.created_at).from(dayjs.utc()) --- diff --git a/src/pages/index.astro b/src/pages/index.astro index 0edce8b..b957874 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -10,17 +10,15 @@ import Layout from "../layouts/Layout.astro";
+ class='flex flex-col gap-8' + id='sample-form' + enctype='multipart/form-data'>

+ class='scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl hover:underline'> Konulu Konum

-

+

3 basit adımda fotoğraflarınızı ve videolarınızı yalnızca belirli bir konumda açılacak şekilde kilitleyin: @@ -29,24 +27,23 @@ import Layout from "../layouts/Layout.astro";

+ class='mt-10 scroll-m-20 border-b pb-2 text-2xl font-semibold tracking-tight transition-colors first:mt-0'> 1. Fotoğraf Seçimi

-

+

Aşağıdaki butona basıp galerinizden bir fotoğraf seçin, seçtiğiniz fotoğraf yalnızca belirli bir konumda açılabilecek bir hale getirilecek.

-
+
-

+

Galerinizden bir fotoğraf seçin.

@@ -54,99 +51,94 @@ import Layout from "../layouts/Layout.astro";

+ class='mt-10 scroll-m-20 border-b pb-2 text-2xl font-semibold tracking-tight transition-colors first:mt-0'> 2. Fotoğrafın Açılacağı Konum

-

+

Haritadan bir nokta seçin. Bağlantıyı gönderdiğiniz kişi bu konuma gittiği zaman seçtiğiniz fotoğrafı görebilecek.

+ id='map' + class='w-full h-[450px] rounded mt-4 transition-colors ease-in-out border-2 duration-300'>
-

- Seçilen Konum: +

+ Seçilen Konum:

+ class='text-lg transition ease-in-out duration-700' + id='coordinates'> Henüz konum seçmediniz, konum seçmek için haritadaki bir noktaya basınız.

+ class='text-lg text-muted-foreground mt-2' + id='location-selected-confirmation'>

- +

+ class='mt-10 scroll-m-20 border-b pb-2 text-2xl font-semibold tracking-tight transition-colors first:mt-0'> 3. Fotoğraf Açıklaması

-
-
-