diff --git a/package.json b/package.json index 489c2d0c481..eda8c4d7670 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "start:prod": "NODE_ENV=production node build/server.js" }, "dependencies": { - "@ecency/ns-query": "^1.0.2", + "@ecency/ns-query": "^1.0.4", "@ecency/render-helper": "^2.2.29", "@ecency/render-helper-amp": "^1.1.0", "@emoji-mart/data": "^1.1.2", diff --git a/src/common/app.tsx b/src/common/app.tsx index 627e7af6fe5..552dfdbeb13 100644 --- a/src/common/app.tsx +++ b/src/common/app.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useMemo, useState } from "react"; import { Route, Switch } from "react-router-dom"; import EntryIndexContainer from "./pages/index"; import { EntryScreen } from "./pages/entry"; @@ -33,6 +33,8 @@ import { ChatPopUp } from "./features/chats/components/chat-popup"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { ChatContextProvider } from "@ecency/ns-query"; import { useGetAccountFullQuery } from "./api/queries"; +import defaults from "./constants/defaults.json"; +import { getAccessToken } from "./helper/user-token"; // Define lazy pages const ProfileContainer = loadable(() => import("./pages/profile-functional")); @@ -105,13 +107,23 @@ const App = (props: any) => { } }); + const accessToken = useMemo( + () => (activeUser ? getAccessToken(activeUser.username) ?? "" : ""), + [activeUser] + ); + return ( {/*Excluded from production*/} - + diff --git a/src/common/components/profile-card/index.tsx b/src/common/components/profile-card/index.tsx index 59ffddc1948..0830fdda92f 100644 --- a/src/common/components/profile-card/index.tsx +++ b/src/common/components/profile-card/index.tsx @@ -260,7 +260,7 @@ export const ProfileCard = (props: Props) => { ))} )} -
+
{isCommunity(account?.name) && ( <> diff --git a/src/common/features/chats/components/chat-channel-messages.tsx b/src/common/features/chats/components/chat-channel-messages.tsx index 2b39aa9c531..04da95085fb 100644 --- a/src/common/features/chats/components/chat-channel-messages.tsx +++ b/src/common/features/chats/components/chat-channel-messages.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { useMappedStore } from "../../../store/use-mapped-store"; import { _t } from "../../../i18n"; import { ChatMessageItem } from "./chat-message-item"; @@ -17,6 +17,7 @@ import { import { groupMessages } from "../utils"; import { ChatFloatingDate } from "./chat-floating-date"; import { differenceInCalendarDays } from "date-fns"; +import useDebounce from "react-use/lib/useDebounce"; interface Props { publicMessages: PublicMessage[]; @@ -31,9 +32,10 @@ export function ChatsChannelMessages({ publicMessages, currentChannel, isPage }: // Message where users interacted with context menu const [currentInteractingMessageId, setCurrentInteractingMessageId] = useState(); + const [needFetchNextPage, setNeedFetchNextPage] = useState(false); const { publicKey } = useKeysQuery(); - const { fetchNextPage } = usePublicMessagesQuery(currentChannel); + const { fetchNextPage, refetch } = usePublicMessagesQuery(currentChannel); const { mutateAsync: updateBlockedUsers, isLoading: isUsersBlockingLoading } = useUpdateChannelBlockedUsers(currentChannel); @@ -49,6 +51,22 @@ export function ChatsChannelMessages({ publicMessages, currentChannel, isPage }: ); const groupedMessages = useMemo(() => groupMessages(messages), [messages]); + useEffect(() => { + if (messages.length === 0) { + refetch(); + } + }, [messages]); + + useDebounce( + () => { + if (needFetchNextPage) { + fetchNextPage(); + } + }, + 500, + [needFetchNextPage] + ); + return ( <>
@@ -88,12 +106,8 @@ export function ChatsChannelMessages({ publicMessages, currentChannel, isPage }: 100 ) } - onInViewport={() => - i === groupedMessages.length - 1 && - j === group.length - 1 && - fetchNextPage({ - pageParam: message.created * 1000 - }) + onInViewport={(inViewport) => + i === 0 && j === 0 && setNeedFetchNextPage(inViewport) } /> void; + onUpload?: (url: string) => void; +} + +function FileItem({ link, file, onDelete, isUploading = false, onUpload }: FileItemProps) { + const { + mutateAsync: upload, + isLoading, + isError + } = useChatFileUpload((_, url) => onUpload?.(url)); + + useMount(() => { + if (file) { + upload(file); + } + }); + + return ( +
+ {!isLoading && ( + + )} + {!isError && (isLoading || isUploading) && } +
+ ); +} + +interface Props { + files: File[]; + setFiles: Dispatch>; + uploadedFileLinks: string[]; + setUploadedFileLinks: Dispatch>; +} + +export function ChatInputFiles({ + files, + setFiles, + uploadedFileLinks, + setUploadedFileLinks +}: Props) { + const fileList = useMemo( + () => + [...files] + .filter((file) => { + const filenameLow = file.name.toLowerCase(); + return CHAT_FILE_CONTENT_TYPES.some((el) => filenameLow.endsWith(el)); + }) + .map((file) => [file, URL.createObjectURL(file)] as const), + [files] + ); + + return ( +
+ {uploadedFileLinks.map((item) => ( + setUploadedFileLinks(uploadedFileLinks.filter((f) => f !== item))} + /> + ))} + {fileList.map(([file, item]) => ( + setFiles(files.filter((f) => f !== file))} + onUpload={(link) => { + setUploadedFileLinks((links) => [...links, link]); + setFiles((files) => files.filter((f) => f !== file)); + }} + /> + ))} +
+ ); +} diff --git a/src/common/features/chats/components/chat-input.tsx b/src/common/features/chats/components/chat-input.tsx index 80964fbbcd5..cc83d1794f1 100644 --- a/src/common/features/chats/components/chat-input.tsx +++ b/src/common/features/chats/components/chat-input.tsx @@ -5,15 +5,15 @@ import { chatBoxImageSvg, emoticonHappyOutlineSvg, gifIcon, + imageSvg, informationOutlineSvg, messageSendSvg } from "../../../img/svg"; -import { CHAT_FILE_CONTENT_TYPES, GifImagesStyle } from "./chat-popup/chat-constants"; +import { GifImagesStyle } from "./chat-popup/chat-constants"; import { _t } from "../../../i18n"; import { Form } from "@ui/form"; import { FormControl } from "@ui/input"; import { Button } from "@ui/button"; -import { useChatFileUpload } from "../mutations"; import { Dropdown, DropdownItemWithIcon, DropdownMenu, DropdownToggle } from "@ui/dropdown"; import GifPicker from "../../../components/gif-picker"; import useClickAway from "react-use/lib/useClickAway"; @@ -28,6 +28,8 @@ import { } from "@ecency/ns-query"; import { useGetAccountFullQuery } from "../../../api/queries"; import Tooltip from "../../../components/tooltip"; +import { ChatInputFiles } from "./chat-input-files"; +import Gallery from "../../../components/gallery"; interface Props { currentChannel?: Channel; @@ -45,10 +47,12 @@ export default function ChatInput({ currentChannel, currentContact }: Props) { const { receiverPubKey } = useContext(ChatContext); const [message, setMessage] = useState(""); + const [files, setFiles] = useState([]); + const [uploadedFileLinks, setUploadedFileLinks] = useState([]); const [showGifPicker, setShowGifPicker] = useState(false); + const [showGallery, setShowGallery] = useState(false); const { data: contactData } = useGetAccountFullQuery(currentContact?.name); - const { mutateAsync: upload } = useChatFileUpload(setMessage); const { mutateAsync: sendMessage, isLoading: isSendMessageLoading } = useSendMessage( currentChannel, currentContact, @@ -68,6 +72,10 @@ export default function ChatInput({ currentChannel, currentContact }: Props) { () => (contactData ? currentContact?.pubkey !== getUserChatPublicKey(contactData) : false), [contactData, currentContact] ); + const isFilesUploading = useMemo( + () => (files.length > 0 ? files.length !== uploadedFileLinks.length : false), + [files, uploadedFileLinks] + ); useClickAway(gifPickerRef, () => setShowGifPicker(false)); @@ -77,25 +85,22 @@ export default function ChatInput({ currentChannel, currentContact }: Props) { } }, [isCommunity, isCurrentUser]); - const checkFile = (filename: string) => { - const filenameLow = filename.toLowerCase(); - return CHAT_FILE_CONTENT_TYPES.some((el) => filenameLow.endsWith(el)); - }; - - const fileInputChanged = (e: React.ChangeEvent): void => { - let files = [...(e.target.files as FileList)].filter((i) => checkFile(i.name)).filter((i) => i); - - if (files.length > 0) { - e.stopPropagation(); - e.preventDefault(); + const submit = async () => { + if (isDisabled || isSendMessageLoading || isFilesUploading || !message) { + return; } - - files.forEach((file) => upload(file)); - - // reset input - e.target.value = ""; + const nextMessage = buildImages(message); + await sendMessage(nextMessage); + setFiles([]); + setUploadedFileLinks([]); + // Re-focus to input because when DOM changes and input position changes then + // focus is lost + setTimeout(() => inputRef.current?.focus(), 1); }; + const buildImages = (message: string) => + `${message}${uploadedFileLinks.map((link) => `\n![](${link})`)}`; + return (
{isReadOnly ? ( @@ -117,8 +122,25 @@ export default function ChatInput({ currentChannel, currentContact }: Props) { fallback={(e) => sendMessage(e)} /> )} + {(files.length > 0 || uploadedFileLinks.length > 0) && !showGifPicker && ( + + )} + {showGallery && ( + setShowGallery(false)} + onPick={(e) => { + setUploadedFileLinks((links) => [...links, e]); + setShowGallery(false); + }} + /> + )} setFiles([...(e.target.files ?? [])])} className="hidden" ref={fileInputRef} type="file" @@ -130,11 +152,7 @@ export default function ChatInput({ currentChannel, currentContact }: Props) { onSubmit={(e) => { e.preventDefault(); e.stopPropagation(); - sendMessage(message).then(() => { - // Re-focus to input because when DOM changes and input position changes then - // focus is lost - setTimeout(() => inputRef.current?.focus(), 1); - }); + submit(); }} className="w-full flex items-center gap-2 p-1.5" > @@ -156,9 +174,14 @@ export default function ChatInput({ currentChannel, currentContact }: Props) { /> fileInputRef.current?.click()} /> + setShowGallery(true)} + /> : messageSendSvg} - disabled={isDisabled || isSendMessageLoading} - onClick={() => sendMessage(message)} + disabled={isDisabled || isSendMessageLoading || isFilesUploading} + onClick={() => submit()} />
diff --git a/src/common/features/chats/components/chat-message-item.tsx b/src/common/features/chats/components/chat-message-item.tsx index 61e85117401..42712492754 100644 --- a/src/common/features/chats/components/chat-message-item.tsx +++ b/src/common/features/chats/components/chat-message-item.tsx @@ -94,7 +94,7 @@ export function ChatMessageItem({
diff --git a/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx b/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx index cc770d5be36..4c516e5c106 100644 --- a/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx @@ -1,14 +1,10 @@ -import React, { useContext } from "react"; +import React from "react"; import { _t } from "../../../../i18n"; import { ChatsWelcome } from "../chats-welcome"; import { Button } from "@ui/button"; -import { ChatDirectContactOrChannelItem } from "./chat-direct-contact-or-channel-item"; -import { - ChatContext, - useChannelsQuery, - useDirectContactsQuery, - useKeysQuery -} from "@ecency/ns-query"; +import { useChannelsQuery, useDirectContactsQuery, useKeysQuery } from "@ecency/ns-query"; +import { ChatSidebarDirectContact } from "../chats-sidebar/chat-sidebar-direct-contact"; +import { ChatSidebarChannel } from "../chats-sidebar/chat-sidebar-channel"; interface Props { communityClicked: (v: string) => void; @@ -21,8 +17,6 @@ export function ChatPopupContactsAndChannels({ userClicked, setShowSearchUser }: Props) { - const { setReceiverPubKey } = useContext(ChatContext); - const { privateKey } = useKeysQuery(); const { data: directContacts } = useDirectContactsQuery(); const { data: channels } = useChannelsQuery(); @@ -38,11 +32,12 @@ export function ChatPopupContactsAndChannels({ {_t("chat.communities")}
{channels?.map((channel) => ( - communityClicked(channel.communityName!)} + onClick={() => communityClicked(channel.communityName!)} /> ))} {directContacts?.length !== 0 && ( @@ -53,13 +48,10 @@ export function ChatPopupContactsAndChannels({ )} {directContacts?.map((user) => ( - { - setReceiverPubKey(user.pubkey); - userClicked(v); - }} + onClick={() => userClicked(user.name)} key={user.pubkey} /> ))} diff --git a/src/common/features/chats/components/chat-popup/index.tsx b/src/common/features/chats/components/chat-popup/index.tsx index 5b91cc13982..93ea64071ef 100644 --- a/src/common/features/chats/components/chat-popup/index.tsx +++ b/src/common/features/chats/components/chat-popup/index.tsx @@ -20,7 +20,6 @@ import { useJoinChat, useKeysQuery } from "@ecency/ns-query"; -import { uploadChatKeys } from "../../utils/upload-chat-keys"; import { ChatInvitation } from "../chat-invitation"; export const ChatPopUp = () => { @@ -28,7 +27,7 @@ export const ChatPopUp = () => { const { receiverPubKey, revealPrivateKey, setRevealPrivateKey, setReceiverPubKey } = useContext(ChatContext); - const { isLoading: isJoinChatLoading } = useJoinChat(uploadChatKeys); + const { isLoading: isJoinChatLoading } = useJoinChat(); const { privateKey } = useKeysQuery(); const { data: directContacts } = useDirectContactsQuery(); @@ -80,6 +79,8 @@ export const ChatPopUp = () => { const handleMessageSvgClick = () => { setShowSearchUser(!showSearchUser); setExpanded(true); + setRevealPrivateKey(false); + setReceiverPubKey(""); }; const handleBackArrowSvg = () => { diff --git a/src/common/features/chats/components/chats-community-actions/blocked-users-modal.tsx b/src/common/features/chats/components/chats-community-actions/blocked-users-modal.tsx index ed9ebc371a2..a39628666de 100644 --- a/src/common/features/chats/components/chats-community-actions/blocked-users-modal.tsx +++ b/src/common/features/chats/components/chats-community-actions/blocked-users-modal.tsx @@ -9,6 +9,7 @@ import { useNostrGetUserProfilesQuery, useUpdateChannelBlockedUsers } from "@ecency/ns-query"; +import useMount from "react-use/lib/useMount"; interface Props { username: string; @@ -24,11 +25,17 @@ export function BlockedUsersModal({ username, setShow, show }: Props) { [channels] ); - const { data: profiles } = useNostrGetUserProfilesQuery(currentChannel?.removedUserIds ?? []); + const { data: profiles, refetch: refetchProfiles } = useNostrGetUserProfilesQuery( + currentChannel?.removedUserIds ?? [] + ); const { mutateAsync: updateBlockedUsers, isLoading: isBlockedUsersLoading } = useUpdateChannelBlockedUsers(currentChannel!!); + useMount(() => { + refetchProfiles(); + }); + return ( setShow(false)}> {_t("chat.blocked-users-management")} diff --git a/src/common/features/chats/components/chats-direct-messages/index.tsx b/src/common/features/chats/components/chats-direct-messages/index.tsx index c70a84e2467..64272777e09 100644 --- a/src/common/features/chats/components/chats-direct-messages/index.tsx +++ b/src/common/features/chats/components/chats-direct-messages/index.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import "./index.scss"; import { ChatMessageItem } from "../chat-message-item"; import { @@ -38,6 +38,12 @@ export default function ChatsDirectMessages(props: Props) { [needFetchNextPage] ); + useEffect(() => { + if (directMessages.length === 0) { + directMessagesQuery.refetch(); + } + }, [directMessages]); + return ( <>
diff --git a/src/common/features/chats/components/chats-import.tsx b/src/common/features/chats/components/chats-import.tsx index 1eb8aae6865..d63c1ce697a 100644 --- a/src/common/features/chats/components/chats-import.tsx +++ b/src/common/features/chats/components/chats-import.tsx @@ -5,14 +5,13 @@ import { CodeInput, FormControl } from "@ui/input"; import React, { useState } from "react"; import { Alert } from "@ui/alert"; import { useImportChatByKeys } from "@ecency/ns-query"; -import { uploadChatKeys } from "../utils/upload-chat-keys"; export function ChatsImport() { const [step, setStep] = useState(0); const [ecencyChatKey, setEcencyChatKey] = useState(""); const [pin, setPin] = useState(""); - const { mutateAsync: importChatByKey } = useImportChatByKeys(uploadChatKeys); + const { mutateAsync: importChatByKey } = useImportChatByKeys(); return ( <> diff --git a/src/common/features/chats/components/chats-sidebar/chat-sidebar-channel.tsx b/src/common/features/chats/components/chats-sidebar/chat-sidebar-channel.tsx index 311f12e0c54..cce6a249a36 100644 --- a/src/common/features/chats/components/chats-sidebar/chat-sidebar-channel.tsx +++ b/src/common/features/chats/components/chats-sidebar/chat-sidebar-channel.tsx @@ -8,9 +8,17 @@ interface Props { username: string; channel: Channel; isChannel: boolean; + isLink?: boolean; + onClick?: () => void; } -export function ChatSidebarChannel({ channel, username, isChannel }: Props) { +export function ChatSidebarChannel({ + channel, + username, + isChannel, + isLink = true, + onClick +}: Props) { const { revealPrivateKey, setRevealPrivateKey, setReceiverPubKey } = useContext(ChatContext); const lastMessage = useLastMessageQuery(undefined, channel); @@ -18,7 +26,20 @@ export function ChatSidebarChannel({ channel, username, isChannel }: Props) { const rawUsername = useMemo(() => username?.replace("@", "") ?? "", [username]); const lastMessageDate = useMemo(() => getRelativeDate(lastMessage?.created), [lastMessage]); - return ( + const content = ( + <> + +
+
+
{channel.name}
+
{lastMessageDate}
+
+
{lastMessage?.content}
+
+ + ); + + return isLink ? ( - -
-
-
{channel.name}
-
{lastMessageDate}
-
-
{lastMessage?.content}
-
+ {content} + ) : ( +
{ + setReceiverPubKey(""); + if (revealPrivateKey) { + setRevealPrivateKey(false); + } + onClick?.(); + }} + > + {content} +
); } diff --git a/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx b/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx index a651da0dd3f..4e0f53bbab2 100644 --- a/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx +++ b/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx @@ -6,6 +6,7 @@ import { DirectContact, getRelativeDate, getUserChatPublicKey, + useKeysQuery, useLastMessageQuery } from "@ecency/ns-query"; import { useGetAccountFullQuery } from "../../../../api/queries"; @@ -17,13 +18,18 @@ import { Link } from "react-router-dom"; interface Props { contact: DirectContact; + isLink?: boolean; + onClick?: () => void; } -export function ChatSidebarDirectContact({ contact }: Props) { +export function ChatSidebarDirectContact({ contact, onClick, isLink = true }: Props) { const { receiverPubKey, setReceiverPubKey, revealPrivateKey, setRevealPrivateKey } = useContext(ChatContext); - const { data: contactData } = useGetAccountFullQuery(contact.name); + const { publicKey } = useKeysQuery(); + const { data: contactData, isLoading: isContactDataLoading } = useGetAccountFullQuery( + contact.name + ); const lastMessage = useLastMessageQuery(contact); const lastMessageDate = useMemo(() => getRelativeDate(lastMessage?.created), [lastMessage]); @@ -36,24 +42,11 @@ export function ChatSidebarDirectContact({ contact }: Props) { [contactData, contact, isJoined] ); - return ( - { - setReceiverPubKey(contact.pubkey); - if (revealPrivateKey) { - setRevealPrivateKey(false); - } - }} - > + const content = ( + <>
@@ -61,7 +54,7 @@ export function ChatSidebarDirectContact({ contact }: Props) {
- {isReadOnly && ( + {isReadOnly && !isContactDataLoading && (
{_t("chat.read-only")} @@ -69,7 +62,7 @@ export function ChatSidebarDirectContact({ contact }: Props) {
)} - {!isJoined && ( + {!isJoined && !isContactDataLoading && (
{_t("chat.user-not-joined")} @@ -81,8 +74,50 @@ export function ChatSidebarDirectContact({ contact }: Props) {
{lastMessageDate}
-
{lastMessage?.content}
+
+ {lastMessage?.creator === publicKey && ( + {_t("g.you")}: + )} + {lastMessage?.content} +
+ + ); + + return isLink ? ( + { + setReceiverPubKey(contact.pubkey); + if (revealPrivateKey) { + setRevealPrivateKey(false); + } + onClick?.(); + }} + > + {content} + ) : ( +
{ + setReceiverPubKey(contact.pubkey); + if (revealPrivateKey) { + setRevealPrivateKey(false); + } + onClick?.(); + }} + > + {content} +
); } diff --git a/src/common/features/chats/components/chats-welcome.tsx b/src/common/features/chats/components/chats-welcome.tsx index 57ab938dbd6..7c18c89af5e 100644 --- a/src/common/features/chats/components/chats-welcome.tsx +++ b/src/common/features/chats/components/chats-welcome.tsx @@ -6,12 +6,7 @@ import { useGetAccountFullQuery } from "../../../api/queries"; import { useMappedStore } from "../../../store/use-mapped-store"; import { CreateAnAccount } from "./create-an-account"; import { ChatsImport } from "./chats-import"; -import { - ChatContext, - getUserChatPrivateKey, - getUserChatPublicKey, - useRestoreChatByPin -} from "@ecency/ns-query"; +import { ChatContext, getUserChatPublicKey, useRestoreChatByPin } from "@ecency/ns-query"; export function ChatsWelcome() { const { activeUser } = useMappedStore(); @@ -33,8 +28,7 @@ export function ChatsWelcome() { return false; } const publicKey = getUserChatPublicKey(fullAccount); - const { key, iv } = getUserChatPrivateKey(fullAccount); - return !!key && !!iv && !!publicKey; + return !!publicKey; }, [fullAccount]); useEffect(() => { diff --git a/src/common/features/chats/components/create-an-account.tsx b/src/common/features/chats/components/create-an-account.tsx index 7eaab90948e..3c51271620d 100644 --- a/src/common/features/chats/components/create-an-account.tsx +++ b/src/common/features/chats/components/create-an-account.tsx @@ -5,13 +5,12 @@ import { Modal, ModalBody, ModalFooter, ModalHeader } from "@ui/modal"; import { Alert } from "@ui/alert"; import { CodeInput } from "@ui/input"; import { useJoinChat } from "@ecency/ns-query"; -import { uploadChatKeys } from "../utils/upload-chat-keys"; export function CreateAnAccount() { const [step, setStep] = useState(0); const [pin, setPin] = useState(""); - const { mutateAsync: joinChat } = useJoinChat(uploadChatKeys); + const { mutateAsync: joinChat } = useJoinChat(); return ( <> diff --git a/src/common/features/chats/mutations/upload.ts b/src/common/features/chats/mutations/upload.ts index e1ac46cf6de..d3e6d06f1f2 100644 --- a/src/common/features/chats/mutations/upload.ts +++ b/src/common/features/chats/mutations/upload.ts @@ -11,7 +11,7 @@ class FileUploadingError { constructor(public code: number, public message: string) {} } -export function useChatFileUpload(setMessage: (v: string) => void) { +export function useChatFileUpload(onSuccess: (file: File, v: string) => void) { const { activeUser, global } = useMappedStore(); return useMutation( @@ -29,9 +29,6 @@ export function useChatFileUpload(setMessage: (v: string) => void) { throw new FileUploadingError(1, "[Chat][File uploading] No token"); } - const tempImgTag = `![Uploading ${file.name} #${Math.floor(Math.random() * 99)}]()\n\n`; - setMessage(tempImgTag); - let imageUrl: string; const resp = await uploadImage(file, token); imageUrl = resp.url; @@ -40,22 +37,22 @@ export function useChatFileUpload(setMessage: (v: string) => void) { await addImage(username, imageUrl); } - const imgTag = imageUrl.length > 0 && `![](${imageUrl})\n\n`; - if (imgTag) { - setMessage(imgTag); - } + return [file, imageUrl] as const; }, { onError: (e) => { if (axios.isAxiosError(e) && e.response?.status === 413) { error(_t("editor-toolbar.image-error-size")); + } else if (axios.isAxiosError(e) && e.response?.status === 409) { + error(_t("editor-toolbar.image-error-conflict-name")); } else if (e instanceof FileUploadingError) { error(_t("editor-toolbar.image-error-cache")); throw new Error(e.message); } else { error(_t("editor-toolbar.image-error")); } - } + }, + onSuccess: ([file, imageUrl]) => onSuccess(file, imageUrl) } ); } diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 0cea8fb1e0d..68e76cb202e 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -82,7 +82,8 @@ "resend": "Resend", "failed": "Failed", "status": "Status", - "download": "Download" + "download": "Download", + "retry": "Retry" }, "confirm": { "title": "Are you sure?", @@ -1407,6 +1408,7 @@ "image-error": "Couldn't upload image.", "image-error-cache": "Couldn't upload image. You seem to be signed out. Try signing in again.", "image-error-size": "Image size is too large, try smaller size image again.", + "image-error-conflict-name": "You have image with this filename already. Select it from gallery instead.", "fragments": "Snippets", "link-image": "Link" }, @@ -1910,6 +1912,7 @@ "blocked-users-management": "Blocked users management", "unlock-the-section": "Please, enter your PIN to see your credentials information", "no-contacts-or-channels": "No conversations yet", + "upload-image": "Upload image", "welcome": { "title": "Welcome to Chats", "description": "Create or import an account start using Chats",