mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-11 17:26:45 +02:00
Add mobile camera capture and reorder media card on new post page
Integrate a new file input for direct camera capture on mobile devices, add a Camera icon button to the UI, and move the Media card to the top of the new post creation form. Replit-Commit-Author: Agent Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/561RCgs
This commit is contained in:
1 parent
3fb57986b4
commit
195f0dcb2d
2 files changed
+115
-66
No files matched your search
@@ -34,10 +34,18 @@ externalPort = 4200
|
|||||||
localPort = 40537
|
localPort = 40537
|
||||||
externalPort = 3000
|
externalPort = 3000
|
||||||
|
|
||||||
|
[[ports]]
|
||||||
|
localPort = 42161
|
||||||
|
externalPort = 6000
|
||||||
|
|
||||||
[[ports]]
|
[[ports]]
|
||||||
localPort = 43245
|
localPort = 43245
|
||||||
externalPort = 3001
|
externalPort = 3001
|
||||||
|
|
||||||
|
[[ports]]
|
||||||
|
localPort = 45355
|
||||||
|
externalPort = 6800
|
||||||
|
|
||||||
[[ports]]
|
[[ports]]
|
||||||
localPort = 46681
|
localPort = 46681
|
||||||
externalPort = 5173
|
externalPort = 5173
|
||||||
|
|||||||
+107
-66
@@ -1,7 +1,7 @@
|
|||||||
import { useState, useCallback } from "react";
|
import { useState, useCallback, useRef } from "react";
|
||||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||||
import { useLocation } from "wouter";
|
import { useLocation } from "wouter";
|
||||||
import { Send, Sparkles, Image as ImageIcon, Calendar, Upload } from "lucide-react";
|
import { Send, Sparkles, Image as ImageIcon, Calendar, Upload, Camera } from "lucide-react";
|
||||||
import { useDropzone } from "react-dropzone";
|
import { useDropzone } from "react-dropzone";
|
||||||
import Sidebar from "@/components/sidebar";
|
import Sidebar from "@/components/sidebar";
|
||||||
import TopBar from "@/components/topbar";
|
import TopBar from "@/components/topbar";
|
||||||
@@ -20,6 +20,7 @@ export default function NewPost() {
|
|||||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||||
const [, navigate] = useLocation();
|
const [, navigate] = useLocation();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
const cameraInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const [productInfo, setProductInfo] = useState('');
|
const [productInfo, setProductInfo] = useState('');
|
||||||
const [selectedMedia, setSelectedMedia] = useState<string | null>(null);
|
const [selectedMedia, setSelectedMedia] = useState<string | null>(null);
|
||||||
@@ -189,6 +190,13 @@ export default function NewPost() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCameraCapture = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
if (file) {
|
||||||
|
uploadMutation.mutate(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-screen overflow-hidden bg-background">
|
<div className="flex h-screen overflow-hidden bg-background">
|
||||||
{sidebarOpen && (
|
{sidebarOpen && (
|
||||||
@@ -218,6 +226,103 @@ export default function NewPost() {
|
|||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 sm:gap-6 lg:gap-8">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 sm:gap-6 lg:gap-8">
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
|
<Card className="rounded-2xl border-border/50 shadow-lg">
|
||||||
|
<CardHeader className="p-6">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<CardTitle>Média</CardTitle>
|
||||||
|
<CardDescription>Sélectionnez ou uploadez une image/vidéo</CardDescription>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button
|
||||||
|
onClick={() => cameraInputRef.current?.click()}
|
||||||
|
disabled={uploadMutation.isPending}
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
className="lg:hidden"
|
||||||
|
data-testid="button-camera-capture"
|
||||||
|
>
|
||||||
|
<Camera className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={open}
|
||||||
|
disabled={uploadMutation.isPending}
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
data-testid="button-upload-new-media"
|
||||||
|
>
|
||||||
|
<Upload className="w-4 h-4 mr-2" />
|
||||||
|
{uploadMutation.isPending ? 'Upload...' : 'Uploader'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<input
|
||||||
|
ref={cameraInputRef}
|
||||||
|
type="file"
|
||||||
|
accept="image/*,video/*"
|
||||||
|
capture="environment"
|
||||||
|
onChange={handleCameraCapture}
|
||||||
|
className="hidden"
|
||||||
|
/>
|
||||||
|
<div {...getRootProps()} className={`${isDragActive ? 'bg-primary/5 border-primary' : ''}`}>
|
||||||
|
<input {...getInputProps()} />
|
||||||
|
{mediaList.length === 0 ? (
|
||||||
|
<div className="text-center py-8 border-2 border-dashed rounded-lg">
|
||||||
|
<ImageIcon className="w-12 h-12 mx-auto text-muted-foreground mb-2" />
|
||||||
|
<p className="text-muted-foreground mb-2">
|
||||||
|
{isDragActive ? "Déposez votre photo ici" : "Aucun média disponible"}
|
||||||
|
</p>
|
||||||
|
<div className="flex gap-2 justify-center">
|
||||||
|
<Button
|
||||||
|
onClick={() => cameraInputRef.current?.click()}
|
||||||
|
disabled={uploadMutation.isPending}
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
className="lg:hidden"
|
||||||
|
data-testid="button-camera-first"
|
||||||
|
>
|
||||||
|
<Camera className="w-4 h-4 mr-2" />
|
||||||
|
Prendre une photo
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={open}
|
||||||
|
disabled={uploadMutation.isPending}
|
||||||
|
size="sm"
|
||||||
|
data-testid="button-upload-first-media"
|
||||||
|
>
|
||||||
|
<Upload className="w-4 h-4 mr-2" />
|
||||||
|
Uploader une photo
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="grid grid-cols-3 gap-2">
|
||||||
|
{mediaList.map((media) => (
|
||||||
|
<button
|
||||||
|
key={media.id}
|
||||||
|
onClick={() => setSelectedMedia(media.id)}
|
||||||
|
className={`relative aspect-square rounded-lg overflow-hidden border-2 transition-all ${
|
||||||
|
selectedMedia === media.id
|
||||||
|
? 'border-primary ring-2 ring-primary'
|
||||||
|
: 'border-transparent hover:border-muted-foreground'
|
||||||
|
}`}
|
||||||
|
data-testid={`button-select-media-${media.id}`}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={media.originalUrl}
|
||||||
|
alt={media.fileName}
|
||||||
|
className="w-full h-full object-cover"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<Card className="rounded-2xl border-border/50 shadow-lg">
|
<Card className="rounded-2xl border-border/50 shadow-lg">
|
||||||
<CardHeader className="p-6">
|
<CardHeader className="p-6">
|
||||||
<CardTitle>Contenu</CardTitle>
|
<CardTitle>Contenu</CardTitle>
|
||||||
@@ -297,70 +402,6 @@ export default function NewPost() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Card className="rounded-2xl border-border/50 shadow-lg">
|
|
||||||
<CardHeader className="p-6">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<CardTitle>Média</CardTitle>
|
|
||||||
<CardDescription>Sélectionnez ou uploadez une image/vidéo</CardDescription>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
onClick={open}
|
|
||||||
disabled={uploadMutation.isPending}
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
data-testid="button-upload-new-media"
|
|
||||||
>
|
|
||||||
<Upload className="w-4 h-4 mr-2" />
|
|
||||||
{uploadMutation.isPending ? 'Upload...' : 'Uploader'}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div {...getRootProps()} className={`${isDragActive ? 'bg-primary/5 border-primary' : ''}`}>
|
|
||||||
<input {...getInputProps()} />
|
|
||||||
{mediaList.length === 0 ? (
|
|
||||||
<div className="text-center py-8 border-2 border-dashed rounded-lg">
|
|
||||||
<ImageIcon className="w-12 h-12 mx-auto text-muted-foreground mb-2" />
|
|
||||||
<p className="text-muted-foreground mb-2">
|
|
||||||
{isDragActive ? "Déposez votre photo ici" : "Aucun média disponible"}
|
|
||||||
</p>
|
|
||||||
<Button
|
|
||||||
onClick={open}
|
|
||||||
disabled={uploadMutation.isPending}
|
|
||||||
size="sm"
|
|
||||||
data-testid="button-upload-first-media"
|
|
||||||
>
|
|
||||||
<Upload className="w-4 h-4 mr-2" />
|
|
||||||
Uploader une photo
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="grid grid-cols-3 gap-2">
|
|
||||||
{mediaList.map((media) => (
|
|
||||||
<button
|
|
||||||
key={media.id}
|
|
||||||
onClick={() => setSelectedMedia(media.id)}
|
|
||||||
className={`relative aspect-square rounded-lg overflow-hidden border-2 transition-all ${
|
|
||||||
selectedMedia === media.id
|
|
||||||
? 'border-primary ring-2 ring-primary'
|
|
||||||
: 'border-transparent hover:border-muted-foreground'
|
|
||||||
}`}
|
|
||||||
data-testid={`button-select-media-${media.id}`}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={media.originalUrl}
|
|
||||||
alt={media.fileName}
|
|
||||||
className="w-full h-full object-cover"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
|
|||||||
Reference in new issue
Block a user