From bd12aa0e4de7c44e3d6b192f0590d22b2cdd70c5 Mon Sep 17 00:00:00 2001 From: Tristan Yang Date: Tue, 27 Dec 2022 21:29:41 +0800 Subject: [PATCH] feat: auto delete message logic --- src/app/slices/message.ts | 3 +- src/assets/icons/timer.svg | 3 + src/common/DayjsSetting.ts | 2 + src/common/component/Message/ExpireTimer.tsx | 67 ++++++++++++++++++++ src/common/component/Message/index.tsx | 46 ++++++++------ src/common/component/Message/styled.tsx | 3 + 6 files changed, 105 insertions(+), 19 deletions(-) create mode 100644 src/assets/icons/timer.svg create mode 100644 src/common/component/Message/ExpireTimer.tsx diff --git a/src/app/slices/message.ts b/src/app/slices/message.ts index 296c7fa9..bc74fffb 100644 --- a/src/app/slices/message.ts +++ b/src/app/slices/message.ts @@ -12,6 +12,7 @@ export interface MessagePayload { content: string; expires_in?: number | null; properties?: { + local_id?: number; content_type: string; size: number; }; @@ -75,7 +76,7 @@ const messageSlice = createSlice({ data.thumbnail = content; } } - state[mid] = data; + state[mid] = { ...state[mid], ...data }; }, removeMessage(state, action: PayloadAction) { const mids = Array.isArray(action.payload) ? action.payload : [action.payload]; diff --git a/src/assets/icons/timer.svg b/src/assets/icons/timer.svg new file mode 100644 index 00000000..235fece6 --- /dev/null +++ b/src/assets/icons/timer.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common/DayjsSetting.ts b/src/common/DayjsSetting.ts index d02df52e..ce2ddeed 100644 --- a/src/common/DayjsSetting.ts +++ b/src/common/DayjsSetting.ts @@ -1,11 +1,13 @@ import dayjs from "dayjs"; import "dayjs/locale/zh-cn"; import "dayjs/locale/ja"; +import duration from "dayjs/plugin/duration"; import relativeTime from "dayjs/plugin/relativeTime"; import isToday from "dayjs/plugin/isToday"; import isYesterday from "dayjs/plugin/isYesterday"; // import i18n from '../i18n'; +dayjs.extend(duration); dayjs.extend(relativeTime); dayjs.extend(isToday); dayjs.extend(isYesterday); diff --git a/src/common/component/Message/ExpireTimer.tsx b/src/common/component/Message/ExpireTimer.tsx new file mode 100644 index 00000000..e144eb2f --- /dev/null +++ b/src/common/component/Message/ExpireTimer.tsx @@ -0,0 +1,67 @@ +import dayjs from 'dayjs'; +import { FC, useEffect, useState } from 'react'; +import { useDispatch } from 'react-redux'; +import { removeMessage } from '../../../app/slices/message'; +import IconTimer from '../../../assets/icons/timer.svg'; +type Props = { + mid: number; + expires_in: number; + create_at: number; +}; + +const ExpireTimer: FC = ({ mid, create_at, expires_in }) => { + const [countdown, setCountdown] = useState(); + const dispatch = useDispatch(); + useEffect(() => { + if (expires_in > 0 && create_at > 0) { + const expire_time = create_at + expires_in * 1000; + if (dayjs().isAfter(new Date(expire_time))) { + // 已过期,立即删除 + dispatch(removeMessage(mid)); + // dispatch() + + } else { + // 倒计时 + setCountdown(Math.floor((expire_time - new Date().getTime()) / 1000)); + } + } + }, [expires_in, create_at, mid]); + useEffect(() => { + let timer = 0; + if (typeof countdown !== "undefined") { + if (countdown > 0) { + timer = window.setTimeout(() => { + setCountdown(prev => { + const _prev = prev ?? 0; + return _prev - 1; + }); + }, 1000); + } else { + // 倒计时结束 + console.log("countdown over", mid, countdown); + + dispatch(removeMessage(mid)); + } + } + return () => { + if (typeof countdown !== "undefined") { + clearTimeout(timer); + } + }; + }, [countdown, mid]); + if (!countdown) return null; + const duration = dayjs.duration(countdown * 1000); + const day = duration.days() !== 0 ? `${duration.days()} day` : ""; + const hours = duration.hours() !== 0 ? `${duration.hours().toString().padStart(2, '0')}:` : ""; + const minutes = duration.minutes() !== 0 ? `${duration.minutes().toString().padStart(2, '0')}:` : ""; + const formatted_countdown = `${day} ${hours}${minutes}${duration.seconds().toString().padStart(2, '0')}`; + return ( +
+ + {formatted_countdown} +
+ ); +}; + +export default ExpireTimer; + diff --git a/src/common/component/Message/index.tsx b/src/common/component/Message/index.tsx index 628f24bc..fe95dcbe 100644 --- a/src/common/component/Message/index.tsx +++ b/src/common/component/Message/index.tsx @@ -16,6 +16,7 @@ import ContextMenu from "./ContextMenu"; import useContextMenu from "../../hook/useContextMenu"; import usePinMessage from "../../hook/usePinMessage"; import { useAppSelector } from "../../../app/store"; +import ExpireTimer from "./ExpireTimer"; interface IProps { readOnly?: boolean; @@ -50,6 +51,19 @@ const Message: FC = ({ setEdit((prev) => !prev); }; + useEffect(() => { + if (!read) { + // 标记已读 + const data = + context == "user" + ? { users: [{ uid: +contextId, mid }] } + : { groups: [{ gid: +contextId, mid }] }; + if (updateReadIndex) { + updateReadIndex(data); + } + } + }, [mid, read]); + const { reply_mid, from_uid: fromUid, @@ -60,20 +74,10 @@ const Message: FC = ({ download, content_type = "text/plain", edited, - properties + properties, + expires_in = 0 } = message; - useEffect(() => { - if (!read) { - const data = - context == "user" - ? { users: [{ uid: +contextId, mid }] } - : { groups: [{ gid: +contextId, mid }] }; - if (updateReadIndex) { - updateReadIndex(data); - } - } - }, [mid, read]); const reactions = reactionMessageData[mid]; const currUser = usersData[fromUid || 0]; @@ -85,15 +89,15 @@ const Message: FC = ({ const pinInfo = getPinInfo(mid); // return null; const _key = properties?.local_id || mid; + const showExpire = (expires_in ?? 0) > 0; return ( = ({ >
{currUser?.name || "Deleted User"} @@ -160,6 +163,13 @@ const Message: FC = ({
+ {showExpire && ( + + )} {!edit && !readOnly && (