@@ -191,19 +217,45 @@ export function RecordingOverlay({
)}
{isReviewing && (
-
-
-
- Enter
- {" "}
- next
-
-
-
- Q
- {" "}
- cancel
-
+
+ {attachments.length > 0 && (
+
+ )}
+
+
+
+ Enter
+ {" "}
+ next
+
+
+
+ Q
+ {" "}
+ cancel
+
+
+
+
+
+
)}
diff --git a/js/src/features/compose/text-compose-step.tsx b/js/src/features/compose/text-compose-step.tsx
index a950216..b4f1be8 100644
--- a/js/src/features/compose/text-compose-step.tsx
+++ b/js/src/features/compose/text-compose-step.tsx
@@ -1,16 +1,26 @@
import { useEffect, useRef, useCallback, useState } from "react";
+import { Paperclip } from "lucide-react";
import { cn } from "@/lib/utils";
-import { useFirstLinkMetadata } from "@/hooks/use-link-metadata";
-import {
- LinkPreviewCard,
- LinkPreviewCardSkeleton,
-} from "@/components/link-preview-card";
+import { useAllLinkMetadata } from "@/hooks/use-link-metadata";
+import { AttachmentStrip } from "@/features/compose/attachment-strip";
+import type { PendingAttachment } from "@/features/compose/attachment-strip";
+import { Button } from "@/components/ui/button";
interface TextComposeStepProps {
textContent: string;
onTextChange: (text: string) => void;
onAdvance: () => void;
onCancel: () => void;
+ attachments: PendingAttachment[];
+ onRemoveAttachment: (id: string) => void;
+ onAddFiles: () => void;
+ isDragging: boolean;
+ dropZoneProps: {
+ onDragOver: (e: React.DragEvent) => void;
+ onDragEnter: (e: React.DragEvent) => void;
+ onDragLeave: (e: React.DragEvent) => void;
+ onDrop: (e: React.DragEvent) => void;
+ };
}
const IMMERSIVE_CHAR_LIMIT = 120;
@@ -26,6 +36,11 @@ export function TextComposeStep({
onTextChange,
onAdvance,
onCancel,
+ attachments,
+ onRemoveAttachment,
+ onAddFiles,
+ isDragging,
+ dropZoneProps,
}: TextComposeStepProps) {
const textareaRef = useRef
(null);
@@ -35,7 +50,7 @@ export function TextComposeStep({
const t = setTimeout(() => setDebouncedText(textContent), 500);
return () => clearTimeout(t);
}, [textContent]);
- const { data: metadata, isLoading, url: firstUrl } = useFirstLinkMetadata(debouncedText);
+ const linkPreviews = useAllLinkMetadata(debouncedText);
useEffect(() => {
textareaRef.current?.focus();
@@ -54,14 +69,16 @@ export function TextComposeStep({
[onCancel, onAdvance, textContent],
);
- const immersive = textContent.length < IMMERSIVE_CHAR_LIMIT;
- const showPreview = firstUrl && (isLoading || metadata);
+ const hasEnrichments = attachments.length > 0 || linkPreviews.length > 0;
+ const immersive = textContent.length < IMMERSIVE_CHAR_LIMIT && !hasEnrichments;
- const linkPreview = (
- <>
- {firstUrl && isLoading && }
- {firstUrl && metadata && }
- >
+ const strip = (
+
);
const keyboardHints = (
@@ -78,19 +95,34 @@ export function TextComposeStep({
{" "}
next
+
+
+
);
if (immersive) {
const style = getImmersiveTextStyle(textContent.length);
return (
-