Add a global floating post button and reusable composer modal

Hop-State: A_06FPA0SKS2W3S1CARQHD1C8
Hop-Proposal: R_06FPA0RRM7YQZ4XK97CBTPR
Hop-Task: T_06FP9Y3D8DT3NEWS9F7PM00
Hop-Attempt: AT_06FP9Y3D8DQE7JTQSMK0DDG
This commit is contained in:
2026-07-15 02:02:16 -07:00
committed by Hop
parent 848f6f93e0
commit 007ca453d2
4 changed files with 272 additions and 9 deletions
+23 -9
View File
@@ -17,14 +17,16 @@ interface MediaAttachment extends Attachment {
}
interface ComposeProps {
onPost: (content: string, mediaIds: string[], linkPreview?: any, replyToId?: string, isNsfw?: boolean) => void;
onPost: (content: string, mediaIds: string[], linkPreview?: any, replyToId?: string, isNsfw?: boolean) => void | boolean | Promise<void | boolean>;
onPosted?: () => void;
replyingTo?: Post | null;
onCancelReply?: () => void;
placeholder?: string;
isReply?: boolean;
autoFocus?: boolean;
}
export function Compose({ onPost, replyingTo, onCancelReply, placeholder = "What's happening?", isReply }: ComposeProps) {
export function Compose({ onPost, onPosted, replyingTo, onCancelReply, placeholder = "What's happening?", isReply, autoFocus = false }: ComposeProps) {
const { isIdentityUnlocked } = useAuth();
const replyToHandle = replyingTo ? useFormattedHandle(replyingTo.author.handle) : '';
const [content, setContent] = useState('');
@@ -116,13 +118,24 @@ export function Compose({ onPost, replyingTo, onCancelReply, placeholder = "What
}
setIsPosting(true);
await onPost(content, attachments.map((item) => item.id).filter(Boolean), linkPreview, replyingTo?.id, isNsfw);
setContent('');
setAttachments([]);
setLinkPreview(null);
setLastDetectedUrl(null);
setIsNsfw(false);
setIsPosting(false);
try {
const posted = await onPost(content, attachments.map((item) => item.id).filter(Boolean), linkPreview, replyingTo?.id, isNsfw);
if (posted === false) {
setIsPosting(false);
return;
}
setContent('');
setAttachments([]);
setLinkPreview(null);
setLastDetectedUrl(null);
setIsNsfw(false);
setIsPosting(false);
onPosted?.();
} catch (error) {
setIsPosting(false);
throw error;
}
};
const uploadMediaFiles = async (files: File[]) => {
@@ -228,6 +241,7 @@ export function Compose({ onPost, replyingTo, onCancelReply, placeholder = "What
value={content}
onChange={(e) => setContent(e.target.value)}
maxLength={maxLength + 50} // Allow some overflow for better UX
autoFocus={autoFocus}
/>
{attachments.length > 0 && (
<div className="compose-media-grid">