网页版支持粘贴图片发送、聊天图片保存到本地
老板反馈:"没有复制粘贴图片功能,只能保存在电脑里面发,图片也没有保存功能" - 聊天输入框新增粘贴图片检测:剪贴板里有图片(截图工具/复制图片)时直接走发图逻辑, 不再要求先存到电脑里再用文件选择器上传;普通文本粘贴不受影响 - "查看大图"预览里新增"保存图片"下载按钮,取消息自带的原始文件名, 取不到时按mime类型生成默认文件名 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
9abae5030b
commit
3d48abdf73
@@ -10,6 +10,24 @@ interface ImageData {
|
||||
mime?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
/** Drafty.insertImage's own `filename` field -- present on every image this WEB client sent
|
||||
* (see sendImage in lib/tinode/attachments.ts). */
|
||||
filename?: string;
|
||||
/** mobileAttachmentContentOf's `name` -- the equivalent field on an image sent from the iOS/Android
|
||||
* app, which flows through a completely different (non-Drafty) flat shape than the web client's
|
||||
* own `IM` entity above; MessageBubble passes that content straight through as this same ImageData
|
||||
* shape, so both need a name field here even though the two clients don't call it the same thing. */
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/** Best-effort filename for the "保存图片" download below -- prefers whatever name the sender's own
|
||||
* client attached (see ImageData's own field comments), falling back to a generic name derived from
|
||||
* the mime type for the inline-base64/mobile-relay cases where neither field is set. */
|
||||
function imageDownloadName(data: ImageData): string {
|
||||
if (data.filename) return data.filename;
|
||||
if (data.name) return data.name;
|
||||
const ext = data.mime?.split('/')[1]?.replace('jpeg', 'jpg') || 'jpg';
|
||||
return `image.${ext}`;
|
||||
}
|
||||
|
||||
const LIGHTBOX_ZOOM_MIN = 1;
|
||||
@@ -22,7 +40,7 @@ const LIGHTBOX_ZOOM_STEP = 0.5;
|
||||
* Zoom is CSS transform scale/translate on the `<img>` itself, not a native browser zoom -- wheel
|
||||
* and the +/- buttons zoom around the center; dragging only does anything once zoomed in, otherwise
|
||||
* the image has nowhere to pan to and a drag gesture would just fight the backdrop's click-to-close. */
|
||||
function ImageLightbox({ src, onClose }: { src: string; onClose: () => void }) {
|
||||
function ImageLightbox({ src, filename, onClose }: { src: string; filename: string; onClose: () => void }) {
|
||||
const [zoom, setZoom] = useState(LIGHTBOX_ZOOM_MIN);
|
||||
const [pan, setPan] = useState({ x: 0, y: 0 });
|
||||
const imgRef = useRef<HTMLImageElement>(null);
|
||||
@@ -92,6 +110,20 @@ function ImageLightbox({ src, onClose }: { src: string; onClose: () => void }) {
|
||||
>
|
||||
<Icon name="zoom_in" className="text-[20px]" />
|
||||
</button>
|
||||
{/* Real anchor + `download`, same technique FileAttachment already relies on below -- works
|
||||
for both inline `data:` URIs (small images) and the `blob:` URL useAuthedBlobUrl fetches
|
||||
for larger ones, triggering a normal save-to-disk with no extra round trip. User report
|
||||
(老板, 2026-09-29): "图片也没有保存功能" -- right-click "另存为" technically already works on
|
||||
an `<img>`, but wasn't discoverable enough; this puts an explicit, visible affordance here,
|
||||
same place every mainstream IM's own "查看大图" view puts it. */}
|
||||
<a
|
||||
href={src}
|
||||
download={filename}
|
||||
aria-label="保存图片"
|
||||
className="grid h-10 w-10 place-items-center rounded-full bg-white/10 text-white"
|
||||
>
|
||||
<Icon name="download" className="text-[20px]" />
|
||||
</a>
|
||||
<button type="button" onClick={onClose} aria-label="关闭" className="grid h-10 w-10 place-items-center rounded-full bg-white/10 text-white">
|
||||
<Icon name="close" className="text-[20px]" />
|
||||
</button>
|
||||
@@ -144,7 +176,7 @@ export function ImageAttachment({ data, locked }: { data: ImageData; locked?: bo
|
||||
<button type="button" onClick={() => setViewing(true)} className="block">
|
||||
{thumb}
|
||||
</button>
|
||||
{viewing ? <ImageLightbox src={src} onClose={() => setViewing(false)} /> : null}
|
||||
{viewing ? <ImageLightbox src={src} filename={imageDownloadName(data)} onClose={() => setViewing(false)} /> : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user