新增功能

This commit is contained in:
Your Name
2026-03-04 15:32:30 +08:00
parent a07e844c47
commit ab77f5488d
2266 changed files with 177942 additions and 3444 deletions
@@ -0,0 +1,19 @@
export * from './useCallerUserInfoContext';
export * from './useCallInfoContext';
export * from './useGetVolumeMap';
export * from './useTip';
export * from './useNetWorkStatus';
export * from './usePlayer';
export * from './useUserInfoContextExcludeVolume';
export * from './useCallDuration';
export * from './useButtonPanelStatus';
export * from './useFocusContext';
export * from './useFloatWindowContext';
export * from './useIsClickableContext';
export * from './usePopover';
export * from './useGroupCallLayout';
export * from './useCustomUIButtonConfig';
export * from './useCustomUI';
export * from './useViewBackgroundConfig';
export * from './useJoinGroupCall';
export * from './useTranslate';
@@ -0,0 +1,6 @@
import { inject, ref } from '../../adapter-vue';
import { ButtonPanelContextKey, TButtonPanelContextValue } from '../context';
export function useButtonPanelStatus() {
return inject<TButtonPanelContextValue>(ButtonPanelContextKey, { status: ref('open') });
}
@@ -0,0 +1,32 @@
import { NAME, StoreName, TUIStore } from '../../TUICallService';
import { ref, onMounted, onUnmounted } from '../../adapter-vue';
export function useCallDuration() {
const callDuration = ref(TUIStore.getData(StoreName.CALL, NAME.CALL_DURATION));
const handleCallDurationChange = (value) => {
callDuration.value = value;
};
onMounted(() => {
TUIStore.watch(
StoreName.CALL,
{
[NAME.CALL_DURATION]: handleCallDurationChange,
},
{
notifyRangeWhenWatch: NAME.MYSELF,
},
);
});
onUnmounted(() => {
TUIStore.unwatch(
StoreName.CALL,
{
[NAME.CALL_DURATION]: handleCallDurationChange,
}
);
});
return { callDuration };
}
@@ -0,0 +1,6 @@
import { inject } from '../../adapter-vue';
import { CallInfoContextKey, TCallInfoContextValue } from '../context';
export function useCallInfoContext() {
return inject<TCallInfoContextValue>(CallInfoContextKey);
}
@@ -0,0 +1,6 @@
import { inject } from '../../adapter-vue';
import { CallerUserInfoContextKey, TCallerUserInfoValue } from '../context';
export function useCallerUserInfoContext() {
return inject<TCallerUserInfoValue>(CallerUserInfoContextKey);
}
@@ -0,0 +1,7 @@
import { ICustomUIConfig } from "../../TUICallService/const";
import { inject, Ref } from "../../adapter-vue";
import { CustomUIConfigContextKey } from "../context";
export function useCustomUI() {
return inject<Ref<ICustomUIConfig>>(CustomUIConfigContextKey);
}
@@ -0,0 +1,96 @@
import { watch, ref, toRefs, computed } from '../../adapter-vue';
import { useCustomUI } from './useCustomUI';
import { TUIGlobal, CallStatus } from '../../TUICallService';
import { add, deepClone, findValues, modify } from '../util';
import { VirtualBackgroundMobileConfig } from '../components/common/ButtonPanel/config/VirtualBackgroundMobileConfig';
import { useCallInfoContext } from './useCallInfoContext';
import { useUserInfoExcludeVolumeContext } from './useUserInfoContextExcludeVolume';
import { ButtonPanelConfig } from '../components/common/ButtonPanel/config/InitConfig';
function setVirtualBackgroundConfig(config) {
const newConfig = deepClone(config);
modify(newConfig, 'mobile.singleCall.video', VirtualBackgroundMobileConfig);
add(newConfig, 'pc.singleCall.video.calling[0][2]', { name: 'virtualBackground', props: {} });
add(newConfig, 'pc.singleCall.video.accept[0][1]', { name: 'virtualBackground', props: {} });
add(newConfig, 'pc.singleCall.video.connected[0][3]', { name: 'virtualBackground', props: {} });
add(newConfig, 'pc.groupCall.video.calling[0][3]', { name: 'virtualBackground', props: {} });
add(newConfig, 'pc.groupCall.video.connected[0][4]', { name: 'virtualBackground', props: {} });
return newConfig;
}
function setCloseCameraConfig(config, isVideoAvailable, isShowVirtualBackgroundIcon) {
let newConfig = deepClone(config);
if(isVideoAvailable){
modify(newConfig, 'mobile.singleCall.video.connected[1][2].props.show', true);
if(isShowVirtualBackgroundIcon) {
setVirtualBackgroundConfig(newConfig);
}
} else {
modify(newConfig, 'mobile.singleCall.video.connected[1][2].props.show', false);
if(isShowVirtualBackgroundIcon) {
modify(newConfig, 'mobile.singleCall.video.connected[1][0].props.show', false);
modify(newConfig, 'pc.singleCall.video.connected[0][3].props.show', false);
modify(newConfig, 'pc.groupCall.video.connected[0][4].props.show', false);
}
}
return newConfig;
}
export function useCustomUIButtonConfig() {
const { isShowEnableVirtualBackground, callStatus } = toRefs(useCallInfoContext());
const customUIConfig = useCustomUI();
const { localUserInfoExcludeVolume: localUserInfo } = toRefs(useUserInfoExcludeVolumeContext());
const isVideoAvailable = computed(() => localUserInfo?.value.isVideoAvailable || false);
const isShowVirtualBackgroundIcon = computed(() => isShowEnableVirtualBackground.value && !TUIGlobal.isH5);
const results = ref([]);
watch([customUIConfig, isShowEnableVirtualBackground, isVideoAvailable], () => {
let initConfig = deepClone(ButtonPanelConfig);
if (isShowVirtualBackgroundIcon.value) {
initConfig = setVirtualBackgroundConfig(ButtonPanelConfig);
}
if(callStatus.value === CallStatus.CONNECTED) {
initConfig = setCloseCameraConfig(initConfig, isVideoAvailable.value, isShowVirtualBackgroundIcon.value);
}
const { button: buttonsConfig } = customUIConfig.value;
const rs = [];
function condition(value) {
return Object.keys(buttonsConfig).includes(value);
}
function formatResults({ key, value }) {
const valueArr = value.split('.');
let path = valueArr.slice(0, valueArr.length - 1);
const rowPath = valueArr.slice(0, valueArr.length - 2);
const rowIndex = rowPath[rowPath.length - 1];
if (rowIndex === '0') {
for(let i = 0; i < 3; i++) {
let newPath = rowPath.slice();
newPath.push(i);
newPath.push('customStyle');
newPath.push('justifyContent');
newPath = newPath.join('.');
rs.push({ path: newPath, value: 'center' });
}
}
path.push('props');
path.push('show');
path = path.join('.');
return {
path,
value: buttonsConfig?.[key]?.show,
};
}
findValues(initConfig, condition, '', rs, formatResults);
rs?.forEach((item) => {
modify(initConfig, item.path, item.value);
});
results.value = initConfig;
}, {
immediate: true,
});
return results;
}
@@ -0,0 +1,51 @@
import { ref, onMounted, onUnmounted } from '../../adapter-vue';
import { TUIStore } from '../../TUICallService';
import { DeviceType, NAME, StoreName } from '../../TUICallService/const';
export function useDeviceList(deviceType: DeviceType) {
const deviceList = ref([]);
const currentDeviceId = ref('');
const handleDeviceListChange = (value) => {
switch (deviceType) {
case DeviceType.CAMERA:
deviceList.value = value?.cameraList || [];
currentDeviceId.value = value?.currentCamera?.deviceId || '';
break;
case DeviceType.MICROPHONE:
deviceList.value = value?.microphoneList || [];
currentDeviceId.value = value?.currentMicrophone?.deviceId || '';
break;
case DeviceType.SPEAKER:
deviceList.value = value?.speakerList || [];
currentDeviceId.value = value?.currentSpeaker?.deviceId || '';
break;
default:
break;
}
};
const updateCurrentDeviceId = (value) => {
currentDeviceId.value = value;
};
onMounted(() => {
TUIStore.watch(
StoreName.CALL,
{
[NAME.DEVICE_LIST]: handleDeviceListChange,
},
{
notifyRangeWhenWatch: NAME.MYSELF,
},
);
});
onUnmounted(() => {
TUIStore.unwatch(StoreName.CALL, {
[NAME.DEVICE_LIST]: handleDeviceListChange,
});
});
return [{ deviceList, currentDeviceId }, { updateCurrentDeviceId }] as const;
}
@@ -0,0 +1,6 @@
import { inject } from '../../adapter-vue';
import { FloatWindowContextKey, TFloatWindowContextValue } from '../context';
export function useFloatWindowContext() {
return inject<TFloatWindowContextValue>(FloatWindowContextKey);
}
@@ -0,0 +1,6 @@
import { inject, ref } from '../../adapter-vue';
import { FocusContextKey } from '../context';
export function useFocusContext() {
return inject(FocusContextKey, ref('open'));
}
@@ -0,0 +1,59 @@
import { ref, onMounted, onUnmounted } from '../../adapter-vue';
import { TUIStore, TUIGlobal } from '../../TUICallService';
import { NAME, StoreName } from '../../TUICallService/const';
import { isEqual } from '../util';
export function useGetVolumeMap() {
const volumeMap = ref();
const remoteUserInfoList = ref(TUIStore.getData(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST));
const handleLocalUserInfoChange = (value) => {
const isAudioAvailable = TUIGlobal.isWeChat ? value?.enableMic : value?.isAudioAvailable;
if (isAudioAvailable) {
volumeMap.value = { ...volumeMap.value, localVideo: value?.volume };
}
};
const handleRemoteUserInfoListChange = (value) => {
const rs = {};
if (value.length !== remoteUserInfoList.value.length || !isEqual(value, remoteUserInfoList.value)) {
remoteUserInfoList.value = value;
remoteUserInfoList.value.forEach(item => {
const isAudioAvailable = TUIGlobal.isWeChat ? item.hasAudio : item.isAudioAvailable;
if (isAudioAvailable) {
const domId = TUIGlobal.isWeChat ? item.userID : item.domId;
rs[domId] = item.volume;
}
});
volumeMap.value = { ...volumeMap.value, ...rs };
}
};
let watchOptions = {
[NAME.LOCAL_USER_INFO]: handleLocalUserInfoChange,
[NAME.REMOTE_USER_INFO_LIST]: handleRemoteUserInfoListChange,
};
if (TUIGlobal.isUniPlatform) {
watchOptions = {
[NAME.PUSHER]: handleLocalUserInfoChange,
[NAME.PLAYER]: handleRemoteUserInfoListChange,
};
}
onMounted(() => {
TUIStore.watch(
StoreName.CALL,
watchOptions,
{
notifyRangeWhenWatch: NAME.MYSELF,
},
);
});
onUnmounted(() => {
TUIStore.unwatch(StoreName.CALL, watchOptions);
});
return volumeMap;
}
@@ -0,0 +1,142 @@
import { watch, ref, toRefs } from '../../adapter-vue';
import { TUIGlobal } from '../../TUICallService';
import { useFloatWindowContext } from './useFloatWindowContext';
const generateDefaultLayout = (length: number, size: number) => {
const layout = [{
i: 0, x: 0, y: 0, w: size, h: size,
}];
for (let i = 1; i < length; i++) {
const isWrap = layout[i - 1].x + size === 12;
layout[i] = {
i,
x: layout[i - 1].x + size === 12 ? 0 : layout[i - 1].x + size,
y: layout[i - 1].y + (isWrap ? size : 0),
w: size,
h: size,
};
}
if (length === 3) {
layout[length - 1].x += 3;
}
if (length > 3 && TUIGlobal.isPC) {
if (length % 3 === 2) {
layout[length - 1].x += 2;
layout[length - 2].x += 2;
}
}
return layout;
};
export function useGroupCallLayout(focus, length) {
const { isFloatWindow } = toRefs(useFloatWindowContext());
const newLayout = ref();
watch([focus, length, isFloatWindow], () => {
if (isFloatWindow.value) {
const a = [];
for(let i = 0; i < length.value; i++) {
a[i] = {
i,
x: 0,
y: 0,
w: 12,
h: 12,
};
}
newLayout.value = a;
return;
}
const arr = Object.keys(Array.from({ length: length.value }));
const gridItemSize = length.value <= 4 ? 6 : 4;
const defaultLayout = generateDefaultLayout(length.value, gridItemSize);
newLayout.value = defaultLayout;
let colIndex;
let rowIndex;
if (focus.value !== null) {
if (arr.length < 5) {
const newArr = arr.concat();
newArr.splice(focus.value, 1);
newArr.unshift(focus.value);
} else {
rowIndex = focus.value % 3;
colIndex = Math.floor(focus.value / 3);
}
if (arr.length < 5) {
const focusIndex = defaultLayout.findIndex((item) => item.i === focus.value);
if (focusIndex !== -1) {
const temp = defaultLayout[0];
defaultLayout[0] = defaultLayout[focusIndex];
defaultLayout[focusIndex] = temp;
for (let i = 0; i < defaultLayout.length; i++) {
const item = defaultLayout[i];
if (i === 0) {
item.w += 6;
item.h += 6;
item.x = 0;
item.y = 0;
} else {
item.x = (i - 1) * 4;
item.y = 12;
item.w = 4;
item.h = 4;
}
}
}
} else {
let focusStyle;
if (rowIndex === 0) {
if (defaultLayout[focus.value + 1]) defaultLayout[focus.value + 1].x += 4;
if (defaultLayout[focus.value + 2]) defaultLayout[focus.value + 2].y += 4;
focusStyle = {
i: focus.value,
x: 0,
y: colIndex * 4,
w: 8,
h: 8,
};
} else if (rowIndex === 2) {
focusStyle = {
i: focus.value,
x: 4,
y: colIndex * 4,
w: 8,
h: 8,
};
defaultLayout[focus.value - 1].x = 0;
defaultLayout[focus.value - 1].y += 4;
} else if (rowIndex === 1) {
focusStyle = {
i: focus.value,
x: 4,
y: colIndex * 4,
w: 8,
h: 8,
};
if (defaultLayout[focus.value + 1]) {
defaultLayout[focus.value + 1].x = 0;
defaultLayout[focus.value + 1].y += 4;
}
}
const start = 3 - rowIndex;
for (let i = focus.value + start; i < defaultLayout.length; i++) {
const item = defaultLayout[i];
item.y += 4;
}
defaultLayout[focus.value] = focusStyle;
}
newLayout.value = defaultLayout;
}
}, {
immediate: true,
});
return newLayout;
}
@@ -0,0 +1,6 @@
import { inject, Ref } from '../../adapter-vue';
import { IsClickableContextKey } from '../context';
export function useIsClickableContext() {
return inject<Ref<boolean>>(IsClickableContextKey);
}
@@ -0,0 +1,69 @@
import { ref, onMounted, onUnmounted } from '../../adapter-vue';
import { TUIStore } from '../../TUICallService';
import { NAME, StoreName } from '../../TUICallService/const';
export function useJoinGroupCall() {
const roomId = ref(TUIStore.getData(StoreName.CALL, NAME.ROOM_ID));
const roomIdType = ref(TUIStore.getData(StoreName.CALL, NAME.ROOM_ID_TYPE));
const groupId = ref(TUIStore.getData(StoreName.CALL, NAME.GROUP_ID));
const callMediaType = ref(TUIStore.getData(StoreName.CALL, NAME.CALL_MEDIA_TYPE));
const groupCallMembers = ref(TUIStore.getData(StoreName.CALL, NAME.GROUP_CALL_MEMBERS));
const callStatus = ref(TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS));
const callId = ref(TUIStore.getData(StoreName.CALL, NAME.CALL_ID));
const handleRoomIDChange = (value) => {
roomId.value = value;
};
const handleGroupIDChange = (value) => {
groupId.value = value;
};
const handleCallMediaTypeChange = (value) => {
callMediaType.value = value;
};
const handleGroupCallMembersChange = (value) => {
groupCallMembers.value = value;
};
const handleCallStatusChange = (value) => {
callStatus.value = value;
};
const handleRoomIdTypeChange = (value) => {
roomIdType.value = value;
};
const handleCallIDChange = (value) => {
callId.value = value;
};
const watchOptions = {
[NAME.ROOM_ID]: handleRoomIDChange,
[NAME.GROUP_ID]: handleGroupIDChange,
[NAME.CALL_MEDIA_TYPE]: handleCallMediaTypeChange,
[NAME.GROUP_CALL_MEMBERS]: handleGroupCallMembersChange,
[NAME.CALL_STATUS]: handleCallStatusChange,
[NAME.ROOM_ID_TYPE]: handleRoomIdTypeChange,
[NAME.CALL_ID]: handleCallIDChange,
};
onMounted(() => {
TUIStore.watch(
StoreName.CALL,
watchOptions,
{
notifyRangeWhenWatch: NAME.MYSELF,
},
);
});
onUnmounted(() => {
TUIStore.unwatch(StoreName.CALL, watchOptions);
});
return {
roomId,
roomIdType,
groupId,
callMediaType,
groupCallMembers,
callStatus,
callId,
};
};
@@ -0,0 +1,30 @@
import { ref, onMounted, onUnmounted } from '../../adapter-vue';
import { TUIStore } from '../../TUICallService';
import { NAME, StoreName } from '../../TUICallService/const';
export function useNetWorkStatus() {
const netWorkQualityList = ref(TUIStore.getData(StoreName.CALL, NAME.NETWORK_STATUS));
const handleNetWorkStatusChange = (value) => {
netWorkQualityList.value = value;
};
onMounted(() => {
TUIStore.watch(
StoreName.CALL,
{
[NAME.NETWORK_STATUS]: handleNetWorkStatusChange,
},
{
notifyRangeWhenWatch: NAME.MYSELF,
},
);
});
onUnmounted(() => {
TUIStore.unwatch(StoreName.CALL, {
[NAME.NETWORK_STATUS]: handleNetWorkStatusChange,
});
});
return { netWorkQualityList };
}
@@ -0,0 +1,32 @@
import { ref, onMounted, onUnmounted } from '../../adapter-vue';
import { TUIStore } from '../../TUICallService';
import { NAME, StoreName } from '../../TUICallService/const';
export function usePlayer() {
const player = ref(TUIStore.getData(StoreName.CALL, NAME.PLAYER));
const handlePlayerChange = (value) => {
player.value = value?.map((item) => {
const { userID, hasVideo, hasAudio } = item;
return { userID, hasVideo, hasAudio };
});
};
const watchOptions = {
[NAME.PLAYER]: handlePlayerChange,
};
onMounted(() => {
TUIStore.watch(
StoreName.CALL,
watchOptions,
{ notifyRangeWhenWatch: NAME.MYSELF },
);
});
onUnmounted(() => {
TUIStore.unwatch(StoreName.CALL, watchOptions);
});
return player;
}
@@ -0,0 +1,6 @@
import { inject, Ref } from '../../adapter-vue';
import { PopoverContextKey } from '../context';
export function usePopover() {
return inject<Ref<string>>(PopoverContextKey);
}
@@ -0,0 +1,42 @@
import { ref, onMounted, onUnmounted } from '../../adapter-vue';
import { TUIStore } from '../../TUICallService';
import { NAME, StoreName } from '../../TUICallService/const';
export function useTip() {
const tip = ref('');
const show = ref(true);
const duration = ref(0);
const handleCallTipsChange = (value) => {
if (typeof value === 'object') {
tip.value = value.text;
duration.value = value.duration || 0;
} else {
tip.value = value;
}
};
onMounted(() => {
TUIStore.watch(
StoreName.CALL,
{
[NAME.CALL_TIPS]: handleCallTipsChange,
},
{
notifyRangeWhenWatch: NAME.MYSELF,
},
);
});
onUnmounted(() => {
TUIStore.unwatch(
StoreName.CALL,
{
[NAME.CALL_TIPS]: handleCallTipsChange,
},
);
});
return { tip, show, duration };
}
@@ -0,0 +1,7 @@
import { noop } from '../../TUICallService/utils/common-utils';
import { inject, Ref, ref } from '../../adapter-vue';
import { translateContextKey } from '../context';
export function useTranslate() {
return inject<Ref<Function>>(translateContextKey, ref(noop));
}
@@ -0,0 +1,6 @@
import { inject } from '../../adapter-vue';
import { TUserInfoExcludeVolumeContextValue, UserInfoExcludeVolumeContextKey } from '../context';
export function useUserInfoExcludeVolumeContext() {
return inject<TUserInfoExcludeVolumeContextValue>(UserInfoExcludeVolumeContextKey);
}
@@ -0,0 +1,12 @@
import { watch, ref } from '../../adapter-vue';
import { useCustomUI } from './useCustomUI';
export function useViewBackgroundConfig() {
const customUIConfig = useCustomUI();
const viewBackgroundObj = ref(customUIConfig.value.viewBackground);
watch(customUIConfig, () => {
viewBackgroundObj.value = customUIConfig.value.viewBackground;
});
return viewBackgroundObj;
}