diff --git a/src-ui/app/main_page/main_section/message_container/log_box/message_container/MessageContainer.jsx b/src-ui/app/main_page/main_section/message_container/log_box/message_container/MessageContainer.jsx index 8ed027d1..003557e6 100644 --- a/src-ui/app/main_page/main_section/message_container/log_box/message_container/MessageContainer.jsx +++ b/src-ui/app/main_page/main_section/message_container/log_box/message_container/MessageContainer.jsx @@ -1,9 +1,42 @@ +import { useState } from "react"; import { useTranslation } from "react-i18next"; import clsx from "clsx"; import styles from "./MessageContainer.module.scss"; - +import { MessageSubMenuContainer } from "./message_sub_menu_container/MessageSubMenuContainer"; +import { useMessage } from "@logics_common"; export const MessageContainer = ({ messages, status, category, created_at }) => { const { t } = useTranslation(); + const { + sendMessage, + updateMessageInputValue, + } = useMessage(); + + const [is_hovered, setIsHovered] = useState(false); + const [is_locked, setIsLocked] = useState(false); + + const resendFunction = () => { + sendMessage(messages.original); + }; + const editFunction = () => { + updateMessageInputValue(messages.original); + }; + + + const handleMouseEnter = () => { + if (!is_locked) { + setIsHovered(true); + } + }; + + const handleMouseLeave = () => { + setIsHovered(false); + setIsLocked(false); + }; + + const lockHoverState = () => { + setIsHovered(false); + setIsLocked(true); + }; const is_translated_exist = messages.translated.length >= 1; const is_pending = status === "pending"; @@ -16,18 +49,31 @@ export const MessageContainer = ({ messages, status, category, created_at }) => }); return ( -
-
-

{created_at}

-

{category_text}

- {is_sent_message && is_pending && } -
-
- {is_translated_exist - ? - :

{messages.original}

- } +
+
+
+

{created_at}

+

{category_text}

+ {is_sent_message && is_pending && } +
+
+ {is_translated_exist + ? + :

{messages.original}

+ } +
+ {is_sent_message && is_hovered ? ( + + ) : null}
); }; diff --git a/src-ui/app/main_page/main_section/message_container/log_box/message_container/MessageContainer.module.scss b/src-ui/app/main_page/main_section/message_container/log_box/message_container/MessageContainer.module.scss index 6ac8ecee..ca7bf6a0 100644 --- a/src-ui/app/main_page/main_section/message_container/log_box/message_container/MessageContainer.module.scss +++ b/src-ui/app/main_page/main_section/message_container/log_box/message_container/MessageContainer.module.scss @@ -1,12 +1,26 @@ @import "@scss_mixins"; +// ******************* ******************* +// ******************* Express in "em" not "rem" ******************* +// ******************* ******************* .container { - margin-bottom: 1em; + width: 100%; + display: flex; + user-select: text; + gap: 0.6rem; + &.sent_message:hover { + background-color: var(--dark_950_color); + } +} + +.message_wrapper { + display: flex; width: 100%; flex-direction: column; - display: flex; justify-content: center; + align-items: end; user-select: text; + padding: 0.6em 0; &.sent_message { align-items: end; } diff --git a/src-ui/app/main_page/main_section/message_container/log_box/message_container/message_sub_menu_container/MessageSubMenuContainer.jsx b/src-ui/app/main_page/main_section/message_container/log_box/message_container/message_sub_menu_container/MessageSubMenuContainer.jsx new file mode 100644 index 00000000..3b9fe829 --- /dev/null +++ b/src-ui/app/main_page/main_section/message_container/log_box/message_container/message_sub_menu_container/MessageSubMenuContainer.jsx @@ -0,0 +1,71 @@ +import React, { useState, useRef } from "react"; +import Tooltip, { tooltipClasses } from '@mui/material/Tooltip'; +import styles from "./MessageSubMenuContainer.module.scss"; +import SendMessageSvg from "@images/send_message.svg?react"; +import RefreshSvg from "@images/refresh_2.svg?react"; + +export const MessageSubMenuContainer = (props) => { + const [is_holding, setIsHolding] = useState(false); + const progressRef = useRef(null); + const holdTimeout = useRef(null); + + const startHold = () => { + setIsHolding(true); + if (progressRef.current) { + progressRef.current.style.transition = "width 500ms linear"; + progressRef.current.style.width = "100%"; + } + holdTimeout.current = setTimeout(() => { + props.resendFunction(); + props.setIsHovered(false); + }, 500); + }; + + const cancelHold = () => { + setIsHolding(false); + if (progressRef.current) { + progressRef.current.style.transition = "none"; + progressRef.current.style.width = "0%"; + } + clearTimeout(holdTimeout.current); + }; + + const onClickFunction = () => { + props.editFunction(); + + }; + + const offset = { + popper: { + sx: { + [`&.${tooltipClasses.popper}[data-popper-placement*="top"] .${tooltipClasses.tooltip}`]: { marginBottom: "0.2em" }, + } + } + }; + + return ( +
+ } + placement="top" + slotProps={offset} + > + + +
+ ); +}; + +const Title_p = () => { + return

Press and hold to send

; +}; diff --git a/src-ui/app/main_page/main_section/message_container/log_box/message_container/message_sub_menu_container/MessageSubMenuContainer.module.scss b/src-ui/app/main_page/main_section/message_container/log_box/message_container/message_sub_menu_container/MessageSubMenuContainer.module.scss new file mode 100644 index 00000000..30b436b1 --- /dev/null +++ b/src-ui/app/main_page/main_section/message_container/log_box/message_container/message_sub_menu_container/MessageSubMenuContainer.module.scss @@ -0,0 +1,45 @@ +// ******************* ******************* +// ******************* Express in "em" not "rem" ******************* +// ******************* ******************* +.container { +} +.resend_button { + background-color: var(--dark_825_color); + position: relative; + height: 100%; + width: 3.8em; +} + +.send_message_svg { + position: absolute; + top: 58%; + left: 50%; + transform: translate(-50%, -50%); + width: 2.2em; + color: var(--dark_400_color); +} +.refresh_svg { + position: absolute; + top: 36%; + left: 42%; + transform: translate(-50%, -50%); + width: 1.8em; + color: var(--sent_400_color); + filter: drop-shadow(0.2em 0.2em 0 var(--dark_825_color)); +} + +.tooltip_title { + font-size: 1.2rem; + color: var(--dark_basic_text_color); +} + +.hold_progress_bar { + position: absolute; + top: 10%; + left: 50%; + transform: translate(-50%, -50%); + width: 0%; + height: 0.4em; + background-color: var(--sent_400_color); + transition: none; +} \ No newline at end of file diff --git a/src-ui/app/main_page/main_section/message_container/message_input_box/MessageInputBox.jsx b/src-ui/app/main_page/main_section/message_container/message_input_box/MessageInputBox.jsx index 6875b0a3..80c5cc83 100644 --- a/src-ui/app/main_page/main_section/message_container/message_input_box/MessageInputBox.jsx +++ b/src-ui/app/main_page/main_section/message_container/message_input_box/MessageInputBox.jsx @@ -7,10 +7,14 @@ import { store } from "@store"; import { scrollToBottom } from "@utils"; export const MessageInputBox = () => { - const [input_value, setInputValue] = useState(""); const [message_history, setMessageHistory] = useState([]); const [history_index, setHistoryIndex] = useState(-1); - const { sendMessage, currentMessageLogs } = useMessage(); + const { + sendMessage, + currentMessageLogs, + currentMessageInputValue, + updateMessageInputValue, + } = useMessage(); const { currentEnableAutoClearMessageInputBox } = useEnableAutoClearMessageInputBox(); const { currentSendMessageButtonType } = useSendMessageButtonType(); @@ -27,11 +31,11 @@ export const MessageInputBox = () => { const onSubmitFunction = (e) => { e.preventDefault(); - if (!input_value.trim()) return setInputValue(""); + if (!currentMessageInputValue.data.trim()) return updateMessageInputValue(""); - sendMessage(input_value); + sendMessage(currentMessageInputValue.data); - if (currentEnableAutoClearMessageInputBox.data) setInputValue(""); + if (currentEnableAutoClearMessageInputBox.data) updateMessageInputValue(""); setTimeout(() => { scrollToBottom(store.log_box_ref); @@ -41,7 +45,7 @@ export const MessageInputBox = () => { }; const onChangeFunction = (e) => { - setInputValue(e.currentTarget.value); + updateMessageInputValue(e.currentTarget.value); }; const onKeyDownFunction = (e) => { @@ -57,7 +61,7 @@ export const MessageInputBox = () => { if (history_index + 1 < message_history.length) { const new_index = history_index + 1; setHistoryIndex(new_index); - setInputValue(message_history[message_history.length - 1 - new_index]); + updateMessageInputValue(message_history[message_history.length - 1 - new_index]); } } @@ -83,7 +87,7 @@ export const MessageInputBox = () => { className={styles.message_box_input_area} onChange={onChangeFunction} placeholder="Input Textfield" - value={input_value} + value={currentMessageInputValue.data} onKeyDown={onKeyDownFunction} />
diff --git a/src-ui/assets/refresh_2.svg b/src-ui/assets/refresh_2.svg new file mode 100644 index 00000000..e6fb5367 --- /dev/null +++ b/src-ui/assets/refresh_2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src-ui/logics/common/useMessage.js b/src-ui/logics/common/useMessage.js index 944c40b7..a951da58 100644 --- a/src-ui/logics/common/useMessage.js +++ b/src-ui/logics/common/useMessage.js @@ -1,11 +1,13 @@ import { useStore_MessageLogs, + useStore_MessageInputValue, } from "@store"; import { useStdoutToPython } from "@logics/useStdoutToPython"; export const useMessage = () => { const { currentMessageLogs, addMessageLogs, updateMessageLogs } = useStore_MessageLogs(); + const { currentMessageInputValue, updateMessageInputValue } = useStore_MessageInputValue(); const { asyncStdoutToPython } = useStdoutToPython(); const sendMessage = (message) => { @@ -46,6 +48,9 @@ export const useMessage = () => { updateSentMessageLogById, addSentMessageLog, addReceivedMessageLog, + + currentMessageInputValue, + updateMessageInputValue, }; }; diff --git a/src-ui/store.js b/src-ui/store.js index 4a426d0c..d082b3fb 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -120,6 +120,7 @@ export const { atomInstance: Atom_TranscriptionReceiveStatus, useHook: useStore_ export const { atomInstance: Atom_ForegroundStatus, useHook: useStore_ForegroundStatus } = createAtomWithHook(false, "ForegroundStatus", {is_state_ok: true}); export const { atomInstance: Atom_MessageLogs, useHook: useStore_MessageLogs } = createAtomWithHook(generateTestData(20), "MessageLogs"); +export const { atomInstance: Atom_MessageInputValue, useHook: useStore_MessageInputValue } = createAtomWithHook("", "MessageInputValue"); export const { atomInstance: Atom_SelectableLanguageList, useHook: useStore_SelectableLanguageList } = createAtomWithHook([], "SelectableLanguageList");