Skip to content

[Feat]: file attachments support for chat component #1893

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: feat/assistant
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
improve attachment logic
  • Loading branch information
iamfaran committed Jul 24, 2025
commit a2160b99fb68178220a432cccd52b8d96fb60804
Original file line number Diff line number Diff line change
Expand Up @@ -144,73 +144,76 @@ const ScreenReaderOnly = styled.span`
overflow: hidden;
`;

// ============================================================================
// UTILITY HOOKS
// ============================================================================


const useFileSrc = (file: File | undefined) => {
const [src, setSrc] = useState<string | undefined>();
const lastFileRef = useRef<File>();

useEffect(() => {
if (!file || file === lastFileRef.current) return;

const objectUrl = URL.createObjectURL(file);
setSrc(objectUrl);
lastFileRef.current = file;

return () => {
URL.revokeObjectURL(objectUrl);
};
}, [file]);

return src;
};



const useAttachmentSrc = () => {
// Listen only to image-type attachments
const attachment = useAttachment(
useCallback((a: any) => {
if (a.type !== "image") return undefined;
return a;
}, [])
useCallback((a: any) => (a.type === "image" ? a : undefined), [])
);

const [src, setSrc] = useState<string | undefined>();
const lastAttachmentRef = useRef<any>();

useEffect(() => {
if (!attachment || attachment === lastAttachmentRef.current) return;
// Keep track of the last generated object URL so that we can revoke it
const objectUrlRef = useRef<string | undefined>();
const lastAttachmentIdRef = useRef<string | undefined>();

// Handle new/pending attachments with File objects
if (attachment.file && attachment.file instanceof File) {
const objectUrl = URL.createObjectURL(attachment.file);
setSrc(objectUrl);
lastAttachmentRef.current = attachment;
useEffect(() => {
// If the same attachment is rendered again, do nothing
if (!attachment || attachment.id === lastAttachmentIdRef.current) return;

// Clean up any previous object URL
if (objectUrlRef.current) {
try {
URL.revokeObjectURL(objectUrlRef.current);
} catch {
/* ignore */
}
objectUrlRef.current = undefined;
}

return () => {
URL.revokeObjectURL(objectUrl);
};
// ------------------------------------------------------------------
// 1. New (local) File object – generate a temporary ObjectURL
// ------------------------------------------------------------------
if (attachment.file instanceof File) {
const url = URL.createObjectURL(attachment.file);
objectUrlRef.current = url;
setSrc(url);
lastAttachmentIdRef.current = attachment.id;
return;
}

// Handle saved attachments with base64 data
const imageContent = attachment.content?.find((c: any) => c.type === "image");
if (imageContent?.image) {
setSrc(imageContent.image);
lastAttachmentRef.current = attachment;
// ------------------------------------------------------------------
// 2. Restored attachment coming from storage – use stored base64 image
// ------------------------------------------------------------------
const imgPart = attachment.content?.find((p: any) => p.type === "image");
if (imgPart?.image) {
setSrc(imgPart.image as string);
lastAttachmentIdRef.current = attachment.id;
return;
}

// If no valid source found, clear the src
// ------------------------------------------------------------------
// 3. No usable preview – clear src
// ------------------------------------------------------------------
setSrc(undefined);
lastAttachmentRef.current = attachment;
lastAttachmentIdRef.current = attachment.id;
}, [attachment]);

/* Cleanup when the component using this hook unmounts */
useEffect(() => {
return () => {
if (objectUrlRef.current) {
try {
URL.revokeObjectURL(objectUrlRef.current);
} catch {
/* ignore */
}
}
};
}, []);

return src;
};

// ============================================================================
// ATTACHMENT COMPONENTS
// ============================================================================
Expand Down
Loading