refactor: useSelector
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { memo, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { shallowEqual, useDispatch } from "react-redux";
|
||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||
import Tippy from "@tippyjs/react";
|
||||
|
||||
@@ -29,15 +29,10 @@ function ChannelChat({ cid = 0, dropFiles = [] }: Props) {
|
||||
const { pathname } = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { userIds, data, loginUser, visibleAside } = useAppSelector((store) => {
|
||||
return {
|
||||
loginUser: store.authData.user,
|
||||
userIds: store.users.ids,
|
||||
data: store.channels.byId[cid],
|
||||
visibleAside: store.footprint.channelAsides[cid]
|
||||
};
|
||||
});
|
||||
const loginUser = useAppSelector((store) => store.authData.user, shallowEqual);
|
||||
const visibleAside = useAppSelector((store) => store.footprint.channelAsides[cid], shallowEqual);
|
||||
const userIds = useAppSelector((store) => store.users.ids, shallowEqual);
|
||||
const data = useAppSelector((store) => store.channels.byId[cid], shallowEqual);
|
||||
useEffect(() => {
|
||||
if (!data) {
|
||||
// channel不存在了 回首页
|
||||
|
||||
@@ -10,6 +10,7 @@ import FavIcon from "@/assets/icons/bookmark.svg";
|
||||
import FavList from "../FavList";
|
||||
import Layout from "../Layout";
|
||||
import VoiceChat from "../VoiceChat";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
type Props = {
|
||||
uid: number;
|
||||
@@ -17,11 +18,7 @@ type Props = {
|
||||
};
|
||||
const DMChat: FC<Props> = ({ uid = 0, dropFiles }) => {
|
||||
const navigate = useNavigate();
|
||||
const { currUser } = useAppSelector((store) => {
|
||||
return {
|
||||
currUser: store.users.byId[uid]
|
||||
};
|
||||
});
|
||||
const currUser = useAppSelector((store) => store.users.byId[uid], shallowEqual);
|
||||
useEffect(() => {
|
||||
if (!currUser) {
|
||||
// user不存在了 回首页
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { shallowEqual, useDispatch } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { BASE_ORIGIN } from "@/app/config";
|
||||
@@ -17,7 +17,7 @@ const GuestBlankPlaceholder = () => {
|
||||
const dispatch = useDispatch();
|
||||
const { clearLocalData } = useLogout();
|
||||
const navigateTo = useNavigate();
|
||||
const serverName = useAppSelector((store) => store.server.name);
|
||||
const serverName = useAppSelector((store) => store.server.name, shallowEqual);
|
||||
const handleSignIn = () => {
|
||||
dispatch(resetAuthData());
|
||||
clearLocalData();
|
||||
|
||||
@@ -5,17 +5,14 @@ import { useAppSelector } from "@/app/store";
|
||||
import ChannelIcon from "@/components/ChannelIcon";
|
||||
import GoBackNav from "@/components/GoBackNav";
|
||||
import Layout from "../Layout";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
type Props = {
|
||||
cid?: number;
|
||||
};
|
||||
export default function GuestChannelChat({ cid = 0 }: Props) {
|
||||
// const { t } = useTranslation("chat");
|
||||
const { data } = useAppSelector((store) => {
|
||||
return {
|
||||
data: store.channels.byId[cid]
|
||||
};
|
||||
});
|
||||
const data = useAppSelector((store) => store.channels.byId[cid], shallowEqual);
|
||||
if (!data) return null;
|
||||
const { name, description, is_public } = data;
|
||||
return (
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useAppSelector } from "@/app/store";
|
||||
import Avatar from "@/components/Avatar";
|
||||
import { fromNowTime } from "@/utils";
|
||||
import { renderPreviewMessage } from "../../chat/utils";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
interface IProps {
|
||||
id: number;
|
||||
@@ -19,13 +20,9 @@ const Session: FC<IProps> = ({ id, mid }) => {
|
||||
mid: number;
|
||||
is_public: boolean;
|
||||
}>();
|
||||
const { messageData, userData, channelData } = useAppSelector((store) => {
|
||||
return {
|
||||
messageData: store.message,
|
||||
userData: store.users.byId,
|
||||
channelData: store.channels.byId
|
||||
};
|
||||
});
|
||||
const messageData = useAppSelector((store) => store.message, shallowEqual);
|
||||
const userData = useAppSelector((store) => store.users.byId, shallowEqual);
|
||||
const channelData = useAppSelector((store) => store.channels.byId, shallowEqual);
|
||||
|
||||
useEffect(() => {
|
||||
const tmp = channelData[id];
|
||||
|
||||
@@ -3,6 +3,7 @@ import { FC, useEffect, useState } from "react";
|
||||
import { useAppSelector } from "@/app/store";
|
||||
import LoginTip from "../Layout/LoginTip";
|
||||
import Session from "./Session";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
export interface ChatSession {
|
||||
key: string;
|
||||
@@ -12,17 +13,12 @@ export interface ChatSession {
|
||||
type Props = {};
|
||||
const SessionList: FC<Props> = () => {
|
||||
const [sessions, setSessions] = useState<ChatSession[]>([]);
|
||||
const { channelIDs, readChannels, readUsers, channelMessage, userMessage, loginUid } =
|
||||
useAppSelector((store) => {
|
||||
return {
|
||||
loginUid: store.authData.user?.uid,
|
||||
channelIDs: store.channels.ids,
|
||||
userMessage: store.userMessage.byId,
|
||||
channelMessage: store.channelMessage,
|
||||
readChannels: store.footprint.readChannels,
|
||||
readUsers: store.footprint.readUsers
|
||||
};
|
||||
});
|
||||
const readChannels = useAppSelector((store) => store.footprint.readChannels, shallowEqual);
|
||||
const readUsers = useAppSelector((store) => store.footprint.readUsers, shallowEqual);
|
||||
const loginUid = useAppSelector((store) => store.authData.user?.uid, shallowEqual);
|
||||
const channelIDs = useAppSelector((store) => store.channels.ids, shallowEqual);
|
||||
const channelMessage = useAppSelector((store) => store.channelMessage, shallowEqual);
|
||||
const userMessage = useAppSelector((store) => store.userMessage.byId, shallowEqual);
|
||||
|
||||
useEffect(() => {
|
||||
const cSessions = channelIDs.map((id) => {
|
||||
|
||||
@@ -5,6 +5,7 @@ import IconBlock from "@/assets/icons/block.svg";
|
||||
import { useUpdateContactStatusMutation } from "../../../app/services/user";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
import { ContactAction } from "../../../types/user";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
type Props = {
|
||||
uid: number;
|
||||
@@ -13,12 +14,11 @@ type Props = {
|
||||
const AddContactTip = (props: Props) => {
|
||||
const { t } = useTranslation("chat");
|
||||
const [updateContactStatus] = useUpdateContactStatusMutation();
|
||||
const { targetUser, enableContact } = useAppSelector((store) => {
|
||||
return {
|
||||
targetUser: store.users.byId[props.uid],
|
||||
enableContact: store.server.contact_verification_enable
|
||||
};
|
||||
});
|
||||
const enableContact = useAppSelector(
|
||||
(store) => store.server.contact_verification_enable,
|
||||
shallowEqual
|
||||
);
|
||||
const targetUser = useAppSelector((store) => store.users.byId[props.uid], shallowEqual);
|
||||
const handleContactStatus = (action: ContactAction) => {
|
||||
updateContactStatus({ target_uid: props.uid, action });
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { shallowEqual, useDispatch } from "react-redux";
|
||||
import clsx from "clsx";
|
||||
|
||||
import { updateCallInfo } from "@/app/slices/voice";
|
||||
@@ -114,17 +114,12 @@ type Props = {
|
||||
const DMVoice = ({ uid }: Props) => {
|
||||
const dispatch = useDispatch();
|
||||
// const { t } = useTranslation("chat");
|
||||
const {
|
||||
voice: { callingFrom, callingTo, voicingMembers },
|
||||
userData,
|
||||
loginUser
|
||||
} = useAppSelector((store) => {
|
||||
return {
|
||||
voice: store.voice,
|
||||
userData: store.users.byId,
|
||||
loginUser: store.authData.user
|
||||
};
|
||||
});
|
||||
const loginUid = useAppSelector((store) => store.authData.user?.uid, shallowEqual);
|
||||
const userData = useAppSelector((store) => store.users.byId, shallowEqual);
|
||||
const { callingFrom, callingTo, voicingMembers } = useAppSelector(
|
||||
(store) => store.voice,
|
||||
shallowEqual
|
||||
);
|
||||
const { leave, joinVoice, joining } = useVoice({ id: callingTo, context: "dm" });
|
||||
useEffect(() => {
|
||||
const ids = voicingMembers.ids;
|
||||
@@ -136,7 +131,7 @@ const DMVoice = ({ uid }: Props) => {
|
||||
|
||||
const { name: fromUsername, avatar: fromAvatar } = userData[callingFrom];
|
||||
const { name: toUsername, avatar: toAvatar, uid: toUid } = userData[callingTo];
|
||||
const sendByMe = loginUser?.uid !== toUid;
|
||||
const sendByMe = loginUid !== toUid;
|
||||
const onlyToSelf = voicingMembers.ids.length == 1 && voicingMembers.ids[0] == callingTo;
|
||||
const handleCancel = () => {
|
||||
console.log("cancel");
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useAppSelector } from "../../../app/store";
|
||||
import { ChatContext } from "../../../types/common";
|
||||
import getUnreadCount from "../utils";
|
||||
import { memo } from "react";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
type Props = {
|
||||
context: ChatContext;
|
||||
@@ -15,17 +16,17 @@ type Props = {
|
||||
// linear-gradient(135deg,_#3C8CE7_0%,_#00EAFF_100%)
|
||||
const NewMessageBottomTip = ({ context, id, scrollToBottom }: Props) => {
|
||||
const { t } = useTranslation("chat");
|
||||
const { readIndex, mids, messageData, loginUid } = useAppSelector((store) => {
|
||||
return {
|
||||
readIndex:
|
||||
context == "channel" ? store.footprint.readChannels[id] : store.footprint.readUsers[id],
|
||||
mids: context == "dm" ? store.userMessage.byId[id] : store.channelMessage[id],
|
||||
selects: store.ui.selectMessages[`${context}_${id}`],
|
||||
loginUid: store.authData.user?.uid ?? 0,
|
||||
data: context == "channel" ? store.channels.byId[id] : undefined,
|
||||
messageData: store.message || {}
|
||||
};
|
||||
});
|
||||
const readIndex = useAppSelector(
|
||||
(store) =>
|
||||
context == "channel" ? store.footprint.readChannels[id] : store.footprint.readUsers[id],
|
||||
shallowEqual
|
||||
);
|
||||
const mids = useAppSelector(
|
||||
(store) => (context == "dm" ? store.userMessage.byId[id] : store.channelMessage[id]),
|
||||
shallowEqual
|
||||
);
|
||||
const loginUid = useAppSelector((store) => store.authData.user?.uid ?? 0, shallowEqual);
|
||||
const messageData = useAppSelector((store) => store.message ?? {}, shallowEqual);
|
||||
const { unreads = 0 } = getUnreadCount({
|
||||
mids,
|
||||
readIndex,
|
||||
|
||||
@@ -13,6 +13,7 @@ import IconBookmark from "@/assets/icons/bookmark.svg";
|
||||
import IconClose from "@/assets/icons/close.circle.svg";
|
||||
import IconDelete from "@/assets/icons/delete.svg";
|
||||
import IconForward from "@/assets/icons/forward.svg";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
type Props = {
|
||||
context: ChatContext;
|
||||
@@ -22,7 +23,7 @@ const Operations: FC<Props> = ({ context, id }) => {
|
||||
const [deleteModalVisible, setDeleteModalVisible] = useState(false);
|
||||
const { canDelete } = useDeleteMessage();
|
||||
const { addFavorite } = useFavMessage({});
|
||||
const mids = useAppSelector((store) => store.ui.selectMessages[`${context}_${id}`]);
|
||||
const mids = useAppSelector((store) => store.ui.selectMessages[`${context}_${id}`], shallowEqual);
|
||||
const [forwardModalVisible, setForwardModalVisible] = useState(false);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import clsx from "clsx";
|
||||
|
||||
import { useAppSelector } from "@/app/store";
|
||||
import EditIcon from "@/assets/icons/edit.svg";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
type ChannelHeaderProps = {
|
||||
cid: number;
|
||||
@@ -12,19 +13,15 @@ type ChannelHeaderProps = {
|
||||
const ChannelHeader = ({ cid }: ChannelHeaderProps) => {
|
||||
const { pathname } = useLocation();
|
||||
const { t } = useTranslation("chat");
|
||||
const { data, loginUser } = useAppSelector((store) => {
|
||||
return {
|
||||
loginUser: store.authData.user,
|
||||
data: store.channels.byId[cid]
|
||||
};
|
||||
});
|
||||
const isAdmin = useAppSelector((store) => store.authData.user?.is_admin, shallowEqual);
|
||||
const data = useAppSelector((store) => store.channels.byId[cid], shallowEqual);
|
||||
return (
|
||||
<div className="pt-14 px-1 md:px-0 flex flex-col items-start gap-2">
|
||||
<h2 className="font-bold text-4xl dark:text-white">
|
||||
{t("welcome_channel", { name: data?.name })}
|
||||
</h2>
|
||||
<p className="text-gray-600 dark:text-gray-300">{t("welcome_desc", { name: data?.name })} </p>
|
||||
{loginUser?.is_admin && (
|
||||
{isAdmin && (
|
||||
<NavLink
|
||||
to={`/setting/channel/${cid}/overview?f=${pathname}`}
|
||||
className="flex items-center gap-1 bg-clip-text text-fill-transparent bg-gradient-to-r from-blue-500 to-primary-400 "
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { shallowEqual, useDispatch } from "react-redux";
|
||||
import { Virtuoso, VirtuosoHandle } from "react-virtuoso";
|
||||
// import clsx from 'clsx';
|
||||
// import { useTranslation } from 'react-i18next';
|
||||
import { useDebounce } from "rooks";
|
||||
|
||||
import { useLazyLoadMoreMessagesQuery, useReadMessageMutation } from "@/app/services/message";
|
||||
@@ -30,24 +28,26 @@ const VirtualMessageFeed = ({ context, id }: Props) => {
|
||||
const vList = useRef<VirtuosoHandle | null>(null);
|
||||
const [updateReadIndex] = useReadMessageMutation();
|
||||
const updateReadDebounced = useDebounce(updateReadIndex, 300);
|
||||
const {
|
||||
historyMid = "",
|
||||
mids = [],
|
||||
selects,
|
||||
messageData,
|
||||
loginUser,
|
||||
footprint
|
||||
} = useAppSelector((store) => {
|
||||
return {
|
||||
historyMid:
|
||||
context == "dm" ? store.footprint.historyUsers[id] : store.footprint.historyChannels[id],
|
||||
mids: context == "dm" ? store.userMessage.byId[id] : store.channelMessage[id],
|
||||
selects: store.ui.selectMessages[`${context}_${id}`],
|
||||
footprint: store.footprint,
|
||||
loginUser: store.authData.user,
|
||||
messageData: store.message || {}
|
||||
};
|
||||
});
|
||||
const historyMid = useAppSelector(
|
||||
(store) =>
|
||||
context == "dm"
|
||||
? store.footprint.historyUsers[id] ?? ""
|
||||
: store.footprint.historyChannels[id] ?? "",
|
||||
shallowEqual
|
||||
);
|
||||
const mids = useAppSelector(
|
||||
(store) =>
|
||||
context == "dm" ? store.userMessage.byId[id] ?? [] : store.channelMessage[id] ?? [],
|
||||
shallowEqual
|
||||
);
|
||||
const selects = useAppSelector(
|
||||
(store) => store.ui.selectMessages[`${context}_${id}`],
|
||||
shallowEqual
|
||||
);
|
||||
const messageData = useAppSelector((store) => store.message || {}, shallowEqual);
|
||||
const loginUid = useAppSelector((store) => store.authData.user?.uid, shallowEqual);
|
||||
const readChannels = useAppSelector((store) => store.footprint.readChannels, shallowEqual);
|
||||
const readUsers = useAppSelector((store) => store.footprint.readUsers, shallowEqual);
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess && historyData) {
|
||||
@@ -102,7 +102,7 @@ const VirtualMessageFeed = ({ context, id }: Props) => {
|
||||
const handleBottomStateChange = (bottom: boolean) => {
|
||||
setAtBottom(bottom);
|
||||
};
|
||||
const readIndex = context == "channel" ? footprint.readChannels[id] : footprint.readUsers[id];
|
||||
const readIndex = context == "channel" ? readChannels[id] : readUsers[id];
|
||||
return (
|
||||
<>
|
||||
<Virtuoso
|
||||
@@ -132,7 +132,7 @@ const VirtualMessageFeed = ({ context, id }: Props) => {
|
||||
if (!curr) return <div className="w-full h-[1px] invisible"></div>;
|
||||
const isFirst = idx == 0;
|
||||
const prev = isFirst ? null : messageData[mids[idx - 1]];
|
||||
const read = curr?.from_uid == loginUser?.uid || mid <= readIndex;
|
||||
const read = curr?.from_uid == loginUid || mid <= readIndex;
|
||||
return renderMessageFragment({
|
||||
selectMode: !!selects,
|
||||
updateReadIndex: updateReadDebounced,
|
||||
|
||||
@@ -19,6 +19,7 @@ import LoginTip from "./LoginTip";
|
||||
import Operations from "./Operations";
|
||||
import VirtualMessageFeed from "./VirtualMessageFeed";
|
||||
import { platform } from "@/utils";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
interface Props {
|
||||
readonly?: boolean;
|
||||
@@ -45,15 +46,13 @@ const Layout: FC<Props> = ({
|
||||
const { reachLimit } = useLicense();
|
||||
const { addStageFile } = useUploadFile({ context, id: to });
|
||||
const messagesContainer = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { selects, channelsData, usersData, inputMode } = useAppSelector((store) => {
|
||||
return {
|
||||
inputMode: store.ui.inputMode,
|
||||
selects: store.ui.selectMessages[`${context}_${to}`],
|
||||
channelsData: store.channels.byId,
|
||||
usersData: store.users.byId
|
||||
};
|
||||
});
|
||||
const inputMode = useAppSelector((store) => store.ui.inputMode, shallowEqual);
|
||||
const selects = useAppSelector(
|
||||
(store) => store.ui.selectMessages[`${context}_${to}`],
|
||||
shallowEqual
|
||||
);
|
||||
const channelsData = useAppSelector((store) => store.channels.byId, shallowEqual);
|
||||
const usersData = useAppSelector((store) => store.users.byId, shallowEqual);
|
||||
const [{ isActive }, drop] = useDrop(
|
||||
() => ({
|
||||
accept: [NativeTypes.FILE],
|
||||
|
||||
@@ -15,6 +15,7 @@ import Tooltip from "../../components/Tooltip";
|
||||
import User from "../../components/User";
|
||||
import { useVoice } from "../../components/Voice";
|
||||
import { ChatContext } from "../../types/common";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
type Props = {
|
||||
id: number;
|
||||
@@ -34,15 +35,11 @@ const RTCWidget = ({ id, context = "channel" }: Props) => {
|
||||
startShareScreen,
|
||||
stopShareScreen
|
||||
} = useVoice({ context, id });
|
||||
const { callFrom, callTo, loginUser, channelData, userData } = useAppSelector((store) => {
|
||||
return {
|
||||
callFrom: store.voice.callingFrom,
|
||||
callTo: store.voice.callingTo,
|
||||
userData: store.users.byId,
|
||||
channelData: store.channels.byId,
|
||||
loginUser: store.authData.user
|
||||
};
|
||||
});
|
||||
const loginUser = useAppSelector((store) => store.authData.user, shallowEqual);
|
||||
const callFrom = useAppSelector((store) => store.voice.callingFrom, shallowEqual);
|
||||
const callTo = useAppSelector((store) => store.voice.callingTo, shallowEqual);
|
||||
const channelData = useAppSelector((store) => store.channels.byId, shallowEqual);
|
||||
const userData = useAppSelector((store) => store.channels.byId, shallowEqual);
|
||||
if (!loginUser || !voicingInfo || joining) return null;
|
||||
// const name = voicingInfo.context == "channel" ? channelData[voicingInfo.id]?.name : userData[voicingInfo.id]?.name;
|
||||
// if (!name) return null;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FC, ReactElement } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { shallowEqual, useDispatch } from "react-redux";
|
||||
import { useLocation, useMatch, useNavigate } from "react-router-dom";
|
||||
import Tippy from "@tippyjs/react";
|
||||
|
||||
@@ -51,11 +51,10 @@ const SessionContextMenu: FC<Props> = ({
|
||||
const dispatch = useDispatch();
|
||||
const navigateTo = useNavigate();
|
||||
const { pathname } = useLocation();
|
||||
const { channelMuted } = useAppSelector((store) => {
|
||||
return {
|
||||
channelMuted: context == "channel" ? store.footprint.muteChannels[id] : false
|
||||
};
|
||||
});
|
||||
const channelMuted = useAppSelector(
|
||||
(store) => (context == "channel" ? store.footprint.muteChannels[id] : false),
|
||||
shallowEqual
|
||||
);
|
||||
|
||||
const handleChannelSetting = () => {
|
||||
navigateTo(`/setting/channel/${id}/overview?f=${pathname}`);
|
||||
|
||||
@@ -17,6 +17,7 @@ import IconMute from "@/assets/icons/mute.svg";
|
||||
import IconVoicing from "@/assets/icons/voicing.svg";
|
||||
import getUnreadCount, { renderPreviewMessage } from "../utils";
|
||||
import ContextMenu from "./ContextMenu";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
interface IProps {
|
||||
type?: ChatContext;
|
||||
@@ -67,31 +68,25 @@ const Session: FC<IProps> = ({
|
||||
mid: number;
|
||||
is_public: boolean;
|
||||
}>();
|
||||
const {
|
||||
callingFrom,
|
||||
callingTo,
|
||||
messageData,
|
||||
userData,
|
||||
channelData,
|
||||
readIndex,
|
||||
loginUid,
|
||||
mids,
|
||||
muted,
|
||||
voiceList
|
||||
} = useAppSelector((store) => {
|
||||
return {
|
||||
callingFrom: store.voice.callingFrom,
|
||||
callingTo: store.voice.callingTo,
|
||||
voiceList: store.voice.list,
|
||||
mids: type == "dm" ? store.userMessage.byId[id] : store.channelMessage[id],
|
||||
loginUid: store.authData.user?.uid || 0,
|
||||
readIndex: type == "dm" ? store.footprint.readUsers[id] : store.footprint.readChannels[id],
|
||||
messageData: store.message,
|
||||
userData: store.users.byId,
|
||||
channelData: store.channels.byId,
|
||||
muted: type == "dm" ? store.footprint.muteUsers[id] : store.footprint.muteChannels[id]
|
||||
};
|
||||
});
|
||||
const loginUid = useAppSelector((store) => store.authData.user?.uid || 0, shallowEqual);
|
||||
const callingFrom = useAppSelector((store) => store.voice.callingFrom, shallowEqual);
|
||||
const callingTo = useAppSelector((store) => store.voice.callingTo, shallowEqual);
|
||||
const voiceList = useAppSelector((store) => store.voice.list, shallowEqual);
|
||||
const mids = useAppSelector(
|
||||
(store) => (type == "dm" ? store.userMessage.byId[id] : store.channelMessage[id]),
|
||||
shallowEqual
|
||||
);
|
||||
const muted = useAppSelector(
|
||||
(store) => (type == "dm" ? store.footprint.muteUsers[id] : store.footprint.muteChannels[id]),
|
||||
shallowEqual
|
||||
);
|
||||
const readIndex = useAppSelector(
|
||||
(store) => (type == "dm" ? store.footprint.readUsers[id] : store.footprint.readChannels[id]),
|
||||
shallowEqual
|
||||
);
|
||||
const messageData = useAppSelector((store) => store.message, shallowEqual);
|
||||
const userData = useAppSelector((store) => store.users.byId, shallowEqual);
|
||||
const channelData = useAppSelector((store) => store.channels.byId, shallowEqual);
|
||||
|
||||
useEffect(() => {
|
||||
const tmp = type == "dm" ? userData[id] : channelData[id];
|
||||
|
||||
@@ -7,6 +7,7 @@ import { PinChatTargetChannel, PinChatTargetUser } from "@/types/sse";
|
||||
import InviteModal from "@/components/InviteModal";
|
||||
import DeleteChannelConfirmModal from "../../settingChannel/DeleteConfirmModal";
|
||||
import Session from "./Session";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
export interface ChatSession {
|
||||
type: ChatContext;
|
||||
@@ -23,19 +24,14 @@ const SessionList: FC<Props> = ({ tempSession }) => {
|
||||
const [inviteChannelId, setInviteChannelId] = useState<number>();
|
||||
const [sessions, setSessions] = useState<ChatSession[]>([]);
|
||||
const [pinSessions, setPinSessions] = useState<ChatSession[]>([]);
|
||||
const { pins, channelIDs, DMs, readChannels, readUsers, channelMessage, userMessage, loginUid } =
|
||||
useAppSelector((store) => {
|
||||
return {
|
||||
pins: store.footprint.pinChats,
|
||||
loginUid: store.authData.user?.uid,
|
||||
channelIDs: store.channels.ids,
|
||||
DMs: store.userMessage.ids,
|
||||
userMessage: store.userMessage.byId,
|
||||
channelMessage: store.channelMessage,
|
||||
readChannels: store.footprint.readChannels,
|
||||
readUsers: store.footprint.readUsers
|
||||
};
|
||||
});
|
||||
const readChannels = useAppSelector((store) => store.footprint.readChannels, shallowEqual);
|
||||
const readUsers = useAppSelector((store) => store.footprint.readUsers, shallowEqual);
|
||||
const loginUid = useAppSelector((store) => store.authData.user?.uid, shallowEqual);
|
||||
const channelIDs = useAppSelector((store) => store.channels.ids, shallowEqual);
|
||||
const DMs = useAppSelector((store) => store.userMessage.ids, shallowEqual);
|
||||
const pins = useAppSelector((store) => store.footprint.pinChats, shallowEqual);
|
||||
const userMessage = useAppSelector((store) => store.userMessage.byId, shallowEqual);
|
||||
const channelMessage = useAppSelector((store) => store.channelMessage, shallowEqual);
|
||||
|
||||
useEffect(() => {
|
||||
// const pinDMs=
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { shallowEqual, useDispatch } from "react-redux";
|
||||
import clsx from "clsx";
|
||||
|
||||
import { updateChannelVisibleAside } from "@/app/slices/footprint";
|
||||
@@ -21,12 +21,8 @@ type Props = {
|
||||
|
||||
const VoiceManagement = ({ id, context, info }: Props) => {
|
||||
const dispatch = useDispatch();
|
||||
const { userData, voicingMembers } = useAppSelector((store) => {
|
||||
return {
|
||||
userData: store.users.byId,
|
||||
voicingMembers: store.voice.voicingMembers
|
||||
};
|
||||
});
|
||||
const userMap = useAppSelector((store) => store.users.byId, shallowEqual);
|
||||
const voicingMembers = useAppSelector((store) => store.voice.voicingMembers, shallowEqual);
|
||||
useEffect(() => {
|
||||
const ids = voicingMembers.ids;
|
||||
ids.forEach((id) => {
|
||||
@@ -66,7 +62,7 @@ const VoiceManagement = ({ id, context, info }: Props) => {
|
||||
<div className="w-full h-full py-2 flex flex-col">
|
||||
<ul className="flex grow flex-col">
|
||||
{members.map((uid) => {
|
||||
const curr = userData[uid];
|
||||
const curr = userMap[uid];
|
||||
if (!curr) return null;
|
||||
const { muted, speakingVolume = 0 } = membersData[uid];
|
||||
const speaking = speakingVolume > 50;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
// import React from 'react';
|
||||
// import Tippy from '@tippyjs/react';
|
||||
// import { useState } from 'react';
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { shallowEqual, useDispatch } from "react-redux";
|
||||
|
||||
import { useGetAgoraStatusQuery } from "@/app/services/server";
|
||||
import { updateChannelVisibleAside, updateDMVisibleAside } from "@/app/slices/footprint";
|
||||
@@ -23,14 +20,12 @@ const isIframe = isInIframe();
|
||||
const VoiceChat = ({ id, context = "channel" }: Props) => {
|
||||
const { joinVoice, joined, joining = false, joinedAtThisContext } = useVoice({ id, context });
|
||||
const dispatch = useDispatch();
|
||||
const { loginUser, voiceList, visibleAside } = useAppSelector((store) => {
|
||||
return {
|
||||
loginUser: store.authData.user,
|
||||
contextData: context == "channel" ? store.channels.byId[id] : store.users.byId[id],
|
||||
voiceList: store.voice.list,
|
||||
visibleAside: context == "channel" ? store.footprint.channelAsides[id] : null
|
||||
};
|
||||
});
|
||||
const loginUid = useAppSelector((store) => store.authData.user?.uid ?? 0, shallowEqual);
|
||||
const visibleAside = useAppSelector(
|
||||
(store) => (context == "channel" ? store.footprint.channelAsides[id] : null),
|
||||
shallowEqual
|
||||
);
|
||||
const voiceList = useAppSelector((store) => store.voice.list, shallowEqual);
|
||||
const { data: enabled } = useGetAgoraStatusQuery();
|
||||
const { t } = useTranslation("chat");
|
||||
const toggleDashboard = () => {
|
||||
@@ -53,14 +48,14 @@ const VoiceChat = ({ id, context = "channel" }: Props) => {
|
||||
dispatch(context == "channel" ? updateChannelVisibleAside(data) : updateDMVisibleAside(data));
|
||||
// 实时显示calling box
|
||||
if (!joinedAtThisContext && context == "dm") {
|
||||
dispatch(updateCallInfo({ from: loginUser?.uid ?? 0, to: id, calling: false }));
|
||||
dispatch(updateCallInfo({ from: loginUid, to: id, calling: false }));
|
||||
}
|
||||
};
|
||||
const handleInIframe = () => {
|
||||
// todo
|
||||
toast.error("Voice is not supported in iframe");
|
||||
};
|
||||
if (!loginUser || !enabled) return null;
|
||||
if (loginUid == 0 || !enabled) return null;
|
||||
const visible = visibleAside == "voice";
|
||||
const memberCount = voiceList.find((v) => v.context == context && v.id == id)?.memberCount ?? 0;
|
||||
const badgeClass = `absolute -top-2 -right-2 w-4 h-4 rounded-full bg-primary-400 text-white `;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { MouseEvent, useEffect, useState } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { shallowEqual, useDispatch } from "react-redux";
|
||||
import clsx from "clsx";
|
||||
|
||||
import Operations from "@/components/Voice/Operations";
|
||||
@@ -21,13 +21,12 @@ type Props = {
|
||||
const VoiceFullscreen = ({ id, context }: Props) => {
|
||||
const dispatch = useDispatch();
|
||||
const [speakingUid, setSpeakingUid] = useState(0);
|
||||
const { name, userData, voicingMembers } = useAppSelector((store) => {
|
||||
return {
|
||||
userData: store.users.byId,
|
||||
voicingMembers: store.voice.voicingMembers,
|
||||
name: context == "channel" ? store.channels.byId[id].name : store.users.byId[id].name
|
||||
};
|
||||
});
|
||||
const name = useAppSelector(
|
||||
(store) => (context == "channel" ? store.channels.byId[id].name : store.users.byId[id].name),
|
||||
shallowEqual
|
||||
);
|
||||
const userData = useAppSelector((store) => store.users.byId, shallowEqual);
|
||||
const voicingMembers = useAppSelector((store) => store.voice.voicingMembers, shallowEqual);
|
||||
useEffect(() => {
|
||||
const ids = voicingMembers.ids;
|
||||
ids.forEach((id) => {
|
||||
|
||||
+10
-14
@@ -16,6 +16,7 @@ import GuestSessionList from "./GuestSessionList";
|
||||
import RTCWidget from "./RTCWidget";
|
||||
import SessionList from "./SessionList";
|
||||
import VoiceFullscreen from "./VoiceFullscreen";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
function ChatPage() {
|
||||
const isHomePath = useMatch(`/`);
|
||||
@@ -24,21 +25,16 @@ function ChatPage() {
|
||||
const [channelModalVisible, setChannelModalVisible] = useState(false);
|
||||
const [usersModalVisible, setUsersModalVisible] = useState(false);
|
||||
const { channel_id = 0, user_id = 0 } = useParams();
|
||||
const {
|
||||
sessionUids,
|
||||
isGuest,
|
||||
aside = "",
|
||||
callingTo
|
||||
} = useAppSelector((store) => {
|
||||
return {
|
||||
callingTo: store.voice.callingTo,
|
||||
isGuest: store.authData.guest,
|
||||
sessionUids: store.userMessage.ids,
|
||||
aside: channel_id
|
||||
const callingTo = useAppSelector((store) => store.voice.callingTo, shallowEqual);
|
||||
const aside = useAppSelector(
|
||||
(store) =>
|
||||
channel_id
|
||||
? store.footprint.channelAsides[+channel_id]
|
||||
: store.footprint.dmAsides[store.voice.callingTo]
|
||||
};
|
||||
});
|
||||
: store.footprint.dmAsides[store.voice.callingTo],
|
||||
shallowEqual
|
||||
);
|
||||
const isGuest = useAppSelector((store) => store.authData.guest, shallowEqual);
|
||||
const sessionUids = useAppSelector((store) => store.userMessage.ids, shallowEqual);
|
||||
const toggleUsersModalVisible = () => {
|
||||
setUsersModalVisible((prev) => !prev);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// import React from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { shallowEqual, useDispatch } from "react-redux";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import { ContentTypes } from "@/app/config";
|
||||
@@ -97,7 +97,10 @@ export const renderPreviewMessage = (message = null) => {
|
||||
const MessageWrapper = ({ selectMode = false, context, id, mid, divider, children, ...rest }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const selects = useAppSelector((store) => store.ui.selectMessages[`${context}_${id}`]);
|
||||
const selects = useAppSelector(
|
||||
(store) => store.ui.selectMessages[`${context}_${id}`],
|
||||
shallowEqual
|
||||
);
|
||||
const selected = !!(selects && selects.find((s) => s == mid));
|
||||
const toggleSelect = () => {
|
||||
const operation = selected ? "remove" : "add";
|
||||
|
||||
@@ -14,6 +14,7 @@ import IconAudio from "@/assets/icons/file.audio.svg";
|
||||
import IconImage from "@/assets/icons/file.image.svg";
|
||||
import IconUnknown from "@/assets/icons/file.unknown.svg";
|
||||
import IconVideo from "@/assets/icons/file.video.svg";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
type filter = "audio" | "video" | "image" | "";
|
||||
|
||||
@@ -44,13 +45,9 @@ function FavsPage() {
|
||||
filter: "audio"
|
||||
}
|
||||
];
|
||||
const { favorites, channelData, userData } = useAppSelector((store) => {
|
||||
return {
|
||||
favorites: store.favorites,
|
||||
userData: store.users.byId,
|
||||
channelData: store.channels.byId
|
||||
};
|
||||
});
|
||||
const favorites = useAppSelector((store) => store.favorites, shallowEqual);
|
||||
const channelData = useAppSelector((store) => store.channels.byId, shallowEqual);
|
||||
const userData = useAppSelector((store) => store.users.byId, shallowEqual);
|
||||
const handleFilter = (ftr: filter) => {
|
||||
setFilter(ftr);
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@ import FilterChannel from "./Channel";
|
||||
import FilterDate, { Dates } from "./Date";
|
||||
import FilterFrom from "./From";
|
||||
import FilterType, { FileTypes } from "./Type";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
const getClass = (selected: boolean) => {
|
||||
return clsx(
|
||||
@@ -42,9 +43,8 @@ export default function Filter({ filter, updateFilter }) {
|
||||
};
|
||||
toggleFilterVisible(tmp);
|
||||
};
|
||||
const { userMap, channelMap } = useAppSelector((store) => {
|
||||
return { userMap: store.users.byId, channelMap: store.channels.byId };
|
||||
});
|
||||
const userMap = useAppSelector((store) => store.users.byId, shallowEqual);
|
||||
const channelMap = useAppSelector((store) => store.channels.byId, shallowEqual);
|
||||
|
||||
const { from, channel, type, date } = filter;
|
||||
const {
|
||||
|
||||
@@ -9,6 +9,7 @@ import Filter from "./Filter";
|
||||
import Search from "./Search";
|
||||
import View from "./View";
|
||||
import { useLazyGetFilesQuery } from "@/app/services/server";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
const checkFilter = (data, filter, channelMessage) => {
|
||||
let selected = true;
|
||||
@@ -35,11 +36,7 @@ function Files() {
|
||||
const [getFiles, { data }] = useLazyGetFilesQuery();
|
||||
const listContainerRef = useRef<HTMLDivElement>();
|
||||
const [filter, setFilter] = useState({});
|
||||
const { view } = useAppSelector((store) => {
|
||||
return {
|
||||
view: store.ui.fileListView
|
||||
};
|
||||
});
|
||||
const view = useAppSelector((store) => store.ui.fileListView.view, shallowEqual);
|
||||
|
||||
const updateFilter = (data) => {
|
||||
setFilter((prev) => {
|
||||
|
||||
@@ -4,12 +4,14 @@ import { Navigate } from "react-router-dom";
|
||||
import { useLazyGuestLoginQuery } from "../app/services/auth";
|
||||
import { useAppSelector } from "../app/store";
|
||||
import { useEffect } from "react";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
// type Props = {};
|
||||
|
||||
const GuestLogin = () => {
|
||||
const [guestLogin] = useLazyGuestLoginQuery();
|
||||
const { token, guest } = useAppSelector((store) => store.authData);
|
||||
const token = useAppSelector((store) => store.authData.token, shallowEqual);
|
||||
const guest = useAppSelector((store) => store.authData.guest, shallowEqual);
|
||||
useEffect(() => {
|
||||
if (!guest || !token) {
|
||||
guestLogin();
|
||||
|
||||
@@ -6,6 +6,7 @@ import ChatIcon from "@/assets/icons/chat.svg";
|
||||
import SettingIcon from "@/assets/icons/setting.svg";
|
||||
import UserIcon from "@/assets/icons/user.svg";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
// type Props = {}
|
||||
|
||||
@@ -16,17 +17,10 @@ const MobileNavs = () => {
|
||||
const isDMChat = useMatch(`/chat/dm/:user_id`);
|
||||
// const isSettingPage = useMatch(`/setting`);
|
||||
const isChannelChat = useMatch(`/chat/channel/:channel_id`);
|
||||
const {
|
||||
ui: {
|
||||
rememberedNavs: { chat: chatPath, user: userPath }
|
||||
}
|
||||
} = useAppSelector((store) => {
|
||||
return {
|
||||
ui: store.ui,
|
||||
loginUid: store.authData.user?.uid,
|
||||
guest: store.authData.guest
|
||||
};
|
||||
});
|
||||
const { chat: chatPath, user: userPath } = useAppSelector(
|
||||
(store) => store.ui.rememberedNavs,
|
||||
shallowEqual
|
||||
);
|
||||
|
||||
const linkClass = `flex`;
|
||||
const isChatPage = isHomePath || pathname.startsWith("/chat");
|
||||
|
||||
@@ -3,6 +3,7 @@ import { NavLink, useLocation } from "react-router-dom";
|
||||
|
||||
import { useAppSelector } from "@/app/store";
|
||||
import Avatar from "@/components/Avatar";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
interface Props {
|
||||
uid: number;
|
||||
@@ -10,7 +11,7 @@ interface Props {
|
||||
|
||||
const User: FC<Props> = ({ uid }) => {
|
||||
const { pathname } = useLocation();
|
||||
const user = useAppSelector((store) => store.users.byId[uid]);
|
||||
const user = useAppSelector((store) => store.users.byId[uid], shallowEqual);
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { memo, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { shallowEqual, useDispatch } from "react-redux";
|
||||
import { NavLink, Outlet, useLocation, useMatch } from "react-router-dom";
|
||||
|
||||
import { updateRememberedNavs } from "@/app/slices/ui";
|
||||
@@ -28,21 +28,13 @@ function HomePage() {
|
||||
const isHomePath = useMatch(`/`);
|
||||
const isChatHomePath = useMatch(`/chat`);
|
||||
const { pathname } = useLocation();
|
||||
const {
|
||||
roleChanged,
|
||||
loginUser: { uid: loginUid },
|
||||
guest,
|
||||
ui: {
|
||||
rememberedNavs: { chat: chatPath, user: userPath }
|
||||
}
|
||||
} = useAppSelector((store) => {
|
||||
return {
|
||||
ui: store.ui,
|
||||
loginUser: store.authData.user ?? { uid: 0, is_admin: false },
|
||||
guest: store.authData.guest,
|
||||
roleChanged: store.authData.roleChanged
|
||||
};
|
||||
});
|
||||
const roleChanged = useAppSelector((store) => store.authData.roleChanged, shallowEqual);
|
||||
const guest = useAppSelector((store) => store.authData.guest, shallowEqual);
|
||||
const loginUid = useAppSelector((store) => store.authData.user?.uid ?? 0, shallowEqual);
|
||||
const { chat: chatPath, user: userPath } = useAppSelector(
|
||||
(store) => store.ui.rememberedNavs,
|
||||
shallowEqual
|
||||
);
|
||||
// preload basic data
|
||||
const { success } = usePreload();
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { lazy, useEffect } from "react";
|
||||
import { lazy, memo, useEffect } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { Provider } from "react-redux";
|
||||
import { Provider, shallowEqual } from "react-redux";
|
||||
import { HashRouter, Route, Routes } from "react-router-dom";
|
||||
import { isEqual } from "lodash";
|
||||
|
||||
@@ -40,12 +40,8 @@ const HomePage = lazy(() => import("./home"));
|
||||
|
||||
let toastId: string;
|
||||
const PageRoutes = () => {
|
||||
const {
|
||||
ui: { online },
|
||||
version
|
||||
} = useAppSelector((store) => {
|
||||
return { ui: store.ui, version: store.server.version };
|
||||
}, isEqual);
|
||||
const version = useAppSelector((store) => store.server.version, shallowEqual);
|
||||
const online = useAppSelector((store) => store.ui.online, shallowEqual);
|
||||
// 提前获取device token
|
||||
useDeviceToken(vapidKey);
|
||||
// 初始化元信息
|
||||
@@ -296,7 +292,7 @@ const PageRoutes = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default function ReduxRoutes() {
|
||||
function ReduxRoutes() {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
{isElectronContext() && <Electron />}
|
||||
@@ -305,3 +301,4 @@ export default function ReduxRoutes() {
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
export default memo(ReduxRoutes);
|
||||
|
||||
@@ -5,10 +5,11 @@ import { useCheckMagicTokenValidMutation } from "../../app/services/auth";
|
||||
import { useJoinPrivateChannelMutation, useLazyGetChannelQuery } from "../../app/services/channel";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
import StyledButton from "../../components/styled/Button";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
const InvitePrivate = () => {
|
||||
const { channel_id } = useParams();
|
||||
const server = useAppSelector((store) => store.server);
|
||||
const server = useAppSelector((store) => store.server, shallowEqual);
|
||||
const navigateTo = useNavigate();
|
||||
const [joinChannel, { isLoading, data, isSuccess }] = useJoinPrivateChannelMutation();
|
||||
const [fetchChannelInfo, { data: channel, isSuccess: fetchChannelSuccess }] =
|
||||
|
||||
@@ -16,13 +16,14 @@ import IconBack from "@/assets/icons/arrow.left.svg";
|
||||
import MagicLinkLogin from "./MagicLinkLogin";
|
||||
import SignUpLink from "./SignUpLink";
|
||||
import SocialLoginButtons from "./SocialLoginButtons";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
const defaultInput = {
|
||||
email: "",
|
||||
password: ""
|
||||
};
|
||||
export default function LoginPage() {
|
||||
const { name: serverName, logo } = useAppSelector((store) => store.server);
|
||||
const { name: serverName, logo } = useAppSelector((store) => store.server, shallowEqual);
|
||||
const { t } = useTranslation("auth");
|
||||
const { t: ct } = useTranslation();
|
||||
const { data: enableSMTP, isLoading: loadingSMTPStatus } = useGetSMTPStatusQuery();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { FC, useEffect, useRef, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { shallowEqual, useDispatch } from "react-redux";
|
||||
import { useWizard } from "react-use-wizard";
|
||||
|
||||
import { useLoginMutation } from "@/app/services/auth";
|
||||
@@ -18,7 +18,7 @@ const AdminAccount: FC<Props> = ({ serverName }) => {
|
||||
const { t } = useTranslation("welcome", { keyPrefix: "onboarding" });
|
||||
const { nextStep } = useWizard();
|
||||
const formRef = useRef<HTMLFormElement | undefined>();
|
||||
const loggedIn = useAppSelector((store) => !!store.authData.token);
|
||||
const loggedIn = useAppSelector((store) => !!store.authData.token, shallowEqual);
|
||||
const dispatch = useDispatch();
|
||||
const [createAdmin, { isLoading: isSigningUp, isError: signUpError, isSuccess: signUpOk }] =
|
||||
useCreateAdminMutation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ChangeEvent, FormEvent, useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { shallowEqual, useDispatch } from "react-redux";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
|
||||
import BASE_URL, { KEY_LOCAL_MAGIC_TOKEN } from "@/app/config";
|
||||
@@ -32,7 +32,7 @@ interface AuthForm {
|
||||
export default function Register() {
|
||||
let [searchParams] = useSearchParams(new URLSearchParams(location.search));
|
||||
const ctx = searchParams.get("ctx");
|
||||
const serverName = useAppSelector((store) => store.server.name);
|
||||
const serverName = useAppSelector((store) => store.server.name, shallowEqual);
|
||||
const { t } = useTranslation("auth");
|
||||
const { t: ct } = useTranslation();
|
||||
const [sendRegMagicLink, { isLoading: signingUp, data, isSuccess }] =
|
||||
|
||||
@@ -42,9 +42,8 @@ export default function Filter({ filter, updateFilter }) {
|
||||
};
|
||||
toggleFilterVisible(tmp);
|
||||
};
|
||||
const { userMap, channelMap } = useAppSelector((store) => {
|
||||
return { userMap: store.users.byId, channelMap: store.channels.byId };
|
||||
});
|
||||
const userMap = useAppSelector((store) => store.users.byId, shallowEqual);
|
||||
const channelMap = useAppSelector((store) => store.channels.byId, shallowEqual);
|
||||
|
||||
const { from, channel, type, date } = filter;
|
||||
const {
|
||||
|
||||
@@ -9,6 +9,7 @@ import useExpiredResMap from "@/hooks/useExpiredResMap";
|
||||
import Filter from "./Filter";
|
||||
import Search from "./Search";
|
||||
import View from "./View";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
const checkFilter = (data, filter, channelMessage) => {
|
||||
let selected = true;
|
||||
@@ -36,14 +37,10 @@ function ResourceManagement() {
|
||||
const { isExpired } = useExpiredResMap();
|
||||
const listContainerRef = useRef<HTMLDivElement>();
|
||||
const [filter, setFilter] = useState({});
|
||||
const { message, view, channelMessage, fileMsgs } = useAppSelector((store) => {
|
||||
return {
|
||||
fileMsgs: store.fileMessage,
|
||||
message: store.message,
|
||||
channelMessage: store.channelMessage,
|
||||
view: store.ui.fileListView
|
||||
};
|
||||
});
|
||||
const view = useAppSelector((store) => store.ui.fileListView.view, shallowEqual);
|
||||
const message = useAppSelector((store) => store.message, shallowEqual);
|
||||
const fileMsgs = useAppSelector((store) => store.fileMessage, shallowEqual);
|
||||
const channelMessage = useAppSelector((store) => store.channelMessage, shallowEqual);
|
||||
|
||||
const updateFilter = (data) => {
|
||||
setFilter((prev) => {
|
||||
|
||||
@@ -4,11 +4,12 @@ import { useTranslation } from "react-i18next";
|
||||
import IconCopy from "@/assets/icons/copy.svg";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
import useCopy from "../../hooks/useCopy";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
// type Props = {}
|
||||
const APIUrl = `${location.origin}/api/swagger`;
|
||||
const APIDocument = () => {
|
||||
const token = useAppSelector((store) => store.authData.token);
|
||||
const token = useAppSelector((store) => store.authData.token, shallowEqual);
|
||||
const { copy } = useCopy();
|
||||
const { t } = useTranslation("setting");
|
||||
const handleCopy = () => {
|
||||
|
||||
@@ -13,6 +13,7 @@ import DeleteModal from "./DeleteModal";
|
||||
import NameEdit from "./NameEdit";
|
||||
import WebhookEdit from "./WebhookEdit";
|
||||
import WebhookModal from "./WebhookModal";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
type TipProps = { title: string; desc: string };
|
||||
const Tip = ({ title, desc }: TipProps) => {
|
||||
@@ -33,7 +34,10 @@ export default function BotConfig() {
|
||||
const [createModalVisible, setCreateModalVisible] = useState(false);
|
||||
const [currWebhookParams, setCurrWebhookParams] = useState<WebhookParams | undefined>(undefined);
|
||||
const [currDeleteParams, setCurrDeleteParams] = useState<DeleteParams | undefined>(undefined);
|
||||
const bots = useAppSelector((store) => Object.values(store.users.byId).filter((u) => !!u.is_bot));
|
||||
const bots = useAppSelector(
|
||||
(store) => Object.values(store.users.byId).filter((u) => !!u.is_bot),
|
||||
shallowEqual
|
||||
);
|
||||
const { t } = useTranslation("setting", { keyPrefix: "bot" });
|
||||
const { t: ct } = useTranslation();
|
||||
|
||||
|
||||
@@ -11,13 +11,17 @@ import SettingBlock from "@/components/SettingBlock";
|
||||
import Button from "@/components/styled/Button";
|
||||
import StyledModal from "@/components/styled/Modal";
|
||||
import StyledRadio from "@/components/styled/Radio";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
// type Props = {
|
||||
// updateConfirmModal: (context: VisibleModalType | null) => void;
|
||||
// };
|
||||
|
||||
const AutoDeleteFiles = () => {
|
||||
const currStatus = useAppSelector((store) => store.server.max_file_expiry_mode ?? "Off");
|
||||
const currStatus = useAppSelector(
|
||||
(store) => store.server.max_file_expiry_mode ?? "Off",
|
||||
shallowEqual
|
||||
);
|
||||
const { t } = useTranslation("setting", { keyPrefix: "data.auto_delete_file" });
|
||||
const { t: ct } = useTranslation();
|
||||
const { refetch } = useGetSystemCommonQuery();
|
||||
|
||||
@@ -9,6 +9,7 @@ import Button from "@/components/styled/Button";
|
||||
import ProfileBasicEditModal from "./ProfileBasicEditModal";
|
||||
import RemoveAccountConfirmModal from "./RemoveAccountConfirmModal";
|
||||
import UpdatePasswordModal from "./UpdatePasswordModal";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
type EditField = "name" | "email" | "";
|
||||
export default function MyAccount() {
|
||||
@@ -31,9 +32,10 @@ export default function MyAccount() {
|
||||
}
|
||||
};
|
||||
|
||||
const loginUser = useAppSelector((store) => {
|
||||
return store.users.byId[store.authData.user?.uid || 0];
|
||||
});
|
||||
const loginUser = useAppSelector(
|
||||
(store) => store.users.byId[store.authData.user?.uid || 0],
|
||||
shallowEqual
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (uploadSuccess) {
|
||||
|
||||
@@ -11,11 +11,15 @@ import {
|
||||
} from "../../../app/services/server";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
import { ChatLayout } from "../../../types/server";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
// type Props = {}
|
||||
|
||||
const Index = () => {
|
||||
const currStatus = useAppSelector((store) => store.server.chat_layout_mode ?? "Left");
|
||||
const currStatus = useAppSelector(
|
||||
(store) => store.server.chat_layout_mode ?? "Left",
|
||||
shallowEqual
|
||||
);
|
||||
const { t } = useTranslation("setting", { keyPrefix: "overview.chat_layout" });
|
||||
const { t: ct } = useTranslation();
|
||||
const { refetch } = useGetSystemCommonQuery();
|
||||
|
||||
@@ -10,11 +10,15 @@ import {
|
||||
useUpdateSystemCommonMutation
|
||||
} from "../../../app/services/server";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
// type Props = {}
|
||||
|
||||
const Index = () => {
|
||||
const currStatus = useAppSelector((store) => !!store.server.contact_verification_enable);
|
||||
const currStatus = useAppSelector(
|
||||
(store) => !!store.server.contact_verification_enable,
|
||||
shallowEqual
|
||||
);
|
||||
const { t } = useTranslation("setting", { keyPrefix: "overview.contact_verify" });
|
||||
const { t: ct } = useTranslation();
|
||||
const { refetch } = useGetSystemCommonQuery();
|
||||
|
||||
@@ -10,11 +10,15 @@ import {
|
||||
useUpdateSystemCommonMutation
|
||||
} from "../../../app/services/server";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
// type Props = {}
|
||||
|
||||
const Index = () => {
|
||||
const currStatus = useAppSelector((store) => !!store.server.show_user_online_status);
|
||||
const currStatus = useAppSelector(
|
||||
(store) => !!store.server.show_user_online_status,
|
||||
shallowEqual
|
||||
);
|
||||
const { t } = useTranslation("setting", { keyPrefix: "overview.online_status" });
|
||||
const { t: ct } = useTranslation();
|
||||
const { refetch } = useGetSystemCommonQuery();
|
||||
|
||||
@@ -9,13 +9,13 @@ import SaveTip from "@/components/SaveTip";
|
||||
import Input from "@/components/styled/Input";
|
||||
import Label from "@/components/styled/Label";
|
||||
import Textarea from "@/components/styled/Textarea";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
const Index = () => {
|
||||
const { t } = useTranslation("setting");
|
||||
const { t: ct } = useTranslation();
|
||||
const { loginUser, server } = useAppSelector((store) => {
|
||||
return { loginUser: store.authData.user, server: store.server };
|
||||
});
|
||||
const isAdmin = useAppSelector((store) => store.authData.user?.is_admin, shallowEqual);
|
||||
const server = useAppSelector((store) => store.server, shallowEqual);
|
||||
|
||||
const [uploadLogo, { isSuccess: uploadSuccess }] = useUpdateLogoMutation();
|
||||
const [updateServer] = useUpdateServerMutation();
|
||||
@@ -58,8 +58,7 @@ const Index = () => {
|
||||
}
|
||||
}, [server, serverValues]);
|
||||
const { name, description, logo } = serverValues;
|
||||
const isAdmin = loginUser?.is_admin;
|
||||
if (!loginUser || !serverValues) return null;
|
||||
if (!serverValues) return null;
|
||||
return (
|
||||
<>
|
||||
<div className="flex gap-4">
|
||||
|
||||
@@ -13,12 +13,11 @@ import FrontendURL from "./FrontendURL";
|
||||
import Language from "./Language";
|
||||
import OnlineStatus from "./OnlineStatus";
|
||||
import Server from "./Server";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
export default function Overview() {
|
||||
const { t } = useTranslation("setting");
|
||||
const { loginUser } = useAppSelector((store) => {
|
||||
return { loginUser: store.authData.user };
|
||||
});
|
||||
const isAdmin = useAppSelector((store) => store.authData.user?.is_admin, shallowEqual);
|
||||
const { values: loginConfig, updateConfig: updateLoginConfig } = useConfig("login");
|
||||
const handleUpdateWhoCanSignUp = (value: WhoCanSignUp) => {
|
||||
updateLoginConfig({ ...loginConfig, who_can_sign_up: value });
|
||||
@@ -30,7 +29,6 @@ export default function Overview() {
|
||||
};
|
||||
if (!loginConfig) return null;
|
||||
const { who_can_sign_up: whoCanSignUp, guest = false } = loginConfig as LoginConfig;
|
||||
const isAdmin = loginUser?.is_admin;
|
||||
|
||||
return (
|
||||
<div className="relative w-full md:w-[512px] flex flex-col gap-6">
|
||||
|
||||
@@ -9,13 +9,13 @@ import SaveTip from "@/components/SaveTip";
|
||||
import Input from "@/components/styled/Input";
|
||||
import Label from "@/components/styled/Label";
|
||||
import Textarea from "@/components/styled/Textarea";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
const Index = () => {
|
||||
const { t } = useTranslation("setting");
|
||||
const { t: ct } = useTranslation();
|
||||
const { loginUser, server } = useAppSelector((store) => {
|
||||
return { loginUser: store.authData.user, server: store.server };
|
||||
});
|
||||
const isAdmin = useAppSelector((store) => store.authData.user?.is_admin, shallowEqual);
|
||||
const server = useAppSelector((store) => store.server, shallowEqual);
|
||||
|
||||
const [uploadLogo, { isSuccess: uploadSuccess }] = useUpdateLogoMutation();
|
||||
const [updateServer] = useUpdateServerMutation();
|
||||
@@ -58,8 +58,7 @@ const Index = () => {
|
||||
}
|
||||
}, [server, serverValues]);
|
||||
const { name, description, logo } = serverValues;
|
||||
const isAdmin = loginUser?.is_admin;
|
||||
if (!loginUser || !serverValues) return null;
|
||||
if (!serverValues) return null;
|
||||
return (
|
||||
<>
|
||||
<div className="flex gap-4">
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useAppSelector } from "../../app/store";
|
||||
import Button from "../../components/styled/Button";
|
||||
import Input from "../../components/styled/Input";
|
||||
import useCopy from "../../hooks/useCopy";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
const Row = ({
|
||||
paramKey,
|
||||
@@ -34,7 +35,7 @@ const Row = ({
|
||||
);
|
||||
};
|
||||
export default function Widget() {
|
||||
const loginUid = useAppSelector((store) => store.authData.user?.uid);
|
||||
const loginUid = useAppSelector((store) => store.authData.user?.uid, shallowEqual);
|
||||
const widgetLink = `${location.origin}/widget.html?host=${loginUid}`;
|
||||
const { t } = useTranslation("setting", { keyPrefix: "widget" });
|
||||
const { t: wt } = useTranslation("widget");
|
||||
|
||||
@@ -15,6 +15,7 @@ import License from "./License";
|
||||
import MyAccount from "./MyAccount";
|
||||
import Overview from "./Overview";
|
||||
import Widget from "./Widget";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
const dataManagementNav = {
|
||||
name: "data_management",
|
||||
@@ -96,9 +97,8 @@ const navs = [
|
||||
|
||||
const useNavs = () => {
|
||||
const { t } = useTranslation("setting");
|
||||
const { loginUser, upgraded } = useAppSelector((store) => {
|
||||
return { loginUser: store.authData.user, upgraded: store.server.upgraded };
|
||||
});
|
||||
const loginUser = useAppSelector((store) => store.authData.user, shallowEqual);
|
||||
const upgraded = useAppSelector((store) => store.server.upgraded, shallowEqual);
|
||||
const filteredNavs = loginUser?.is_admin
|
||||
? navs
|
||||
: navs
|
||||
|
||||
@@ -17,16 +17,13 @@ import Label from "@/components/styled/Label";
|
||||
import Radio from "@/components/styled/Radio";
|
||||
import Textarea from "@/components/styled/Textarea";
|
||||
import IconChannel from "@/assets/icons/channel.svg";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
export default function Overview({ id = 0 }) {
|
||||
const { t } = useTranslation("setting", { keyPrefix: "channel" });
|
||||
const { t: ct } = useTranslation();
|
||||
const { loginUser, channel } = useAppSelector((store) => {
|
||||
return {
|
||||
loginUser: store.authData.user,
|
||||
channel: store.channels.byId[id]
|
||||
};
|
||||
});
|
||||
const loginUser = useAppSelector((store) => store.authData.user, shallowEqual);
|
||||
const channel = useAppSelector((store) => store.channels.byId[id], shallowEqual);
|
||||
const { data, refetch } = useGetChannelQuery(id);
|
||||
const [changed, setChanged] = useState(false);
|
||||
const [values, setValues] = useState<Channel>();
|
||||
|
||||
@@ -7,18 +7,18 @@ import LeaveChannel from "@/components/LeaveChannel";
|
||||
import StyledSettingContainer from "@/components/StyledSettingContainer";
|
||||
import DeleteConfirmModal from "./DeleteConfirmModal";
|
||||
import useNavs from "./navs";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
let from: string = "";
|
||||
|
||||
export default function ChannelSetting() {
|
||||
const { t } = useTranslation("setting");
|
||||
const { cid = 0, nav: navKey } = useParams();
|
||||
const { loginUser, channel } = useAppSelector((store) => {
|
||||
return {
|
||||
loginUser: store.authData.user,
|
||||
channel: cid ? store.channels.byId[+cid] : undefined
|
||||
};
|
||||
});
|
||||
const loginUser = useAppSelector((store) => store.authData.user, shallowEqual);
|
||||
const channel = useAppSelector(
|
||||
(store) => (cid ? store.channels.byId[+cid] : undefined),
|
||||
shallowEqual
|
||||
);
|
||||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
const navs = useNavs(+cid);
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useAppSelector } from "@/app/store";
|
||||
import StyledSettingContainer from "@/components/StyledSettingContainer";
|
||||
import DeleteConfirmModal from "./DeleteConfirmModal";
|
||||
import useNavs from "./navs";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
let from: string = "";
|
||||
|
||||
@@ -13,12 +14,7 @@ export default function DMSetting() {
|
||||
// const { t } = useTranslation("setting");
|
||||
const { t: ct } = useTranslation();
|
||||
const { uid = 0, nav: navKey } = useParams();
|
||||
const { loginUser } = useAppSelector((store) => {
|
||||
return {
|
||||
loginUser: store.authData.user,
|
||||
user: uid ? store.users.byId[+uid] : undefined
|
||||
};
|
||||
});
|
||||
const isAdmin = useAppSelector((store) => store.authData.user?.is_admin, shallowEqual);
|
||||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
const navs = useNavs(+uid);
|
||||
@@ -38,7 +34,6 @@ export default function DMSetting() {
|
||||
};
|
||||
if (!uid) return null;
|
||||
const currNav = flattenNavs.find((n) => n.name == navKey);
|
||||
const canDelete = loginUser?.is_admin;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -49,7 +44,7 @@ export default function DMSetting() {
|
||||
title="DM Setting"
|
||||
navs={navs}
|
||||
dangers={
|
||||
canDelete
|
||||
isAdmin
|
||||
? [
|
||||
{
|
||||
title: ct("action.remove_user"),
|
||||
|
||||
@@ -8,6 +8,7 @@ import AddEntriesMenu from "@/components/AddEntriesMenu";
|
||||
import Tooltip from "@/components/Tooltip";
|
||||
import IconAdd from "@/assets/icons/add.svg";
|
||||
import IconSearch from "@/assets/icons/search.svg";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
type Props = {
|
||||
type?: "users" | "members";
|
||||
@@ -16,7 +17,10 @@ type Props = {
|
||||
updateInput: (input: string) => void;
|
||||
};
|
||||
const Search: FC<Props> = ({ input, updateInput, openModal, type = "users" }) => {
|
||||
const enableContact = useAppSelector((store) => store.server.contact_verification_enable);
|
||||
const enableContact = useAppSelector(
|
||||
(store) => store.server.contact_verification_enable,
|
||||
shallowEqual
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
const handleInput = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
updateInput(evt.target.value);
|
||||
|
||||
Reference in New Issue
Block a user