diff --git a/src/app/listener.middleware/handler.footprint.js b/src/app/listener.middleware/handler.footprint.js index d4a2b8a9..b6acbc6d 100644 --- a/src/app/listener.middleware/handler.footprint.js +++ b/src/app/listener.middleware/handler.footprint.js @@ -18,6 +18,12 @@ export default async function handler({ operation, data = {}, payload }) { await table.setItem("afterMid", afterMid); } break; + case "updateMute": + { + await table.setItem("muteUsers", data.muteUsers || {}); + await table.setItem("muteChannels", data.muteChannels || {}); + } + break; case "updateReadChannels": { await table.setItem("readChannels", data.readChannels); diff --git a/src/app/services/contact.js b/src/app/services/contact.js index 85f71b4d..7ebbac5b 100644 --- a/src/app/services/contact.js +++ b/src/app/services/contact.js @@ -3,6 +3,7 @@ import { createApi } from "@reduxjs/toolkit/query/react"; import { KEY_UID } from "../config"; import baseQuery from "./base.query"; import { resetAuthData, setUid } from "../slices/auth.data"; +import { updateMute } from "../slices/footprint"; import { fullfillContacts } from "../slices/contacts"; import BASE_URL, { ContentTypes } from "../config"; import { onMessageSendStarted } from "./handlers"; @@ -47,6 +48,21 @@ export const contactApi = createApi({ deleteContact: builder.query({ query: (uid) => ({ url: `/admin/user/${uid}`, method: "DELETE" }), }), + updateMuteSetting: builder.mutation({ + query: (data) => ({ + url: `/user/mute`, + method: "POST", + body: data, + }), + async onQueryStarted(data, { dispatch, queryFulfilled }) { + try { + await queryFulfilled; + dispatch(updateMute(data)); + } catch (error) { + console.log("update mute failed", error); + } + }, + }), updateAvatar: builder.mutation({ query: (data) => ({ headers: { @@ -89,6 +105,7 @@ export const contactApi = createApi({ }); export const { + useUpdateMuteSettingMutation, useLazyDeleteContactQuery, useUpdateInfoMutation, useUpdateAvatarMutation, diff --git a/src/app/slices/footprint.js b/src/app/slices/footprint.js index 18e23b1d..3a57c3aa 100644 --- a/src/app/slices/footprint.js +++ b/src/app/slices/footprint.js @@ -4,6 +4,8 @@ const initialState = { afterMid: 0, readUsers: {}, readChannels: {}, + muteUsers: {}, + muteChannels: {}, }; const footprintSlice = createSlice({ name: "footprint", @@ -18,8 +20,17 @@ const footprintSlice = createSlice({ afterMid = 0, readUsers = {}, readChannels = {}, + muteUsers = {}, + muteChannels = {}, } = action.payload; - return { usersVersion, afterMid, readUsers, readChannels }; + return { + usersVersion, + afterMid, + readUsers, + readChannels, + muteUsers, + muteChannels, + }; }, updateUsersVersion(state, action) { const usersVersion = action.payload; @@ -29,6 +40,47 @@ const footprintSlice = createSlice({ const afterMid = action.payload; state.afterMid = afterMid; }, + updateMute(state, action) { + const payload = action.payload || {}; + Object.keys(payload).forEach((key) => { + switch (key) { + case "remove_users": + { + const uids = payload.remove_users; + uids.forEach((id) => { + delete state.muteUsers[id]; + }); + } + break; + case "remove_groups": + { + const gids = payload.remove_groups; + gids.forEach((id) => { + delete state.muteChannels[id]; + }); + } + break; + case "add_users": + { + const mutes = payload.add_users; + mutes.forEach(({ uid, expired_in = null }) => { + state.muteUsers[uid] = { expired_in }; + }); + } + break; + case "add_groups": + { + const mutes = payload.add_groups; + mutes.forEach(({ gid, expired_in = null }) => { + state.muteChannels[gid] = { expired_in }; + }); + } + break; + default: + break; + } + }); + }, updateReadUsers(state, action) { const reads = action.payload || []; if (reads.length == 0) return; @@ -52,5 +104,6 @@ export const { updateUsersVersion, updateReadChannels, updateReadUsers, + updateMute, } = footprintSlice.actions; export default footprintSlice.reducer; diff --git a/src/common/hook/useStreaming/index.js b/src/common/hook/useStreaming/index.js index 908d648e..7a2f4b9d 100644 --- a/src/common/hook/useStreaming/index.js +++ b/src/common/hook/useStreaming/index.js @@ -19,6 +19,7 @@ import { updateUsersVersion, updateReadChannels, updateReadUsers, + updateMute, } from "../../../app/slices/footprint"; import { updateUsersByLogs, @@ -151,9 +152,45 @@ export default function useStreaming() { case "user_settings_changed": { console.log("users settings"); - const { read_index_users = [], read_index_groups = [] } = data; - dispatch(updateReadChannels(read_index_groups)); - dispatch(updateReadUsers(read_index_users)); + Object.keys(data).forEach((key) => { + switch (key) { + case "read_index_groups": + dispatch(updateReadChannels(data[key])); + break; + case "read_index_users": + dispatch(updateReadUsers(data[key])); + break; + case "add_mute_users": + case "mute_users": + case "add_mute_groups": + case "mute_groups": + { + const arr = data[key]; + if (arr && arr.length) { + const _key = key.endsWith("users") + ? "add_users" + : "add_groups"; + dispatch(updateMute({ [_key]: arr })); + } + } + break; + case "remove_mute_users": + case "remove_mute_groups": + { + const arr = data[key]; + if (arr && arr.length) { + const _key = key.endsWith("users") + ? "remove_users" + : "remove_groups"; + dispatch(updateMute({ [_key]: arr })); + } + } + break; + + default: + break; + } + }); } break; case "users_state": diff --git a/src/routes/chat/ChannelList/NavItem.js b/src/routes/chat/ChannelList/NavItem.js index a9b29f1f..3a53dd12 100644 --- a/src/routes/chat/ChannelList/NavItem.js +++ b/src/routes/chat/ChannelList/NavItem.js @@ -8,6 +8,7 @@ import ContextMenu from "../../../common/component/ContextMenu"; import Tooltip from "../../../common/component/Tooltip"; // import { useDebounce} from "rooks"; import { useReadMessageMutation } from "../../../app/services/message"; +import { useUpdateMuteSettingMutation } from "../../../app/services/contact"; import StyledLink from "./styled"; import { toggleChannelSetting } from "../../../app/slices/ui"; @@ -17,6 +18,7 @@ import { getUnreadCount } from "../utils"; const NavItem = ({ id, setFiles, toggleRemoveConfirm }) => { const dispatch = useDispatch(); const navigate = useNavigate(); + const [muteChannel] = useUpdateMuteSettingMutation(); const [updateReadIndex] = useReadMessageMutation(); const { @@ -25,17 +27,23 @@ const NavItem = ({ id, setFiles, toggleRemoveConfirm }) => { handleContextMenuEvent, hideContextMenu, } = useContextMenu(); - const { channel, mids, messageData, readIndex, loginUid } = useSelector( - (store) => { - return { - channel: store.channels.byId[id], - mids: store.channelMessage[id], - messageData: store.message, - loginUid: store.authData.uid, - readIndex: store.footprint.readChannels[id], - }; - } - ); + const { + channel, + mids, + messageData, + readIndex, + muted, + loginUid, + } = useSelector((store) => { + return { + channel: store.channels.byId[id], + mids: store.channelMessage[id], + messageData: store.message, + loginUid: store.authData.uid, + readIndex: store.footprint.readChannels[id], + muted: store.footprint.muteChannels[id], + }; + }); const handleChannelSetting = (evt) => { evt.preventDefault(); evt.stopPropagation(); @@ -66,8 +74,15 @@ const NavItem = ({ id, setFiles, toggleRemoveConfirm }) => { updateReadIndex(param); } }; + const handleMute = () => { + const data = muted + ? { remove_groups: [id] } + : { add_groups: [{ gid: id }] }; + muteChannel(data); + }; const { is_public, name } = channel; const unreads = getUnreadCount({ mids, messageData, readIndex, loginUid }); + const isDot = muted || unreads > 99; return ( { handler: handleReadAll, }, { - title: "Mute", + title: muted ? "Unmute" : "Mute", + handler: handleMute, }, { title: "Notification Settings", @@ -123,8 +139,8 @@ const NavItem = ({ id, setFiles, toggleRemoveConfirm }) => { {unreads > 0 && ( - 99 ? "dot" : ""}`}> - {unreads > 99 ? null : unreads} + + {isDot ? null : unreads} )}