| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- import { useState } from 'react';
- import { Link } from 'react-router-dom';
- import { useAuth } from '@picobaas/client/react';
- import { useImages } from '../hooks/useImages';
- import { PUBLIC_BUCKET } from '../config';
- import ImageUploader from '../components/ImageUploader';
- export default function HomePage() {
- const { isAuthenticated } = useAuth();
- const { uploadImage } = useImages(PUBLIC_BUCKET);
- const [isUploading, setIsUploading] = useState(false);
- const [uploadResult, setUploadResult] = useState<{
- success: boolean;
- url?: string;
- error?: string;
- } | null>(null);
- const handleUpload = async (file: File, expirySeconds: number) => {
- setIsUploading(true);
- setUploadResult(null);
- try {
- const image = await uploadImage(file, expirySeconds);
- if (image) {
- setUploadResult({ success: true, url: image.url });
- }
- } catch (err) {
- setUploadResult({
- success: false,
- error: err instanceof Error ? err.message : 'Upload failed',
- });
- } finally {
- setIsUploading(false);
- }
- };
- const copyToClipboard = async (url: string) => {
- try {
- await navigator.clipboard.writeText(url);
- alert('Link copied to clipboard!');
- } catch {
- // Fallback
- const input = document.createElement('input');
- input.value = url;
- document.body.appendChild(input);
- input.select();
- document.execCommand('copy');
- document.body.removeChild(input);
- alert('Link copied to clipboard!');
- }
- };
- return (
- <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
- {/* Hero section */}
- <div className="text-center mb-12">
- <h1 className="text-4xl sm:text-5xl font-bold text-gray-900 mb-4">
- Share images instantly
- </h1>
- <p className="text-xl text-gray-600 mb-6">
- Upload an image and get a shareable link. No account required.
- </p>
- {!isAuthenticated && (
- <p className="text-sm text-gray-500">
- <Link to="/register" className="text-primary-600 hover:text-primary-700 font-medium">
- Create an account
- </Link>{' '}
- to manage your uploads and access more features.
- </p>
- )}
- </div>
- {/* Upload result */}
- {uploadResult && (
- <div
- className={`mb-8 p-6 rounded-xl ${
- uploadResult.success
- ? 'bg-green-50 border border-green-200'
- : 'bg-red-50 border border-red-200'
- }`}
- >
- {uploadResult.success ? (
- <div className="text-center">
- <div className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-green-100 mb-4">
- <svg className="h-6 w-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
- </svg>
- </div>
- <h3 className="text-lg font-semibold text-green-900 mb-2">
- Image uploaded successfully!
- </h3>
- <div className="flex gap-2 max-w-xl mx-auto">
- <input
- type="text"
- value={uploadResult.url}
- readOnly
- className="input flex-1 bg-white text-sm"
- />
- <button
- onClick={() => uploadResult.url && copyToClipboard(uploadResult.url)}
- className="btn btn-primary whitespace-nowrap"
- >
- Copy Link
- </button>
- </div>
- <div className="mt-4">
- <a
- href={uploadResult.url}
- target="_blank"
- rel="noopener noreferrer"
- className="text-green-700 hover:text-green-800 text-sm font-medium"
- >
- Open image in new tab →
- </a>
- </div>
- <button
- onClick={() => setUploadResult(null)}
- className="mt-4 text-sm text-gray-600 hover:text-gray-800"
- >
- Upload another image
- </button>
- </div>
- ) : (
- <div className="text-center">
- <div className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-red-100 mb-4">
- <svg className="h-6 w-6 text-red-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
- </svg>
- </div>
- <h3 className="text-lg font-semibold text-red-900 mb-2">
- Upload failed
- </h3>
- <p className="text-red-700 mb-4">{uploadResult.error}</p>
- <button
- onClick={() => setUploadResult(null)}
- className="btn btn-secondary"
- >
- Try again
- </button>
- </div>
- )}
- </div>
- )}
- {/* Upload area */}
- {!uploadResult?.success && (
- <div className="card p-8">
- <ImageUploader
- onUpload={handleUpload}
- isUploading={isUploading}
- />
- </div>
- )}
- {/* Features */}
- <div className="mt-16 grid md:grid-cols-3 gap-8">
- <div className="text-center">
- <div className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-primary-100 mb-4">
- <svg className="h-6 w-6 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
- </svg>
- </div>
- <h3 className="text-lg font-semibold text-gray-900 mb-2">Instant Upload</h3>
- <p className="text-gray-600">
- Drop your image and get a link instantly. No sign-up needed.
- </p>
- </div>
- <div className="text-center">
- <div className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-primary-100 mb-4">
- <svg className="h-6 w-6 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
- </svg>
- </div>
- <h3 className="text-lg font-semibold text-gray-900 mb-2">Auto Expiry</h3>
- <p className="text-gray-600">
- Set expiration times to automatically delete images after a period.
- </p>
- </div>
- <div className="text-center">
- <div className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-primary-100 mb-4">
- <svg className="h-6 w-6 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z" />
- </svg>
- </div>
- <h3 className="text-lg font-semibold text-gray-900 mb-2">Easy Sharing</h3>
- <p className="text-gray-600">
- Get shareable links for your images with just one click.
- </p>
- </div>
- </div>
- </div>
- );
- }
|