feat: watch position

This commit is contained in:
log101 2024-01-18 00:50:12 +03:00
parent cdcf08898f
commit 730f5e9715
9 changed files with 270 additions and 109 deletions

61
package-lock.json generated
View File

@ -12,8 +12,8 @@
"@astrojs/react": "^3.0.9", "@astrojs/react": "^3.0.9",
"@astrojs/tailwind": "^5.1.0", "@astrojs/tailwind": "^5.1.0",
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-slot": "^1.0.2",
"@types/leaflet": "^1.9.8",
"@types/react": "^18.2.47", "@types/react": "^18.2.47",
"@types/react-dom": "^18.2.18", "@types/react-dom": "^18.2.18",
"astro": "^4.1.2", "astro": "^4.1.2",
@ -1102,6 +1102,52 @@
"react": "^16.x || ^17.x || ^18.x" "react": "^16.x || ^17.x || ^18.x"
} }
}, },
"node_modules/@radix-ui/react-primitive": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz",
"integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-slot": "1.0.2"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-separator": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.0.3.tgz",
"integrity": "sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-primitive": "1.0.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-slot": { "node_modules/@radix-ui/react-slot": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
@ -1326,11 +1372,6 @@
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
}, },
"node_modules/@types/geojson": {
"version": "7946.0.13",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.13.tgz",
"integrity": "sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ=="
},
"node_modules/@types/google.maps": { "node_modules/@types/google.maps": {
"version": "3.54.10", "version": "3.54.10",
"resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.54.10.tgz", "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.54.10.tgz",
@ -1345,14 +1386,6 @@
"@types/unist": "*" "@types/unist": "*"
} }
}, },
"node_modules/@types/leaflet": {
"version": "1.9.8",
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.8.tgz",
"integrity": "sha512-EXdsL4EhoUtGm2GC2ZYtXn+Fzc6pluVgagvo2VC1RHWToLGlTRwVYoDpqS/7QXa01rmDyBjJk3Catpf60VMkwg==",
"dependencies": {
"@types/geojson": "*"
}
},
"node_modules/@types/mdast": { "node_modules/@types/mdast": {
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz",

View File

@ -14,8 +14,8 @@
"@astrojs/react": "^3.0.9", "@astrojs/react": "^3.0.9",
"@astrojs/tailwind": "^5.1.0", "@astrojs/tailwind": "^5.1.0",
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-slot": "^1.0.2",
"@types/leaflet": "^1.9.8",
"@types/react": "^18.2.47", "@types/react": "^18.2.47",
"@types/react-dom": "^18.2.18", "@types/react-dom": "^18.2.18",
"astro": "^4.1.2", "astro": "^4.1.2",

View File

@ -1,81 +1,94 @@
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card"
import distance from "@/utils/distance"; import distance from "@/utils/distance"
import { LockClosedIcon, LockOpen1Icon } from "@radix-ui/react-icons"; 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"
const LocationButton = () => { const LocationButton = () => {
const [isLocked, setIsLocked] = useState(true); const [atTarget, setAtTarget] = useState(false)
const [contentVisible, setContentVisible] = useState(false)
const [hasPermission, setHasPermission] = useState(false)
const startWatchingLocation = () => {
setHasPermission(true)
navigator.geolocation.watchPosition(
(position: GeolocationPosition) => {
const pos = {
lat: position.coords.latitude,
lng: position.coords.longitude
}
const totalDistanceInKM = distance(pos.lat, pos.lng, pos.lat, pos.lng).toFixed(0)
if (totalDistanceInKM === "0") {
setAtTarget(true)
}
},
() => null,
{ enableHighAccuracy: true, maximumAge: 30000, timeout: 27000 }
)
}
useEffect(() => { useEffect(() => {
setInterval( navigator.permissions.query({ name: "geolocation" }).then(permissionStatus => {
() => if (permissionStatus.state === "granted") {
navigator.geolocation.getCurrentPosition( setHasPermission(true)
(position: GeolocationPosition) => { startWatchingLocation()
const pos = { }
lat: position.coords.latitude, })
lng: position.coords.longitude, }, [])
};
const totalDistanceInKM = distance( if (contentVisible) {
pos.lat, return <div className='module-unlocked w-full h-[450px] p-4'></div>
pos.lng,
pos.lat,
pos.lng
).toFixed(0);
if (totalDistanceInKM === "0") {
setIsLocked(false);
}
}
),
3000
);
}, []);
if (isLocked) {
return (
<div className="module w-full h-[450px] p-4">
<div className="module-inside flex flex-col justify-center items-center gap-2">
<div>
<Button size={"lg"}>
<LockClosedIcon className="mr-2 h-4 w-4" /> İçerik Kilitli
</Button>
</div>
<Card className="p-2">
<CardContent className="pb-0 text-center">
İçeriği görmek için konuma gitmelisin!
</CardContent>
</Card>
</div>
</div>
);
} else { } else {
return ( return (
<div className="module w-full h-[450px] p-4"> <div className='module w-full h-[450px] p-4'>
<div className="module-inside flex flex-col justify-center items-center gap-2"> {atTarget ? (
<div> <div className='module-inside flex flex-col justify-center items-center gap-2'>
<Button size={"lg"} asChild> <div>
<a href="/unlocked"> <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ı
</a> </Button>
</Button> </div>
<Card className='p-2'>
<CardContent className='pb-0 text-center'>İçeriği görmek için butona bas!</CardContent>
</Card>
</div> </div>
) : (
<div className='module-inside flex flex-col justify-center items-center gap-4'>
<Button size={"lg"}>
<LockClosedIcon className='mr-2 h-4 w-4' /> İçerik Kilitli
</Button>
<Card className="p-2"> <Card className='p-2'>
<CardContent className="pb-0 text-center"> {hasPermission ? (
İçeriği görmek için butona bas! <CardContent className='pb-0 text-center'>İçeriği görmek için konuma gitmelisin!</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'
onClick={() => startWatchingLocation()}
>
Konum İzni Ver
</Button>
</div>
)}
</Card>
</div>
)}
</div> </div>
); )
} }
}; }
export default LocationButton; export default LocationButton

View File

@ -0,0 +1,29 @@
import * as React from "react"
import * as SeparatorPrimitive from "@radix-ui/react-separator"
import { cn } from "@/lib/utils"
const Separator = React.forwardRef<
React.ElementRef<typeof SeparatorPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
>(
(
{ className, orientation = "horizontal", decorative = true, ...props },
ref
) => (
<SeparatorPrimitive.Root
ref={ref}
decorative={decorative}
orientation={orientation}
className={cn(
"shrink-0 bg-border",
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
className
)}
{...props}
/>
)
)
Separator.displayName = SeparatorPrimitive.Root.displayName
export { Separator }

View File

@ -1,5 +1,5 @@
--- ---
const googleSdkKey = import.meta.env.MAPS_SDK_KEY;
--- ---
<!doctype html> <!doctype html>
@ -23,7 +23,7 @@ const googleSdkKey = import.meta.env.MAPS_SDK_KEY;
<meta name="generator" content={Astro.generator} /> <meta name="generator" content={Astro.generator} />
<title>Konulu Konum</title> <title>Konulu Konum</title>
</head> </head>
<body class="bg-purple-50 container"> <body class="container my-8">
<slot /> <slot />
</body> </body>
</html> </html>

View File

@ -13,17 +13,36 @@ import {
import { CalendarIcon } from '@radix-ui/react-icons'; import { CalendarIcon } from '@radix-ui/react-icons';
import '../styles/locked-page.css'; import '../styles/locked-page.css';
import LocationButton from '@/components/LockedContent'; import LockedContent from '@/components/LockedContent';
import { Separator } from '@/components/ui/separator';
--- ---
<Layout> <Layout>
<main class="flex flex-col gap-3 my-4 items-center"> <main class="flex flex-col gap-4">
<div>
<h1
class="scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl"
>
Konulu Konum
</h1>
<p class="leading-7 [&:not(:first-child)]:mt-6">
Arkadaşınız size bir fotoğraf ve bir not bıraktı. Fakat fotoğrafı
görebilmeniz için aşağıdaki konuma gitmeniz gerekiyor. Konuma
yaklaştığınızda butona basıp fotoğrafı görüntüleyebilirsiniz.
</p>
</div>
<Separator />
<Card className="w-full"> <Card className="w-full">
<CardHeader> <CardHeader>
<CardTitle>Abdurrahman</CardTitle> <CardTitle>Abdurrahman</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<p>Aşağıdaki konuma gitmen gerekiyor.</p> <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 />
@ -31,11 +50,16 @@ import LocationButton from '@/components/LockedContent';
</CardFooter> </CardFooter>
</Card> </Card>
<div id="map" class="w-full h-[450px]"></div> <LockedContent client:load />
<LocationButton client:load /> <div id="map" class="w-full h-[450px] rounded"></div>
<Button className="w-full">Paylaş</Button>
<div class="flex justify-center">
<p class="text-muted-foreground">
Fotoğrafın kilidi şu ana dek <b>2</b> kez açıldı
</p>
</div>
</main> </main>
<Button className="w-full">Paylaş</Button>
<script src="../scripts/initMap.js"></script> <script src="../scripts/initMap.js"></script>
</Layout> </Layout>

View File

@ -15,6 +15,13 @@ var targetLocationIcon = L.icon({
L.marker(TARGET_LOCATION, { icon: targetLocationIcon }).addTo(map); L.marker(TARGET_LOCATION, { icon: targetLocationIcon }).addTo(map);
var circle = L.circle(TARGET_LOCATION, {
color: 'blue',
fillColor: '#30f',
fillOpacity: 0.2,
radius: 50
}).addTo(map);
var currentLocationIcon = L.icon({ var currentLocationIcon = L.icon({
iconUrl: 'blue-dot.png', iconUrl: 'blue-dot.png',
iconSize: [32, 32], iconSize: [32, 32],
@ -92,3 +99,39 @@ L.control.targetLocation = function (opts) {
L.control.currentLocation({ position: 'bottomleft' }).addTo(map); L.control.currentLocation({ position: 'bottomleft' }).addTo(map);
L.control.targetLocation({ position: 'bottomleft' }).addTo(map); L.control.targetLocation({ position: 'bottomleft' }).addTo(map);
navigator.permissions
.query({ name: "geolocation" })
.then((permissionStatus) => {
if (permissionStatus === 'granted') {
navigator.geolocation.watchPosition(
(position) => {
const pos = {
lat: position.coords.latitude,
lng: position.coords.longitude
}
console.log('The map location is' + pos)
},
() => null,
{ enableHighAccuracy: true, maximumAge: 60000, timeout: 57000 }
)
} else {
permissionStatus.onchange = () => {
if (permissionStatus.state === 'granted') {
navigator.geolocation.watchPosition(
(position) => {
const pos = {
lat: position.coords.latitude,
lng: position.coords.longitude
}
console.log('The map location is' + pos)
},
() => null,
{ enableHighAccuracy: true, maximumAge: 60000, timeout: 57000 }
)
}
};
}
});

View File

@ -1,7 +1,7 @@
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
@layer base { @layer base {
:root { :root {
--background: 0 0% 100%; --background: 0 0% 100%;
@ -9,63 +9,63 @@
--card: 0 0% 100%; --card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%; --card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%; --popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%; --popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%; --primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%; --primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%; --secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%; --secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%; --muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%; --muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%; --accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%; --accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%; --destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%; --destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%; --border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%; --input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%; --ring: 222.2 84% 4.9%;
--radius: 0.5rem; --radius: 0.5rem;
} }
.dark { .dark {
--background: 222.2 84% 4.9%; --background: 222.2 84% 4.9%;
--foreground: 210 40% 98%; --foreground: 210 40% 98%;
--card: 222.2 84% 4.9%; --card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%; --card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%; --popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%; --popover-foreground: 210 40% 98%;
--primary: 210 40% 98%; --primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%; --primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%; --secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%; --secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%; --muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%; --muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%; --accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%; --accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%; --destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%; --destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%; --border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%; --input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%; --ring: 212.7 26.8% 83.9%;
} }
} }
@layer base { @layer base {
* { * {
@apply border-border; @apply border-border;
@ -73,4 +73,4 @@
body { body {
@apply bg-background text-foreground; @apply bg-background text-foreground;
} }
} }

View File

@ -5,6 +5,13 @@
overflow: hidden; overflow: hidden;
} }
.module-unlocked {
display: grid;
place-items: center;
position: relative;
overflow: hidden;
}
.module::before { .module::before {
content: ""; content: "";
top: 0; top: 0;
@ -18,6 +25,18 @@
filter: blur(20px); filter: blur(20px);
} }
.module-unlocked::before {
content: "";
top: 0;
left: 0;
width: 100%;
height: 100%;
position: absolute;
background-image: url("/sample-selfie.webp");
background-position: center;
background-repeat: no-repeat;
}
.module-inside { .module-inside {
position: relative; position: relative;
} }