177 lines
5.2 KiB
TypeScript
177 lines
5.2 KiB
TypeScript
"use client"
|
|
|
|
import type React from "react"
|
|
import { useState, useEffect } from "react"
|
|
import { LocationMap } from "./LocationMap"
|
|
import type { LocationData, LocationShare } from "@/lib/location/locationStorage"
|
|
import { LocationStorageService } from "@/lib/location/locationStorage"
|
|
import { useAuth } from "@/context/AuthContext"
|
|
|
|
interface LocationViewerProps {
|
|
shareToken: string
|
|
}
|
|
|
|
export const LocationViewer: React.FC<LocationViewerProps> = ({ shareToken }) => {
|
|
const { fileSystem } = useAuth()
|
|
const [location, setLocation] = useState<LocationData | null>(null)
|
|
const [share, setShare] = useState<LocationShare | null>(null)
|
|
const [loading, setLoading] = useState(true)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
useEffect(() => {
|
|
const loadSharedLocation = async () => {
|
|
if (!fileSystem) {
|
|
setError("File system not available")
|
|
setLoading(false)
|
|
return
|
|
}
|
|
|
|
try {
|
|
const storageService = new LocationStorageService(fileSystem)
|
|
await storageService.initialize()
|
|
|
|
// Get share by token
|
|
const shareData = await storageService.getShareByToken(shareToken)
|
|
if (!shareData) {
|
|
setError("Share not found or expired")
|
|
setLoading(false)
|
|
return
|
|
}
|
|
|
|
// Check if share is expired
|
|
if (shareData.expiresAt && shareData.expiresAt < Date.now()) {
|
|
setError("This share has expired")
|
|
setLoading(false)
|
|
return
|
|
}
|
|
|
|
// Check if max views reached
|
|
if (shareData.maxViews && shareData.viewCount >= shareData.maxViews) {
|
|
setError("This share has reached its maximum view limit")
|
|
setLoading(false)
|
|
return
|
|
}
|
|
|
|
// Get location data
|
|
const locationData = await storageService.getLocation(shareData.locationId)
|
|
if (!locationData) {
|
|
setError("Location data not found")
|
|
setLoading(false)
|
|
return
|
|
}
|
|
|
|
setShare(shareData)
|
|
setLocation(locationData)
|
|
|
|
// Increment view count
|
|
await storageService.incrementShareViews(shareData.id)
|
|
|
|
setLoading(false)
|
|
} catch (err) {
|
|
console.error("Error loading shared location:", err)
|
|
setError("Failed to load shared location")
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
loadSharedLocation()
|
|
}, [shareToken, fileSystem])
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="location-viewer flex items-center justify-center min-h-[400px]">
|
|
<div className="flex flex-col items-center gap-3">
|
|
<div className="spinner" />
|
|
<p className="text-sm text-muted-foreground">Loading shared location...</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<div className="location-viewer flex items-center justify-center min-h-[400px]">
|
|
<div className="text-center max-w-md">
|
|
<div className="text-4xl mb-4">📍</div>
|
|
<h2 className="text-xl font-semibold mb-2">Unable to Load Location</h2>
|
|
<p className="text-sm text-muted-foreground">{error}</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (!location || !share) {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<div className="location-viewer max-w-4xl mx-auto p-6">
|
|
<div className="viewer-header mb-6">
|
|
<h1 className="text-3xl font-bold text-balance">Shared Location</h1>
|
|
<p className="text-sm text-muted-foreground mt-2">Someone has shared their location with you</p>
|
|
</div>
|
|
|
|
<div className="viewer-content space-y-6">
|
|
{/* Map Display */}
|
|
<LocationMap location={location} precision={share.precision} showAccuracy={true} height="500px" />
|
|
|
|
{/* Share Info */}
|
|
<div className="share-info bg-muted/50 rounded-lg p-4 space-y-2">
|
|
<div className="info-row flex justify-between text-sm">
|
|
<span className="text-muted-foreground">Precision Level:</span>
|
|
<span className="font-medium capitalize">{share.precision}</span>
|
|
</div>
|
|
<div className="info-row flex justify-between text-sm">
|
|
<span className="text-muted-foreground">Views:</span>
|
|
<span className="font-medium">
|
|
{share.viewCount} {share.maxViews ? `/ ${share.maxViews}` : ""}
|
|
</span>
|
|
</div>
|
|
{share.expiresAt && (
|
|
<div className="info-row flex justify-between text-sm">
|
|
<span className="text-muted-foreground">Expires:</span>
|
|
<span className="font-medium">{new Date(share.expiresAt).toLocaleString()}</span>
|
|
</div>
|
|
)}
|
|
<div className="info-row flex justify-between text-sm">
|
|
<span className="text-muted-foreground">Shared:</span>
|
|
<span className="font-medium">{new Date(share.createdAt).toLocaleString()}</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Privacy Notice */}
|
|
<div className="privacy-notice bg-primary/5 border border-primary/20 rounded-lg p-4">
|
|
<p className="text-xs text-muted-foreground">
|
|
This location is shared securely and will expire based on the sender's privacy settings. The location data
|
|
is stored in a decentralized filesystem and is only accessible via this unique link.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|