Highly ambitious ATProtocol AppView service and sdks
1import { useState } from "react";
2import { Avatar } from "./Avatar.tsx";
3
4interface AvatarInputProps {
5 currentAvatarUrl?: string;
6 onChange: (file: File | null) => void;
7}
8
9export function AvatarInput(
10 { currentAvatarUrl, onChange }: AvatarInputProps,
11) {
12 const [previewUrl, setPreviewUrl] = useState<string | null>(null);
13
14 const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
15 const file = e.target.files?.[0];
16 if (file) {
17 // Create preview URL
18 const objectUrl = URL.createObjectURL(file);
19 setPreviewUrl(objectUrl);
20 onChange(file);
21
22 // Clean up previous preview URL
23 return () => {
24 if (previewUrl) {
25 URL.revokeObjectURL(previewUrl);
26 }
27 };
28 } else {
29 setPreviewUrl(null);
30 onChange(null);
31 }
32 };
33
34 const displayUrl = previewUrl || currentAvatarUrl;
35
36 return (
37 <div>
38 <label
39 htmlFor="avatar"
40 className="block text-sm font-medium text-zinc-400 mb-2"
41 >
42 Avatar
43 </label>
44 <label htmlFor="avatar" className="cursor-pointer inline-block">
45 <div className="border rounded-full border-zinc-700 w-16 h-16 mb-2 relative hover:border-zinc-600 transition-colors">
46 {/* Camera icon overlay */}
47 <div className="absolute bottom-0 right-0 bg-zinc-700 rounded-full w-5 h-5 flex items-center justify-center z-10">
48 <svg
49 className="w-3 h-3 text-white"
50 fill="currentColor"
51 viewBox="0 0 20 20"
52 >
53 <path
54 fillRule="evenodd"
55 d="M4 5a2 2 0 00-2 2v8a2 2 0 002 2h12a2 2 0 002-2V7a2 2 0 00-2-2h-1.586a1 1 0 01-.707-.293l-1.121-1.121A2 2 0 0011.172 3H8.828a2 2 0 00-1.414.586L6.293 4.707A1 1 0 015.586 5H4zm6 9a3 3 0 100-6 3 3 0 000 6z"
56 clipRule="evenodd"
57 />
58 </svg>
59 </div>
60
61 {/* Avatar preview */}
62 <div className="w-full h-full rounded-full overflow-hidden">
63 {displayUrl
64 ? (
65 <img
66 src={displayUrl}
67 alt="Avatar preview"
68 className="w-full h-full object-cover"
69 />
70 )
71 : <Avatar alt="Default avatar" size="lg" />}
72 </div>
73 </div>
74 </label>
75 <input
76 type="file"
77 id="avatar"
78 name="avatar"
79 accept="image/*"
80 className="hidden"
81 onChange={handleFileChange}
82 />
83 </div>
84 );
85}