diff --git a/package-lock.json b/package-lock.json index cb55cab..ad96b1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "react-i18next": "^15.6.1", "react-virtualized": "^9.22.6", "tailwind-merge": "^3.3.1", + "use-long-press": "^3.3.0", "zustand": "^5.0.6" }, "devDependencies": { @@ -6620,6 +6621,15 @@ } } }, + "node_modules/use-long-press": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/use-long-press/-/use-long-press-3.3.0.tgz", + "integrity": "sha512-Yedz46ILxsb1BTS6kUzpV/wyEZPUlJDq+8Oat0LP1eOZQHbS887baJHJbIGENqCo8wTKNxmoTHLdY8lU/e+wvw==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/use-sidecar": { "version": "1.1.3", "license": "MIT", diff --git a/package.json b/package.json index e60c54b..c519036 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "react-i18next": "^15.6.1", "react-virtualized": "^9.22.6", "tailwind-merge": "^3.3.1", + "use-long-press": "^3.3.0", "zustand": "^5.0.6" }, "devDependencies": { diff --git a/src/app/[name]/chat/page.tsx b/src/app/[name]/chat/page.tsx index 84fd312..25a6020 100644 --- a/src/app/[name]/chat/page.tsx +++ b/src/app/[name]/chat/page.tsx @@ -1,13 +1,18 @@ 'use client'; +import Button from '@/components/button/PrimaryButton'; import SearchBox from '@/components/input/SearchBox'; +import BlurredOverlayContainer from '@/components/overlays/BlurredOverlayContainer'; import { ScrollArea } from '@/components/ui/scrollarea'; import { Radar2, Trash } from 'iconsax-react'; import { Check, CheckCheck } from 'lucide-react'; import Image from 'next/image'; import Link from 'next/link'; -import React, { useState } from 'react' +import React, { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next'; +import { motion } from 'framer-motion'; +import { LongPressCallbackMeta, LongPressReactEvents, useLongPress } from 'use-long-press' +import { useRouter } from 'next/navigation'; type Props = object @@ -25,10 +30,15 @@ type ChatEntryModel = { function ChatIndex({ }: Props) { const { t } = useTranslation('chat'); + const { t: tDeleteModal } = useTranslation('common', { + keyPrefix: 'DeleteChatModal' + }); const [search, setSearch] = useState(''); - const [selectedChats, setSelectedChats] = useState([]); + const [selectedChats, setSelectedChats] = useState>([]); + const [deleteModalVisible, setDeleteModalVisible] = useState(false); + const router = useRouter(); - const chatsList: Array = [ + const [chatsList, setChatList] = useState>([ { id: '0', username: 'علی مصلحی', @@ -89,66 +99,152 @@ function ChatIndex({ }: Props) { seen: false } }, - ] + ]); + + const toggleDeleteModalVisiblity = (e?: React.MouseEvent) => { + e?.preventDefault(); + e?.stopPropagation(); + setDeleteModalVisible((state) => { + if (state) { + setSelectedChats([]); + } + return !state; + }); + }; + + const deleteSelectedChats = () => { + if (setSelectedChats.length <= 0) return; + setChatList((prev) => { + return prev.filter(x => !selectedChats.includes(x.id)); + }) + toggleDeleteModalVisiblity(); + } + + const longPressCallback = useCallback((e: LongPressReactEvents, ctx: LongPressCallbackMeta) => { + e.preventDefault(); + if (!ctx?.context) return; + const id = String(ctx.context); + setSelectedChats((prev) => { + if (prev.includes(id)) return prev.filter(x => x !== id); + return [...prev, id] + }) + }, []) + + const longPressCancelledCallback = useCallback((id: string, reason?: string) => { + if (reason && reason !== 'cancelled-by-release') return; + if (selectedChats.length > 0) { + setSelectedChats((prev) => { + if (prev.includes(id)) return prev.filter(x => x !== id); + return [...prev, id] + }) + } else { + router.push(`chat/${id}`) + } + }, [router, selectedChats.length]) + + const longPress = useLongPress(longPressCallback, { + onCancel: (event, ctx) => longPressCancelledCallback(String(ctx.context), String(ctx.reason)), + threshold: 500, // In milliseconds + captureEvent: true, // Event won't get cleared after React finish processing it + cancelOnMovement: 100, // Square side size (in pixels) inside which movement won't cancel long press + cancelOutsideElement: true, // Cancel long press when moved mouse / pointer outside element while pressing + }); return (
setSearch(e.target.value)} /> - + + : + + + + }
-
+
    {chatsList.map((chat, i) => { return ( -
  • - - {`${chat.username}'s - -
    - {chat.username} - {chat.update?.content} -
    - -
    - {chat.update?.time} - {chat.update?.count !== undefined && chat.update?.count > 0 ? - - {chat.update?.count} - +
  • +
    + { + selectedChats.includes(chat.id) ? +
    + +
    : - chat.update?.seen !== undefined && - chat.update?.seen ? - : - - } + - + alt={`${chat.username}'s avatar`} + unoptimized + /> + } + +
    + +
    +
    {chat.username}
    +

    {chat.update?.content}

    +
    + +
    + {chat.update?.time} + {chat.update?.count !== undefined && chat.update?.count > 0 ? + + {chat.update?.count} + + : + chat.update?.seen !== undefined && + chat.update?.seen ? + : + + } + +
  • ) })}
+ + +
+ +

+ {tDeleteModal('Heading')} +

+

+ {tDeleteModal('Description')} +

+
+ + +
+
+
+
) } diff --git a/src/locales/fa/chat.json b/src/locales/fa/chat.json index 78e2c9b..5df18d2 100644 --- a/src/locales/fa/chat.json +++ b/src/locales/fa/chat.json @@ -1,4 +1,7 @@ { + "InputSearch": { + "Placeholder": "جستجو" + }, "InputMessage": { "AriaLabelBy": "ارسال پیام", "Placeholder": "پیام خود را بنویسید" diff --git a/src/locales/fa/common.json b/src/locales/fa/common.json index 54e01e9..54b0657 100644 --- a/src/locales/fa/common.json +++ b/src/locales/fa/common.json @@ -29,5 +29,11 @@ "ButtonOk": "بله خارج می‌شوم", "ButtonCancel": "منصرف شدم" }, + "DeleteChatModal": { + "Heading": "پاک کردن چت", + "Description": "آیا از پاک کردن {count} چت اطمینان دارید؟", + "ButtonOk": "بله پاک میکنم", + "ButtonCancel": "منصرف شدم" + }, "SearchPlaceholder": "جستجو" } \ No newline at end of file