refactor: remember channel aside
This commit is contained in:
@@ -17,7 +17,7 @@ export default async function handler({ operation, data, payload }: Params) {
|
|||||||
{
|
{
|
||||||
const chs = payload as Channel[];
|
const chs = payload as Channel[];
|
||||||
const arr = chs.map(({ gid, ...rest }) => {
|
const arr = chs.map(({ gid, ...rest }) => {
|
||||||
return { key: gid + "", value: rest };
|
return { key: gid + "", value: { gid, ...rest } };
|
||||||
});
|
});
|
||||||
await table.setItems(arr);
|
await table.setItems(arr);
|
||||||
}
|
}
|
||||||
@@ -40,6 +40,7 @@ export default async function handler({ operation, data, payload }: Params) {
|
|||||||
await table?.setItem(id + "", data.byId[id]);
|
await table?.setItem(id + "", data.byId[id]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,11 @@ export default async function handler({ operation, data = {}, payload }: Params)
|
|||||||
await table?.setItem("autoDeleteMsgChannels", data.autoDeleteMsgChannels || []);
|
await table?.setItem("autoDeleteMsgChannels", data.autoDeleteMsgChannels || []);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "updateChannelVisibleAside":
|
||||||
|
{
|
||||||
|
await table?.setItem("channelAsides", data.channelAsides);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,10 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
|||||||
import { isNull, omitBy } from "lodash";
|
import { isNull, omitBy } from "lodash";
|
||||||
import BASE_URL from "../config";
|
import BASE_URL from "../config";
|
||||||
import { Channel, UpdateChannelDTO, UpdatePinnedMessageDTO } from "../../types/channel";
|
import { Channel, UpdateChannelDTO, UpdatePinnedMessageDTO } from "../../types/channel";
|
||||||
import { resetAuthData } from "./auth.data";
|
|
||||||
// import { updateVoicingInfo } from "./voice";
|
// import { updateVoicingInfo } from "./voice";
|
||||||
|
|
||||||
type ChannelAside = "members" | "voice" | null;
|
|
||||||
interface StoredChannel extends Channel {
|
interface StoredChannel extends Channel {
|
||||||
icon?: string;
|
icon?: string;
|
||||||
visibleAside: ChannelAside
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
@@ -28,9 +25,10 @@ const channelsSlice = createSlice({
|
|||||||
resetChannels() {
|
resetChannels() {
|
||||||
return initialState;
|
return initialState;
|
||||||
},
|
},
|
||||||
fillChannels(state, action: PayloadAction<Channel[]>) {
|
fillChannels(state, action: PayloadAction<StoredChannel[]>) {
|
||||||
const channels = action.payload || [];
|
const channels = action.payload || [];
|
||||||
state.ids = channels.map(({ gid }) => gid);
|
state.ids = channels.map(({ gid }) => gid);
|
||||||
|
|
||||||
channels.forEach((c) => {
|
channels.forEach((c) => {
|
||||||
state.byId[c.gid] = {
|
state.byId[c.gid] = {
|
||||||
...c,
|
...c,
|
||||||
@@ -38,7 +36,6 @@ const channelsSlice = createSlice({
|
|||||||
c.avatar_updated_at == 0
|
c.avatar_updated_at == 0
|
||||||
? ""
|
? ""
|
||||||
: `${BASE_URL}/resource/group_avatar?gid=${c.gid}&t=${c.avatar_updated_at}`,
|
: `${BASE_URL}/resource/group_avatar?gid=${c.gid}&t=${c.avatar_updated_at}`,
|
||||||
visibleAside: state.byId[c.gid]?.visibleAside ?? null
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -54,7 +51,6 @@ const channelsSlice = createSlice({
|
|||||||
avatar_updated_at == 0
|
avatar_updated_at == 0
|
||||||
? ""
|
? ""
|
||||||
: `${BASE_URL}/resource/group_avatar?gid=${gid}&t=${avatar_updated_at}`,
|
: `${BASE_URL}/resource/group_avatar?gid=${gid}&t=${avatar_updated_at}`,
|
||||||
visibleAside: "members"
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateChannel(state, action: PayloadAction<UpdateChannelDTO>) {
|
updateChannel(state, action: PayloadAction<UpdateChannelDTO>) {
|
||||||
@@ -105,12 +101,6 @@ const channelsSlice = createSlice({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateChannelVisibleAside(state, action: PayloadAction<{ id: number, aside: ChannelAside }>) {
|
|
||||||
const { id, aside } = action.payload;
|
|
||||||
if (state.byId[id]) {
|
|
||||||
state.byId[id].visibleAside = aside;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
removeChannel(state, action: PayloadAction<number>) {
|
removeChannel(state, action: PayloadAction<number>) {
|
||||||
const gid = action.payload;
|
const gid = action.payload;
|
||||||
const idx = state.ids.findIndex((i) => i == gid);
|
const idx = state.ids.findIndex((i) => i == gid);
|
||||||
@@ -120,16 +110,7 @@ const channelsSlice = createSlice({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
extraReducers: (builder) => {
|
|
||||||
builder.addCase(resetAuthData, (state) => {
|
|
||||||
// 如果有aside是voice的,就把它关掉
|
|
||||||
Object.values(state.byId).forEach((ch) => {
|
|
||||||
if (ch.visibleAside === "voice") {
|
|
||||||
ch.visibleAside = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
@@ -139,7 +120,6 @@ export const {
|
|||||||
addChannel,
|
addChannel,
|
||||||
updateChannel,
|
updateChannel,
|
||||||
removeChannel,
|
removeChannel,
|
||||||
updateChannelVisibleAside
|
|
||||||
} = channelsSlice.actions;
|
} = channelsSlice.actions;
|
||||||
|
|
||||||
export default channelsSlice.reducer;
|
export default channelsSlice.reducer;
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
|||||||
import { MuteDTO } from "../../types/message";
|
import { MuteDTO } from "../../types/message";
|
||||||
import { OG } from "../../types/resource";
|
import { OG } from "../../types/resource";
|
||||||
import { AutoDeleteMessageSettingDTO, AutoDeleteMsgForGroup, AutoDeleteMsgForUser, AutoDeleteSettingForChannels, AutoDeleteSettingForUsers } from "../../types/sse";
|
import { AutoDeleteMessageSettingDTO, AutoDeleteMsgForGroup, AutoDeleteMsgForUser, AutoDeleteSettingForChannels, AutoDeleteSettingForUsers } from "../../types/sse";
|
||||||
|
import { resetAuthData } from "./auth.data";
|
||||||
|
|
||||||
|
type ChannelAside = "members" | "voice" | null;
|
||||||
export interface State {
|
export interface State {
|
||||||
og: { [url: string]: OG }
|
og: { [url: string]: OG }
|
||||||
usersVersion: number;
|
usersVersion: number;
|
||||||
@@ -15,8 +17,10 @@ export interface State {
|
|||||||
muteChannels: { [cid: number]: { expired_in?: number } | undefined };
|
muteChannels: { [cid: number]: { expired_in?: number } | undefined };
|
||||||
autoDeleteMsgUsers: AutoDeleteMsgForUser[];
|
autoDeleteMsgUsers: AutoDeleteMsgForUser[];
|
||||||
autoDeleteMsgChannels: AutoDeleteMsgForGroup[];
|
autoDeleteMsgChannels: AutoDeleteMsgForGroup[];
|
||||||
|
channelAsides: { [cid: number]: ChannelAside };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const initialState: State = {
|
const initialState: State = {
|
||||||
og: {},
|
og: {},
|
||||||
usersVersion: 0,
|
usersVersion: 0,
|
||||||
@@ -29,6 +33,7 @@ const initialState: State = {
|
|||||||
muteChannels: {},
|
muteChannels: {},
|
||||||
autoDeleteMsgUsers: [],
|
autoDeleteMsgUsers: [],
|
||||||
autoDeleteMsgChannels: [],
|
autoDeleteMsgChannels: [],
|
||||||
|
channelAsides: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
const footprintSlice = createSlice({
|
const footprintSlice = createSlice({
|
||||||
@@ -50,7 +55,8 @@ const footprintSlice = createSlice({
|
|||||||
muteUsers = {},
|
muteUsers = {},
|
||||||
muteChannels = {},
|
muteChannels = {},
|
||||||
autoDeleteMsgUsers = [],
|
autoDeleteMsgUsers = [],
|
||||||
autoDeleteMsgChannels = []
|
autoDeleteMsgChannels = [],
|
||||||
|
channelAsides = {}
|
||||||
} = action.payload;
|
} = action.payload;
|
||||||
return {
|
return {
|
||||||
og,
|
og,
|
||||||
@@ -63,7 +69,8 @@ const footprintSlice = createSlice({
|
|||||||
muteUsers,
|
muteUsers,
|
||||||
muteChannels,
|
muteChannels,
|
||||||
autoDeleteMsgUsers,
|
autoDeleteMsgUsers,
|
||||||
autoDeleteMsgChannels
|
autoDeleteMsgChannels,
|
||||||
|
channelAsides
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateUsersVersion(state, action: PayloadAction<number>) {
|
updateUsersVersion(state, action: PayloadAction<number>) {
|
||||||
@@ -177,7 +184,21 @@ const footprintSlice = createSlice({
|
|||||||
reads.forEach(({ gid, mid }) => {
|
reads.forEach(({ gid, mid }) => {
|
||||||
state.readChannels[gid] = mid;
|
state.readChannels[gid] = mid;
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
updateChannelVisibleAside(state, action: PayloadAction<{ id: number, aside: ChannelAside }>) {
|
||||||
|
const { id, aside } = action.payload;
|
||||||
|
state.channelAsides[id] = aside;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder.addCase(resetAuthData, (state) => {
|
||||||
|
// 如果有aside是voice的,就把它关掉
|
||||||
|
Object.keys(state.channelAsides).forEach((channel_id: string) => {
|
||||||
|
if (state.channelAsides[+channel_id] === "voice") {
|
||||||
|
state.channelAsides[+channel_id] = null;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -191,7 +212,8 @@ export const {
|
|||||||
updateMute,
|
updateMute,
|
||||||
updateAutoDeleteSetting,
|
updateAutoDeleteSetting,
|
||||||
updateHistoryMark,
|
updateHistoryMark,
|
||||||
upsertOG
|
upsertOG,
|
||||||
|
updateChannelVisibleAside
|
||||||
} = footprintSlice.actions;
|
} = footprintSlice.actions;
|
||||||
|
|
||||||
export default footprintSlice.reducer;
|
export default footprintSlice.reducer;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import AgoraRTC, { IMicrophoneAudioTrack } from 'agora-rtc-sdk-ng';
|
|||||||
import { memo, useEffect } from 'react';
|
import { memo, useEffect } from 'react';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { useGetAgoraConfigQuery, useGetAgoraVoicingListQuery, useLazyGetAgoraTokenQuery } from '../../app/services/server';
|
import { useGetAgoraConfigQuery, useGetAgoraVoicingListQuery, useLazyGetAgoraTokenQuery } from '../../app/services/server';
|
||||||
import { updateChannelVisibleAside } from '../../app/slices/channels';
|
import { updateChannelVisibleAside } from '../../app/slices/footprint';
|
||||||
import { addVoiceMember, removeVoiceMember, updateConnectionState, updateDeafenStatus, updateMuteStatus, updateVoicingInfo, updateVoicingMember, updateVoicingNetworkQuality, upsertVoiceList } from '../../app/slices/voice';
|
import { addVoiceMember, removeVoiceMember, updateConnectionState, updateDeafenStatus, updateMuteStatus, updateVoicingInfo, updateVoicingMember, updateVoicingNetworkQuality, upsertVoiceList } from '../../app/slices/voice';
|
||||||
import { useAppSelector } from '../../app/store';
|
import { useAppSelector } from '../../app/store';
|
||||||
import AudioJoin from '../../assets/join.wav';
|
import AudioJoin from '../../assets/join.wav';
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { useAppSelector } from "../../../app/store";
|
|||||||
import GoBackNav from "../../../common/component/GoBackNav";
|
import GoBackNav from "../../../common/component/GoBackNav";
|
||||||
import Members from "./Members";
|
import Members from "./Members";
|
||||||
import VoiceChat from "../VoiceChat";
|
import VoiceChat from "../VoiceChat";
|
||||||
import { updateChannelVisibleAside } from "../../../app/slices/channels";
|
import { updateChannelVisibleAside } from "../../../app/slices/footprint";
|
||||||
import Dashboard from "../VoiceChat/Dashboard";
|
import Dashboard from "../VoiceChat/Dashboard";
|
||||||
type Props = {
|
type Props = {
|
||||||
cid?: number;
|
cid?: number;
|
||||||
@@ -33,11 +33,13 @@ function ChannelChat({ cid = 0, dropFiles = [] }: Props) {
|
|||||||
userIds,
|
userIds,
|
||||||
data,
|
data,
|
||||||
loginUser,
|
loginUser,
|
||||||
|
visibleAside
|
||||||
} = useAppSelector((store) => {
|
} = useAppSelector((store) => {
|
||||||
return {
|
return {
|
||||||
loginUser: store.authData.user,
|
loginUser: store.authData.user,
|
||||||
userIds: store.users.ids,
|
userIds: store.users.ids,
|
||||||
data: store.channels.byId[cid],
|
data: store.channels.byId[cid],
|
||||||
|
visibleAside: store.footprint.channelAsides[cid]
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -56,7 +58,7 @@ function ChannelChat({ cid = 0, dropFiles = [] }: Props) {
|
|||||||
const toggleMembersVisible = () => {
|
const toggleMembersVisible = () => {
|
||||||
dispatch(updateChannelVisibleAside({
|
dispatch(updateChannelVisibleAside({
|
||||||
id: cid,
|
id: cid,
|
||||||
aside: data.visibleAside !== "members" ? "members" : null
|
aside: visibleAside !== "members" ? "members" : null
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -109,7 +111,7 @@ function ChannelChat({ cid = 0, dropFiles = [] }: Props) {
|
|||||||
onClick={toggleMembersVisible}
|
onClick={toggleMembersVisible}
|
||||||
>
|
>
|
||||||
<Tooltip tip={t("channel_members")} placement="left">
|
<Tooltip tip={t("channel_members")} placement="left">
|
||||||
<IconPeople className={data.visibleAside == "members" ? "fill-gray-600" : ""} />
|
<IconPeople className={visibleAside == "members" ? "fill-gray-600" : ""} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -125,10 +127,10 @@ function ChannelChat({ cid = 0, dropFiles = [] }: Props) {
|
|||||||
</header>
|
</header>
|
||||||
}
|
}
|
||||||
users={
|
users={
|
||||||
<Members uids={memberIds} addVisible={addVisible} cid={cid} ownerId={owner} membersVisible={data.visibleAside == "members"} />
|
<Members uids={memberIds} addVisible={addVisible} cid={cid} ownerId={owner} membersVisible={visibleAside == "members"} />
|
||||||
}
|
}
|
||||||
voice={
|
voice={
|
||||||
<Dashboard visible={data.visibleAside == "voice"} id={cid} context="channel" />
|
<Dashboard visible={visibleAside == "voice"} id={cid} context="channel" />
|
||||||
}
|
}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// import { useState } from 'react';
|
// import { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { updateChannelVisibleAside } from '../../../app/slices/channels';
|
import { updateChannelVisibleAside } from '../../../app/slices/footprint';
|
||||||
import { useAppSelector } from '../../../app/store';
|
import { useAppSelector } from '../../../app/store';
|
||||||
import IconHeadphone from '../../../assets/icons/headphone.svg';
|
import IconHeadphone from '../../../assets/icons/headphone.svg';
|
||||||
import Tooltip from '../../../common/component/Tooltip';
|
import Tooltip from '../../../common/component/Tooltip';
|
||||||
@@ -18,11 +18,12 @@ type Props = {
|
|||||||
const VoiceChat = ({ id, context = "channel" }: Props) => {
|
const VoiceChat = ({ id, context = "channel" }: Props) => {
|
||||||
const { joinVoice, joined, joining = false, joinedAtThisContext } = useVoice({ id, context });
|
const { joinVoice, joined, joining = false, joinedAtThisContext } = useVoice({ id, context });
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { loginUser, contextData, voiceList } = useAppSelector(store => {
|
const { loginUser, voiceList, visibleAside } = useAppSelector(store => {
|
||||||
return {
|
return {
|
||||||
loginUser: store.authData.user,
|
loginUser: store.authData.user,
|
||||||
contextData: context == "channel" ? store.channels.byId[id] : store.users.byId[id],
|
contextData: context == "channel" ? store.channels.byId[id] : store.users.byId[id],
|
||||||
voiceList: store.voice.list
|
voiceList: store.voice.list,
|
||||||
|
visibleAside: context == "channel" ? store.footprint.channelAsides[id] : null,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const { data: agoraConfig } = useGetAgoraConfigQuery(undefined, { skip: !loginUser?.is_admin });
|
const { data: agoraConfig } = useGetAgoraConfigQuery(undefined, { skip: !loginUser?.is_admin });
|
||||||
@@ -31,7 +32,7 @@ const VoiceChat = ({ id, context = "channel" }: Props) => {
|
|||||||
if (context == "channel") {
|
if (context == "channel") {
|
||||||
dispatch(updateChannelVisibleAside({
|
dispatch(updateChannelVisibleAside({
|
||||||
id,
|
id,
|
||||||
aside: contextData.visibleAside == "voice" ? null : "voice"
|
aside: visibleAside == "voice" ? null : "voice"
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
// todo DM
|
// todo DM
|
||||||
@@ -51,7 +52,7 @@ const VoiceChat = ({ id, context = "channel" }: Props) => {
|
|||||||
};
|
};
|
||||||
// 只有admin才能看到入口,后续会改
|
// 只有admin才能看到入口,后续会改
|
||||||
if (!loginUser || !loginUser.is_admin || !agoraConfig?.enabled) return null;
|
if (!loginUser || !loginUser.is_admin || !agoraConfig?.enabled) return null;
|
||||||
const visible = contextData.visibleAside == "voice";
|
const visible = visibleAside == "voice";
|
||||||
const memberCount = voiceList.find((v) => v.context == context && v.id == id)?.memberCount ?? 0;
|
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 `;
|
const badgeClass = `absolute -top-2 -right-2 w-4 h-4 rounded-full bg-primary-400 text-white `;
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user