diff --git a/app/src/features/authentication/login/services/apiLogin.ts b/app/src/features/authentication/login/services/apiLogin.ts
index bef7de74..93975f0d 100644
--- a/app/src/features/authentication/login/services/apiLogin.ts
+++ b/app/src/features/authentication/login/services/apiLogin.ts
@@ -1,5 +1,6 @@
import { API_URL } from "@constants";
import { User } from "../LoginForm/LoginForm";
+import toast from "react-hot-toast";
export async function login(user: User) {
const res = await fetch(`${API_URL}/auth/login`, {
@@ -15,6 +16,7 @@ export async function login(user: User) {
const data = await res.json();
if (data.status !== "success") {
+ toast.error(data.message);
throw new Error(data.message);
}
diff --git a/app/src/features/chats/ChatBody.tsx b/app/src/features/chats/ChatBody.tsx
index 5fd1969f..9a09fb76 100644
--- a/app/src/features/chats/ChatBody.tsx
+++ b/app/src/features/chats/ChatBody.tsx
@@ -81,6 +81,7 @@ function ChatBody() {
chatType={chat?.type as "private" | "group" | "channel"}
sender={members.find((member) => member._id === data.senderId)}
numberOfMembers={chat?.numberOfMembers}
+ isAppropriate={data.isAppropriate}
>
@@ -96,6 +97,7 @@ function ChatBody() {
chatType="channel"
sender={members.find((member) => member._id === data.senderId)}
numberOfMembers={chat?.numberOfMembers}
+ isAppropriate={data.isAppropriate}
>
diff --git a/app/src/features/chats/ChatInputIcons.tsx b/app/src/features/chats/ChatInputIcons.tsx
index 4d234f58..85c5ca6e 100644
--- a/app/src/features/chats/ChatInputIcons.tsx
+++ b/app/src/features/chats/ChatInputIcons.tsx
@@ -20,7 +20,7 @@ function ChatInputIcons() {
setInput,
file,
setFile,
- setIsFilePreviewOpen,
+ setIsFilePreviewOpen
} = useContext(ChatInputContext);
const toggleShowEmojies = () => {
@@ -28,11 +28,17 @@ function ChatInputIcons() {
};
const handleKeyDown = (e: React.KeyboardEvent) => {
+ setIsEmojiSelectorOpen(false);
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
handleSubmit(e as unknown as Event);
}
};
+ const handleSetFile = (file: File) => {
+ setIsEmojiSelectorOpen(false);
+ setFile(file);
+ setIsFilePreviewOpen(true);
+ };
return (
<>
@@ -50,7 +56,7 @@ function ChatInputIcons() {
>
diff --git a/app/src/features/chats/ChatItem.tsx b/app/src/features/chats/ChatItem.tsx
index 960b6ebf..246a515e 100644
--- a/app/src/features/chats/ChatItem.tsx
+++ b/app/src/features/chats/ChatItem.tsx
@@ -59,7 +59,7 @@ type ChatItemProps = {
const ChatItem = ({
chat: { _id, lastMessage, name, photo },
- onClick,
+ onClick
}: ChatItemProps) => {
const navigate = useNavigate();
@@ -86,7 +86,7 @@ const ChatItem = ({
{new Date(timestamp).toLocaleTimeString("en-US", {
hour: "2-digit",
- minute: "2-digit",
+ minute: "2-digit"
}) || "No messages"}
diff --git a/app/src/features/chats/MessageContent.tsx b/app/src/features/chats/MessageContent.tsx
index ce977bc8..873a0d1d 100644
--- a/app/src/features/chats/MessageContent.tsx
+++ b/app/src/features/chats/MessageContent.tsx
@@ -36,6 +36,7 @@ function MessageContent() {
media,
contentType,
chatType,
+ isAppropriate,
} = useMessageContext();
const { searchTerm, searchResults } = useAppSelector((state) => state.search);
@@ -49,6 +50,10 @@ function MessageContent() {
media && (contentType === "GIF" || contentType === "sticker");
const isFile = media && !(contentType === "GIF" || contentType === "sticker");
+ const filteredContent =
+ (chatType === "group" || chatType === "channel") && !isAppropriate
+ ? content
+ : "🚫️ This mesaage has unappropriate content";
return (
diff --git a/app/src/features/chats/Topbar.tsx b/app/src/features/chats/Topbar.tsx
index 768adcb5..fe6c4599 100644
--- a/app/src/features/chats/Topbar.tsx
+++ b/app/src/features/chats/Topbar.tsx
@@ -123,6 +123,7 @@ function Topbar() {
const { chatId } = useParams<{ chatId: string }>();
const userId = useAppSelector((state) => state.user.userInfo.id);
const chats = useAppSelector((state) => state.chats.chats);
+ const dispatch = useAppDispatch();
const { activeThread } = useAppSelector((state) => state.channelsThreads);
const [isSearching, setIsSearching] = useState(false);
const [isCollapsed, setIsCollapsed] = useState(false);
@@ -153,28 +154,31 @@ function Topbar() {
const { removeFromBlockList } = useBlock();
const { setIsRightSideBarOpen } = useRightSideBarContext();
- const dispatch = useAppDispatch();
- const handleOpenRightSideBar = useCallback(() => {
- if (!chat) return;
- if (chat?.type === "private") setIsRightSideBarOpen(false);
- else {
- dispatch(
- updateSideBarView({
- redirect:
- chat?.type === "group"
- ? sideBarPages.GROUP_INFO
- : sideBarPages.CHANNEL_INFO,
- data: { type: "right" }
- })
- );
- }
- }, [chat, dispatch, setIsRightSideBarOpen]);
+ const cachedOpenRightSideBar = useCallback(
+ function handleOpenRightSideBar() {
+ if (!chat) return;
+ if (chat?.type === "private") setIsRightSideBarOpen(false);
+ else {
+ dispatch(
+ updateSideBarView({
+ redirect:
+ chat?.type === "group"
+ ? sideBarPages.GROUP_INFO
+ : sideBarPages.CHANNEL_INFO,
+ data: { type: "right" }
+ })
+ );
+ }
+ },
+ [chat, dispatch, setIsRightSideBarOpen]
+ );
useEffect(() => {
if (!chatId) return;
- handleOpenRightSideBar();
- }, [chatId, handleOpenRightSideBar]);
+ cachedOpenRightSideBar();
+ }, [chatId, chat, cachedOpenRightSideBar]);
+
let image;
let lastSeen;
diff --git a/app/src/features/chats/audio/VoiceRecorder.tsx b/app/src/features/chats/audio/VoiceRecorder.tsx
index 1106f081..fe34f6eb 100644
--- a/app/src/features/chats/audio/VoiceRecorder.tsx
+++ b/app/src/features/chats/audio/VoiceRecorder.tsx
@@ -82,9 +82,29 @@ const VoiceRecorder: React.FC = ({
lastModified: Date.now()
});
audioChunks.current = [];
- uploadVoiceNote(file);
+ try {
+ uploadVoiceNote(file, {
+ onSuccess: (url) => {
+ console.log("File uploaded successfully:", url);
+ handleSendMessage("", chatId, url);
+ },
+ onError: (error) => {
+ console.error("Error uploading file:", error);
+ }
+ });
+ } catch (error) {
+ console.error("Unexpected error while sending file:", error);
+ }
+ setIsRecording("idle");
}
- }, [isRecording, recordingMimeType, uploadVoiceNote]);
+ }, [
+ isRecording,
+ recordingMimeType,
+ setIsRecording,
+ uploadVoiceNote,
+ handleSendMessage,
+ chatId
+ ]);
useEffect(() => {
if (voiceNoteURL && isRecording === "pause") {
diff --git a/app/src/features/chats/contexts/MessageProvider.tsx b/app/src/features/chats/contexts/MessageProvider.tsx
index af1983f4..65894c02 100644
--- a/app/src/features/chats/contexts/MessageProvider.tsx
+++ b/app/src/features/chats/contexts/MessageProvider.tsx
@@ -18,6 +18,7 @@ type ProviderProps = {
chatType: string;
sender?: Member;
numberOfMembers?: number;
+ isAppropriate: boolean;
};
function MessageProvider({
@@ -26,13 +27,21 @@ function MessageProvider({
chatType,
sender,
numberOfMembers,
+ isAppropriate,
}: ProviderProps) {
const userId = useAppSelector((state) => state.user.userInfo.id);
const isMine = userId === data.senderId;
return (
{children}
diff --git a/app/src/features/chats/hooks/useChatInput.ts b/app/src/features/chats/hooks/useChatInput.ts
index aade6945..e85de81a 100644
--- a/app/src/features/chats/hooks/useChatInput.ts
+++ b/app/src/features/chats/hooks/useChatInput.ts
@@ -32,7 +32,7 @@ function useChatInput() {
e.preventDefault();
setIsEmojiSelectorOpen(false);
if (isRecording !== "idle") return;
- handleSendMessage(input, chatId, voiceNoteName);
+ handleSendMessage(input, chatId, voiceNoteName, "audio");
dispatch(clearActiveMessage());
setInput("");
};
diff --git a/app/src/features/chats/media/MediaUploadComponent.tsx b/app/src/features/chats/media/MediaUploadComponent.tsx
index 62693c8c..be0e1e3e 100644
--- a/app/src/features/chats/media/MediaUploadComponent.tsx
+++ b/app/src/features/chats/media/MediaUploadComponent.tsx
@@ -10,7 +10,7 @@ const InvisibleButton = styled.div`
`;
interface ChildProps {
file: File | null;
- setFile: React.Dispatch>;
+ setFile: (file: File) => void;
setIsFilePreviewOpen: React.Dispatch>;
}
diff --git a/app/src/features/chats/media/hooks/useUploadMedia.tsx b/app/src/features/chats/media/hooks/useUploadMedia.tsx
index 59b44545..c33afd6a 100644
--- a/app/src/features/chats/media/hooks/useUploadMedia.tsx
+++ b/app/src/features/chats/media/hooks/useUploadMedia.tsx
@@ -1,5 +1,6 @@
import { useMutation } from "@tanstack/react-query";
import { uploadFile } from "../services/uploadfileHandler";
+import toast from "react-hot-toast";
export function useUploadMedia() {
const { mutate, isPending, error, data } = useMutation({
@@ -7,9 +8,11 @@ export function useUploadMedia() {
onError: (error) => {
const errorMessage =
error instanceof Error ? error.message : "An unknown error occurred";
+ toast.error("Error uploading file: " + errorMessage);
console.error("Error uploading file:", errorMessage);
},
onSuccess: (data) => {
+ toast.success("File uploaded successfully");
console.log("File uploaded successfully:", data);
},
});
diff --git a/app/src/features/chats/services/apiFetchNextPage.ts b/app/src/features/chats/services/apiFetchNextPage.ts
index eeb00466..a2c2fba1 100644
--- a/app/src/features/chats/services/apiFetchNextPage.ts
+++ b/app/src/features/chats/services/apiFetchNextPage.ts
@@ -25,10 +25,6 @@ async function apiFetchNextPage({
throw new Error(data.message);
}
- if (data.status !== "success") {
- throw new Error(data.message);
- }
-
return { messages: data.data.messages, nextPage: data.data.nextPage };
}
diff --git a/app/src/types/messages.ts b/app/src/types/messages.ts
index 767a1edd..8a4329a2 100644
--- a/app/src/types/messages.ts
+++ b/app/src/types/messages.ts
@@ -30,6 +30,7 @@ export interface MessageInterface {
parentMessageId: string | null;
status: MessageStatus;
+ isAppropriate: boolean;
media?: string;
diff --git a/app/src/utils/helpers.ts b/app/src/utils/helpers.ts
index 652cdc2f..91150cad 100644
--- a/app/src/utils/helpers.ts
+++ b/app/src/utils/helpers.ts
@@ -24,7 +24,7 @@ function getElapsedTime(timestamp: string): string {
} else if (minutes > 0) {
return `${minutes}m`;
} else {
- return `${seconds}s`;
+ return `${Math.max(seconds, 1)}s`;
}
}