feat: click to preview in widget

This commit is contained in:
Tristan Yang
2023-07-24 19:22:23 +08:00
parent 4328dc11fb
commit ccb5e93172
7 changed files with 155 additions and 29 deletions
+5
View File
@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#667085" xmlns="http://www.w3.org/2000/svg">
<path
d="M5 21H19C20.1046 21 21 20.1046 21 19V5C21 3.89543 20.1046 3 19 3H5C3.89543 3 3 3.89543 3 5V19C3 20.1046 3.89543 21 5 21ZM5 21L16 10L21 15M10 8.5C10 9.32843 9.32843 10 8.5 10C7.67157 10 7 9.32843 7 8.5C7 7.67157 7.67157 7 8.5 7C9.32843 7 10 7.67157 10 8.5Z"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 472 B

+30
View File
@@ -0,0 +1,30 @@
import { useState } from "react";
import IconClose from "@/assets/icons/close.circle.svg";
type Props = {
thumbnail?: string;
content: string;
};
const Image = ({ thumbnail, content }: Props) => {
const [originalVisible, setOriginalVisible] = useState(false);
const toggleVisible = () => {
setOriginalVisible((prev) => !prev);
};
return (
<>
{originalVisible && (
<div className="fixed top-0 left-0 w-full h-full bg-black/50 flex items-center justify-center">
<div className="relative">
<img className="max-w-full" src={content || thumbnail} />
<button className="absolute -top-2 -right-2" onClick={toggleVisible}>
<IconClose className="w-5 h-5 dark:fill-gray-300" />
</button>
</div>
</div>
)}
<img onClick={toggleVisible} className="max-w-xs cursor-pointer" src={thumbnail || content} />
</>
);
};
export default Image;
+14 -3
View File
@@ -5,6 +5,8 @@ import localizedFormat from "dayjs/plugin/localizedFormat";
import { useAppSelector } from "../../../app/store";
import Text from "./Text";
import Image from "./Image";
import { ContentType } from "@/types/message";
dayjs.extend(localizedFormat);
@@ -12,7 +14,8 @@ export interface IWidgetMessage {
mid: number;
uid: number;
host?: boolean;
type?: "text";
type?: ContentType;
thumbnail?: string;
content: string;
create_time: number;
sending: boolean;
@@ -30,12 +33,20 @@ const Time = ({ time }: { time: number }) => {
};
const Index = (props: IWidgetMessage) => {
const { logo } = useAppSelector((store) => store.server);
const { host = false, type = "text", content, uid, create_time, sending } = props;
const { host = false, type, content, thumbnail = "", uid, create_time, sending } = props;
let contentContainer = null;
console.log("render message", type, content);
switch (type) {
case "text":
case "text/plain":
contentContainer = <Text sending={sending} content={content} host={host} uid={uid} />;
break;
case "vocechat/file":
{
console.log("image file", content);
contentContainer = <Image thumbnail={thumbnail} content={content} />;
}
break;
default:
break;
+12 -4
View File
@@ -53,10 +53,17 @@ const MessageFeed = ({ hostId }: Props) => {
{mids.map((mid) => {
const currMsg = messageMap[mid];
if (!currMsg) return null;
const lastMsg = messageMap[mids[mids.length - 1]];
console.log("lll", lastMsg);
// const lastMsg = messageMap[mids[mids.length - 1]];
const { content, created_at = 0, from_uid = 0, sending = false } = currMsg;
const {
content_type,
thumbnail = "",
content,
created_at = 0,
from_uid = 0,
sending = false
} = currMsg;
console.log("lll", content, content_type);
return (
<Message
uid={from_uid}
@@ -64,8 +71,9 @@ const MessageFeed = ({ hostId }: Props) => {
sending={sending}
key={mid}
mid={mid}
type="text"
type={content_type}
content={content as string}
thumbnail={thumbnail}
create_time={created_at}
/>
);
+66 -8
View File
@@ -1,10 +1,14 @@
import { memo, useRef, useState } from "react";
import { ChangeEvent, memo, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import clsx from "clsx";
import IconSend from "@/assets/icons/send.svg";
import IconImage from "@/assets/icons/image.svg";
import useSendMessage from "../../hooks/useSendMessage";
import { useWidget } from "../WidgetContext";
import useUploadFile from "@/hooks/useUploadFile";
import { Wobble } from "@uiball/loaders";
import { getImageSize } from "@/utils";
type Props = {
from: number;
@@ -12,6 +16,8 @@ type Props = {
};
let isComposing = false;
const MessageInput = (props: Props) => {
const inputRef = useRef<HTMLInputElement>(null);
const { uploadFile, isUploading, isSuccess: uploadSuccess } = useUploadFile();
const { t } = useTranslation("widget");
const { color } = useWidget();
const { from, to } = props;
@@ -36,6 +42,36 @@ const MessageInput = (props: Props) => {
});
setContent("");
};
const handleImageSelect = () => {
if (inputRef?.current) {
inputRef.current.click();
}
};
const handleFileChange = async (evt: ChangeEvent<HTMLInputElement>) => {
const files = evt.target.files;
if (!files || files?.length == 0) return;
const [file] = Array.from(files);
const result = await uploadFile(file);
if (result) {
// send message
const properties: any = await getImageSize(URL.createObjectURL(file));
console.log("uploaded", result, properties);
const { path } = result;
sendMessage({
ignoreLocal: true,
type: "file",
content: { path },
properties
});
}
};
// useEffect(() => {
// if(uploadSuccess){
// inputRef.current.value="";
// }
// }, [uploadSuccess])
return (
<div className="relative border-t border-gray-300 dark:border-gray-600 w-full">
<div className={"px-3 py-2 min-h-[48px] flex items-center gap-2"}>
@@ -69,13 +105,35 @@ const MessageInput = (props: Props) => {
}
}}
/>
<button
onClick={handleSend}
disabled={content.trim().length === 0}
className="px-2 py-1 disabled:opacity-60"
>
<IconSend className="dark:stroke-white w-4 h-4" />
</button>
<div className="flex items-center gap-2">
<button
onClick={handleImageSelect}
disabled={isUploading}
className="p-1 disabled:opacity-60"
>
{isUploading ? (
<Wobble size={16} />
) : (
<IconImage className="dark:stroke-gray-100 w-4 h-4" />
)}
<input
onChange={handleFileChange}
accept="image/*"
ref={inputRef}
type="file"
name="image"
id="image"
hidden
/>
</button>
<button
onClick={handleSend}
disabled={content.trim().length === 0}
className="p-1 disabled:opacity-60"
>
<IconSend className="dark:fill-gray-100 w-4 h-4" />
</button>
</div>
</div>
</div>
);