Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/chat updates: 채팅 검색 후 뒤로가기, 채팅 및 북마크 번역 연결, 채팅방 모달, 채팅방 ui/ux 관련 업데이트 #131

Merged
merged 7 commits into from
Sep 19, 2024
20 changes: 20 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@
"sha1-file": "^3.0.0",
"text-encoding": "^0.7.0",
"typescript": "~5.3.3",
"uuid": "^9.0.1"
"uuid": "^9.0.1",
"expo-clipboard": "~6.0.3",
"expo-haptics": "~13.0.1"
},
"devDependencies": {
"@babel/core": "^7.24.0",
Expand Down
100 changes: 80 additions & 20 deletions src/components/chat/Bookmark.jsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,95 @@
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
import { useTranslation } from "react-i18next";
import * as Sentry from "@sentry/react-native";

import { CustomTheme } from "@styles/CustomTheme";
import { getMyProfile, translationByBookmarkedId } from "config/api";

import IconChatProfile from "@components/chat/IconChatProfile";
import IconBookmark from "@components/chat/IconBookmark";

import ModalNoBookmark from "@components/chat/ModalNoBookmark";
import DashedLine from "@components/chat/DashedLine";
import ModalTranslationsCount from "@components/common/ModalTranslationsCount";

const { fontCaption, fontNavi } = CustomTheme;

const Bookmark = ({
name = "name",
context = "context",
date = "date",
time = "time",
translation = "translation",
bookmarkedId,
username,
context,
created,
translations,
}) => {
const { t } = useTranslation();
const [expanded, setExpanded] = useState(false);
const [content, setContent] = useState();
const [date, setDate] = useState("");
const [time, setTime] = useState("");
const [isTranslation, setIsTranslation] = useState(false);
const [translationCount, setTranslationCount] = useState();
const [modalVisible, setModalVisible] = useState(false);
const [modalTranslationVisible, setModalTranslationVisible] =
useState(false);

const rectangleStyle = () =>
expanded ? styles.rectangleExpanded : styles.rectangle;

const handleExpanded = () => {
setExpanded(!expanded);
};

const [modalVisible, setModalVisible] = useState(false);

const pressButton = () => {
setModalVisible(!modalVisible);
};

const rectangleStyle = () =>
expanded ? styles.rectangleExpanded : styles.rectangle;
const handleTranslations = async () => {
try {
const responseCount = await getMyProfile();
const count =
responseCount.data.translationCount === 0
? 0
: responseCount.data.translationCount + 1;
setTranslationCount(count);

const handleTranslation = () => {
setIsTranslation(!isTranslation);
if (!isTranslation) {
if (translations.length > 0) {
setIsTranslation(true);
setContent(translations[0].text);
} else {
if (translationCount === 0) {
setModalTranslationVisible(true);
setIsTranslation(true);
const response =
await translationByBookmarkedId(bookmarkedId);
setContent(response.data.translations[0].text);
} else if (translationCount <= 15) {
setModalTranslationVisible(false);
setIsTranslation(true);
const response =
await translationByBookmarkedId(bookmarkedId);
setContent(response.data.translations[0].text);
} else if (translationCount > 15) {
setModalTranslationVisible(true);
}
}
} else {
setIsTranslation(false);
}
} catch (error) {
Sentry.captureException(error);
console.error(
"채팅 북마크 번역 오류:",
error.response ? error.response.data : error.message,
);
}
};

useEffect(() => {
setDate(created.split("T")[0].replace("-", "."));
setTime(created.split("T")[1].substring(0, 5));
}, [created]);

return (
<>
<TouchableOpacity style={rectangleStyle()} onPress={handleExpanded}>
Expand All @@ -46,7 +99,7 @@ const Bookmark = ({
<IconChatProfile size="32" />
</View>
<View style={styles.textContainer}>
<Text style={styles.textName}>{name}</Text>
<Text style={styles.textName}>{username}</Text>
<Text style={styles.textContext}>{context}</Text>
</View>
</View>
Expand All @@ -65,7 +118,7 @@ const Bookmark = ({
<ModalNoBookmark
modalVisible={modalVisible}
setModalVisible={setModalVisible}
name={name}
name={username}
context={context}
date={date}
time={time}
Expand All @@ -84,25 +137,32 @@ const Bookmark = ({
<View style={styles.containerOriginalTranslation}>
<View style={styles.containerTextExpanded}>
<Text style={styles.textExpandedTitle}>
원문:
{t("originalText")}
</Text>
<Text style={styles.textExpandedContext}>
{context}
</Text>
</View>
<TouchableOpacity onPress={handleTranslation}>
<TouchableOpacity onPress={handleTranslations}>
<Text style={styles.textTranslation}>
{isTranslation ? "원문만 보기" : "번역하기"}
{isTranslation
? t("viewOriginalOnlyButton")
: t("translateButton")}
</Text>
</TouchableOpacity>
<ModalTranslationsCount
modalVisible={modalTranslationVisible}
setModalVisible={setModalTranslationVisible}
translationCount={translationCount}
/>
</View>
{isTranslation && (
<View style={styles.containerTextExpanded}>
<Text style={styles.textExpandedTitle}>
번역:
{t("translationText")}
</Text>
<Text style={styles.textExpandedContext}>
{translation}
{content}
</Text>
</View>
)}
Expand Down
6 changes: 4 additions & 2 deletions src/components/chat/ChatInputSend.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
KeyboardAvoidingView,
Platform,
} from "react-native";
import { useTranslation } from "react-i18next";

import { CustomTheme } from "@styles/CustomTheme";

Expand All @@ -23,6 +24,7 @@ import { useWebSocket } from "context/WebSocketContext";
const { fontBody14 } = CustomTheme;

const ChatInputSend = ({ chatroomId, memberId }) => {
const { t } = useTranslation();
const [chatInput, setChatInput] = useState("");
const [plusClick, setPlusClick] = useState(false);
const { publishMessage } = useWebSocket();
Expand Down Expand Up @@ -85,13 +87,13 @@ const ChatInputSend = ({ chatroomId, memberId }) => {
<TouchableOpacity style={styles.iconCircleCamera}>
<IconCircleCamera />
<Text style={styles.textIconCircle}>
카메라
{t("accessCameraSubtitle")}
</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.iconCircleGallery}>
<IconCircleGallery />
<Text style={styles.textIconCircle}>
갤러리
{t("gallery")}
</Text>
</TouchableOpacity>
</View>
Expand Down
5 changes: 3 additions & 2 deletions src/components/chat/ChatroomItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import { useNavigation } from "@react-navigation/native";

const { fontCaption, fontNavi } = CustomTheme;

const ChatroomItem = ({ chatroomInfo, context, name, time, myMemberId }) => {
const ChatroomItem = ({ chatroomInfo, context, time, myMemberId }) => {
const navigation = useNavigation("");
const otherMember = chatroomInfo.members.find(
(member) => member.id !== myMemberId,
);
const otherMemberProfileImageName = otherMember.profileImg?.originalName;
const username = otherMember.username;

return (
<TouchableOpacity
Expand All @@ -31,7 +32,7 @@ const ChatroomItem = ({ chatroomInfo, context, name, time, myMemberId }) => {
/>
</View>
<View style={styles.textContainer}>
<Text style={styles.textName}>{name}</Text>
<Text style={styles.textName}>{username}</Text>
<Text style={styles.textContext}>{context}</Text>
</View>
</View>
Expand Down
21 changes: 21 additions & 0 deletions src/components/chat/IconModalBookmark.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from "react";
import Svg, { Path } from "react-native-svg";

const IconModalBookmark = (props) => (
<Svg
xmlns="http://www.w3.org/2000/svg"
width={16}
height={16}
fill="none"
{...props}
>
<Path
fill="#AFB0B3"
fillRule="evenodd"
d="M9.68 1c2.226 0 3.503 1.102 3.503 3.022v9.54c0 .4-.206.76-.553.962a1.105 1.105 0 0 1-1.11.009l-3.912-2.21-3.948 2.214a1.106 1.106 0 0 1-1.108-.011A1.103 1.103 0 0 1 2 13.565V3.9C2 2.057 3.278 1 5.506 1h4.173Zm0 .984H5.505c-1.673 0-2.522.645-2.522 1.917v9.664c0 .062.036.095.065.112.03.018.077.032.13.001l4.189-2.35a.494.494 0 0 1 .483.002l4.152 2.345a.122.122 0 0 0 .13 0 .126.126 0 0 0 .065-.114V3.947c-.005-.565-.114-1.963-2.519-1.963Zm.287 3.432a.492.492 0 0 1 0 .984H5.165a.492.492 0 0 1 0-.984h4.802Z"
clipRule="evenodd"
/>
</Svg>
);

export default IconModalBookmark;
19 changes: 19 additions & 0 deletions src/components/chat/IconModalCopy.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as React from "react";
import Svg, { G, Path } from "react-native-svg";

const IconModalCopy = (props) => (
<Svg
xmlns="http://www.w3.org/2000/svg"
width={16}
height={16}
fill="none"
{...props}
>
<G fill="#AFB0B3" fillRule="evenodd" clipRule="evenodd">
<Path d="M2.233 1.64c.49-.527 1.184-.816 1.987-.816h3.826c.708 0 1.332.223 1.808.64.474.416.767.994.868 1.648a.552.552 0 0 1-1.091.17c-.067-.433-.25-.765-.505-.988-.252-.221-.609-.366-1.08-.366H4.22c-.528 0-.918.184-1.18.464-.264.285-.436.713-.436 1.266v4.928c0 .505.144.905.369 1.186.222.276.548.468.984.522a.552.552 0 0 1-.136 1.096c-.706-.087-1.298-.415-1.709-.927C1.704 9.956 1.5 9.3 1.5 8.586V3.658c0-.782.246-1.495.733-2.018Z" />
<Path d="M5.97 5.4c.49-.526 1.184-.814 1.987-.814h3.824c.805 0 1.5.288 1.99.814.486.523.73 1.235.73 2.017v4.928c0 .782-.244 1.494-.73 2.016-.491.527-1.186.815-1.99.815H7.956c-.805 0-1.5-.288-1.99-.815-.486-.522-.73-1.234-.73-2.016V7.417c0-.783.245-1.495.732-2.017Zm.807.753c-.265.284-.437.71-.437 1.264v4.928c0 .554.172.98.435 1.264.26.279.65.463 1.182.463h3.823c.533 0 .923-.184 1.182-.463.264-.283.435-.71.435-1.264V7.417c0-.554-.17-.981-.435-1.264-.26-.279-.649-.463-1.181-.463H7.957c-.53 0-.92.184-1.18.463Z" />
</G>
</Svg>
);

export default IconModalCopy;
Loading