diff --git a/src/app/services/contact.js b/src/app/services/contact.js index 4bccc014..6ab8d753 100644 --- a/src/app/services/contact.js +++ b/src/app/services/contact.js @@ -1,118 +1,126 @@ -import { createApi } from '@reduxjs/toolkit/query/react'; +import { createApi } from "@reduxjs/toolkit/query/react"; // import toast from "react-hot-toast"; -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'; +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"; export const contactApi = createApi({ - reducerPath: 'contactApi', - baseQuery, - endpoints: (builder) => ({ - getContacts: builder.query({ - query: () => ({ url: `user` }), - transformResponse: (data) => { - return data.map((user) => { - const avatar = - user.avatar_updated_at == 0 - ? '' - : `${BASE_URL}/resource/avatar?uid=${user.uid}&t=${user.avatar_updated_at}`; - user.avatar = avatar; - return user; - }); - }, - async onQueryStarted(data, { dispatch, queryFulfilled }) { - const local_uid = localStorage.getItem(KEY_UID); - try { - const { data: contacts } = await queryFulfilled; - const matchedUser = contacts.find((c) => c.uid == local_uid); - console.log('wtf', contacts, matchedUser); - if (!matchedUser) { - // 用户已注销或被禁用 - console.log('no matched user, redirect to login'); - dispatch(resetAuthData()); - } else { - const markedContacts = contacts.map((u) => { - return u.uid == matchedUser.uid ? { ...u, online: true } : u; - }); - dispatch(setUid(matchedUser.uid)); - dispatch(fullfillContacts(markedContacts)); - } - } catch { - console.log('get contact list error'); - } - } + reducerPath: "contactApi", + baseQuery, + endpoints: (builder) => ({ + getContacts: builder.query({ + query: () => ({ url: `user` }), + transformResponse: (data) => { + return data.map((user) => { + const avatar = + user.avatar_updated_at == 0 + ? "" + : `${BASE_URL}/resource/avatar?uid=${user.uid}&t=${user.avatar_updated_at}`; + user.avatar = avatar; + return user; + }); + }, + async onQueryStarted(data, { dispatch, queryFulfilled }) { + const local_uid = localStorage.getItem(KEY_UID); + try { + const { data: contacts } = await queryFulfilled; + const matchedUser = contacts.find((c) => c.uid == local_uid); + console.log("wtf", contacts, matchedUser); + if (!matchedUser) { + // 用户已注销或被禁用 + console.log("no matched user, redirect to login"); + dispatch(resetAuthData()); + } else { + const markedContacts = contacts.map((u) => { + return u.uid == matchedUser.uid ? { ...u, online: true } : u; + }); + dispatch(setUid(matchedUser.uid)); + dispatch(fullfillContacts(markedContacts)); + } + } catch { + console.log("get contact list error"); + } + }, + }), + deleteContact: builder.query({ + query: (uid) => ({ url: `/admin/user/${uid}`, method: "DELETE" }), + }), + updateContact: builder.mutation({ + query: ({ id, ...rest }) => ({ + url: `/admin/user/${id}`, + body: rest, + method: "PUT", + }), + }), + 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: { + "content-type": "image/png", + }, + url: `user/avatar`, + method: "POST", + body: data, + }), + }), + updateInfo: builder.mutation({ + query: (data) => ({ + url: `user`, + method: "PUT", + body: data, + }), + }), + register: builder.mutation({ + query: (data) => ({ + url: `user/register`, + method: "POST", + body: data, + }), + }), + sendMsg: builder.mutation({ + query: ({ id, content, type = "text", properties = "" }) => ({ + headers: { + "content-type": ContentTypes[type], + "X-Properties": properties + ? btoa(unescape(encodeURIComponent(JSON.stringify(properties)))) + : "", + }, + url: `user/${id}/send`, + method: "POST", + body: type == "file" ? JSON.stringify(content) : content, + }), + async onQueryStarted(param1, param2) { + await onMessageSendStarted.call(this, param1, param2, "user"); + }, + }), }), - 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: { - 'content-type': 'image/png' - }, - url: `user/avatar`, - method: 'POST', - body: data - }) - }), - updateInfo: builder.mutation({ - query: (data) => ({ - url: `user`, - method: 'PUT', - body: data - }) - }), - register: builder.mutation({ - query: (data) => ({ - url: `user/register`, - method: 'POST', - body: data - }) - }), - sendMsg: builder.mutation({ - query: ({ id, content, type = 'text', properties = '' }) => ({ - headers: { - 'content-type': ContentTypes[type], - 'X-Properties': properties - ? btoa(unescape(encodeURIComponent(JSON.stringify(properties)))) - : '' - }, - url: `user/${id}/send`, - method: 'POST', - body: type == 'file' ? JSON.stringify(content) : content - }), - async onQueryStarted(param1, param2) { - await onMessageSendStarted.call(this, param1, param2, 'user'); - } - }) - }) }); export const { - useUpdateMuteSettingMutation, - useLazyDeleteContactQuery, - useUpdateInfoMutation, - useUpdateAvatarMutation, - useGetContactsQuery, - useLazyGetContactsQuery, - useSendMsgMutation, - useRegisterMutation + useUpdateContactMutation, + useUpdateMuteSettingMutation, + useLazyDeleteContactQuery, + useUpdateInfoMutation, + useUpdateAvatarMutation, + useGetContactsQuery, + useLazyGetContactsQuery, + useSendMsgMutation, + useRegisterMutation, } = contactApi; diff --git a/src/common/component/ManageMembers.js b/src/common/component/ManageMembers.js index 4ecc5b08..7769bb4a 100644 --- a/src/common/component/ManageMembers.js +++ b/src/common/component/ManageMembers.js @@ -6,13 +6,18 @@ import { hideAll } from "tippy.js"; import { useSelector } from "react-redux"; import toast from "react-hot-toast"; import useCopy from "../hook/useCopy"; -import { useLazyDeleteContactQuery } from "../../app/services/contact"; +import { + useLazyDeleteContactQuery, + useUpdateContactMutation, +} from "../../app/services/contact"; import { useRemoveMembersMutation } from "../../app/services/channel"; import Contact from "./Contact"; import StyledMenu from "./styled/Menu"; import InviteLink from "./InviteLink"; import moreIcon from "../../assets/icons/more.svg?url"; import IconOwner from "../../assets/icons/owner.svg"; +import IconArrowDown from "../../assets/icons/arrow.down.svg"; +import IconCheck from "../../assets/icons/check.sign.svg"; const StyledWrapper = styled.section` display: flex; flex-direction: column; @@ -81,6 +86,16 @@ const StyledWrapper = styled.section` line-height: 18px; text-align: right; color: #616161; + display: flex; + align-items: center; + gap: 4px; + .icon { + cursor: pointer; + } + /* override */ + .menu { + min-width: 120px; + } } .opts { position: relative; @@ -107,6 +122,10 @@ export default function ManageMembers({ cid = null }) { }; }); const [copied, copy] = useCopy(); + const [ + updateContact, + { isSuccess: updateSuccess }, + ] = useUpdateContactMutation(); const [ removeUser, { isSuccess: removeSuccess }, @@ -121,7 +140,7 @@ export default function ManageMembers({ cid = null }) { }; useEffect(() => { if (removeSuccess) { - toast.success("delete successfully"); + toast.success("Delete Successfully"); } }, [removeSuccess]); useEffect(() => { @@ -131,13 +150,23 @@ export default function ManageMembers({ cid = null }) { }, [copied]); useEffect(() => { if (removeMemberSuccess) { - toast.success("remove member successfully"); + toast.success("Remove Member successfully"); } }, [removeMemberSuccess]); + useEffect(() => { + if (updateSuccess) { + toast.success("Update Successfully"); + } + }, [updateSuccess]); const handleCopy = (str) => { copy(str); hideAll(); }; + const handleToggleRole = ({ ignore = false, uid = null, isAdmin = true }) => { + hideAll(); + if (ignore) return; + updateContact({ id: uid, is_admin: isAdmin }); + }; const channel = channels.byId[cid] ?? null; const uids = channel ? channel.is_public @@ -159,6 +188,8 @@ export default function ManageMembers({ cid = null }) { {uids.map((uid) => { const { name, email, is_admin } = contacts.byId[uid]; const owner = channel && channel.owner == uid; + const switchRoleVisible = loginUser.is_admin && loginUser.uid !== uid; + const dotsVisible = email || loginUser?.is_admin; return (
  • @@ -172,40 +203,77 @@ export default function ManageMembers({ cid = null }) {
    - {is_admin ? "Admin" : cid ? "Member" : "User"} + {is_admin ? "Admin" : "User"} + {switchRoleVisible && ( + +
  • + Admin + {is_admin && } +
  • +
  • + User + {!is_admin && } +
  • + + } + > + + + )} - - {email && ( -
  • - Copy Email -
  • - )} - {/*
  • Mute
  • */} - {/*
  • Change Nickname
  • */} - {/*
  • Ban
  • */} - {loginUser?.is_admin && ( -
  • - Remove -
  • - )} - - } - > -
    - dots icon -
    -
    + {dotsVisible && ( + + {email && ( +
  • + Copy Email +
  • + )} + {/*
  • Mute
  • */} + {/*
  • Change Nickname
  • */} + {/*
  • Ban
  • */} + {loginUser?.is_admin && ( +
  • + Remove +
  • + )} + + } + > +
    + dots icon +
    +
    + )} );