This commit is contained in:
zoujiandong
2024-01-22 08:55:30 +08:00
parent 22f3da6c33
commit 2bd2fd31ac
3460 changed files with 136738 additions and 0 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+47
View File
@@ -0,0 +1,47 @@
{
"name": "@tencentcloud/call-uikit-wechat",
"version": "2.1.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"node_modules/@tencentcloud/chat": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/@tencentcloud/chat/-/chat-3.2.4.tgz",
"integrity": "sha512-EC1M5exzI3BIvAt8apAq0/sZbecCaXri6mxE4Wifvzsz7OvJldF9qetMgUcLVC/nyQMt54BIEKnB3LolSHcPTQ=="
},
"node_modules/@tencentcloud/tui-core": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@tencentcloud/tui-core/-/tui-core-2.0.3.tgz",
"integrity": "sha512-kmrNjUobs2cSwRKkOuc1N7hinAB4ka4PZV6VyfO/hPr5iVG8NCKRJvh20Oj10bpYUjeN/2uJR+PuC/s7uPgm4w==",
"dependencies": {
"@tencentcloud/chat": "latest",
"tim-profanity-filter-plugin": "latest",
"tim-upload-plugin": "latest"
}
},
"node_modules/tim-profanity-filter-plugin": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/tim-profanity-filter-plugin/-/tim-profanity-filter-plugin-1.1.0.tgz",
"integrity": "sha512-+49p+n5LJs18rSPBvJFyq/m6ib8wiXvPEx2/Sz3M7hhDXAz69WuZ8cB/painNrN0MKAETf6G5cWa6xtVZfAK7A=="
},
"node_modules/tim-upload-plugin": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/tim-upload-plugin/-/tim-upload-plugin-1.3.0.tgz",
"integrity": "sha512-meoGeB3vOs3FvtBh+ik7zlOj8C03tC6MZEkjc8BKYvgTrCzBWVDXZi9a3X8AM/YtoyuTaeLuEmaLQj1O/5luuw=="
},
"node_modules/trtc-wx-sdk": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/trtc-wx-sdk/-/trtc-wx-sdk-1.1.5.tgz",
"integrity": "sha512-DOXPTekracOtp0e1tlZJtiImkTwUP2PXExp+xW8OUUTvu9F/lw9RVNqj4zsoCYZjBNbWHB0FwN5YT/6dtXVWKw=="
},
"node_modules/tuicall-engine-wx": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/tuicall-engine-wx/-/tuicall-engine-wx-2.1.3.tgz",
"integrity": "sha512-0RwMZ05gONHLzLMYOxz2yqdCB1zdL/vNXMZ63tIXPjsKyFI25DjAk9A0mcyoEuI904dE3Y1qElgAtiOqIFXlxw==",
"dependencies": {
"@tencentcloud/chat": "latest",
"trtc-wx-sdk": "latest"
}
}
}
}
+53
View File
@@ -0,0 +1,53 @@
# TUICallkit 组件接入说明
TUICallkit 是小程序音视频通话 UI 组件,通过编写几行代码,就可以为您的小程序应用添加音视频通话功能。
**低于 v3.0.4 版本,参考的 [readme](https://www.npmjs.com/package/@tencentcloud/call-uikit-wechat/v/1.4.4)。**
## 环境准备
- 微信 App iOS 最低版本要求:8.0.40
- 微信 App Android 最低版本要求:8.0.40
- 小程序基础库最低版本要求:2.10.0
- 由于小程序测试号不具备 live-pusher 和 live-player 的使用权限,请使用企业小程序账号申请相关权限进行开发
- 由于微信开发者工具不支持原生组件(即 live-pusher 和 live-player 标签),需要在真机上进行运行体验
## 特性
- ⚡️ 功能全面 —— 支持单人/多人/音频/视频通话、支持视频转音频通话、支持自由选择通话设备
- 🎨 灵活样式 —— 组件开源,可复用逻辑,自定义 UI 样式
- 🛠 优秀生态 —— 与 [TUIKit](https://cloud.tencent.com/document/product/269/79737) 协同使用,可以在 [TIM](https://cloud.tencent.com/document/product/269) 会话中直接发起音视频通话
- 🌍 跨平台 —— 支持 Android、iOS、Web、小程序、Flutter、UniApp 等 [多个开发平台](https://cloud.tencent.com/document/product/647/78742)
- ☁️ 低延迟 —— 腾讯云全球链路资源储备,保证国际链路端到端平均时延 < 300ms
- 🤙🏻 低卡顿 —— 抗丢包率超过 80%、抗网络抖动超过 1000ms,弱网环境仍顺畅稳定
- 🌈 高品质 —— 支持 720P、1080P 高清画质,70% 丢包率仍可正常视频
## 目录结构
```
TUICallkit
├─ debug // 用来本地测试时生成 userSig
├─ src // UI 组件源码
├─ Components // 呼叫中 UI 组件
└─ TUICallService // UI 组件的逻辑
└─ adapter-vue.ts // 用来适配 vue2、vue3 的适配层
└─ index.ts // UI 组件逻辑的入口
├─ types
├─ package.json
├─ README.md
├─ tuicall-uikit-vue.es.js
├─ tuicall-uikit-vue.umd.js
```
## 使用指引
为方便您的使用,本组件配套多篇使用指引:
- 如果您想要了解 TUICallKit,请阅读 [组件介绍 TUICallKit](https://cloud.tencent.com/document/product/647/78742)。
- 如果您想把我们的功能直接嵌入到您的项目中,请阅读 [快速接入 TUICallKit](https://cloud.tencent.com/document/product/647/78912)。
- 如果您想要了解详细 API,请阅读 [API 概览](https://cloud.tencent.com/document/product/647/78759)。
## 附录
- 如果你遇到了困难,可以先参阅 [常见问题](https://cloud.tencent.com/document/product/647/78733)。
- 如果发现了示例代码的 bug,欢迎提交 issue。
- 欢迎加入 QQ 群:**646165204**,进行技术交流和反馈~
-
@@ -0,0 +1,196 @@
import { TUIStore, StoreName, NAME } from "../index";
const {
CALL_STATUS,
CALL_ROLE,
CALL_MEDIA_TYPE,
LOCAL_USER_INFO,
REMOTE_USER_INFO_LIST,
CALLER_USER_INFO,
IS_GROUP,
CALL_DURATION,
PUSHER,
PLAYER,
BIG_SCREEN_USER_ID,
IS_EAR_PHONE,
LOCAL_VIDEO,
MYSELF,
TOAST_INFO,
} = NAME;
Component({
properties: {},
data: {
callStatus: TUIStore.getData(StoreName.CALL, CALL_STATUS), // 通话状态
isGroupCall: TUIStore.getData(StoreName.CALL, IS_GROUP), // 是否群通话
callMediaType: TUIStore.getData(StoreName.CALL, CALL_MEDIA_TYPE), // 通话类型
callDuration: TUIStore.getData(StoreName.CALL, CALL_DURATION), // 通话时长
callRole: TUIStore.getData(StoreName.CALL, CALL_ROLE), // 通话角色
isEarPhone: TUIStore.getData(StoreName.CALL, IS_EAR_PHONE), // 声音模式 听筒/扬声器
bigScreenUserId: TUIStore.getData(StoreName.CALL, BIG_SCREEN_USER_ID), // 大屏显示的用户
localUserInfo: TUIStore.getData(StoreName.CALL, LOCAL_USER_INFO), // 本地用户信息
callerUserInfo: TUIStore.getData(StoreName.CALL, CALLER_USER_INFO),// 邀请者用户信息
remoteUserInfoList: TUIStore.getData(StoreName.CALL, REMOTE_USER_INFO_LIST), // 远端用户信息
pusher: TUIStore.getData(StoreName.CALL, PUSHER), // TRTC 本地流
playerList: TUIStore.getData(StoreName.CALL, PLAYER), // TRTC 远端流
playerProcess: {}, // 经过处理的的远端流(多人通话)
},
methods: {
// 监听通话状态变更回调
handleCallStatusChange(value) {
this.setData({
callStatus: value,
});
},
// 监听是否群组通话变更回调
handleIsGroupChange(value) {
this.setData({
isGroupCall: value,
});
},
// 监听通话类型变更回调
handleCallMediaTypeChange(value) {
this.setData({
callMediaType: value,
});
},
// 监听通话角色变更回调
handleCallRoleChange(value) {
this.setData({
callRole: value,
});
},
// 监听通话时长变更回调
handleCallDurationChange(value) {
this.setData({
callDuration: value,
});
},
// 监听声音模式变更回调
handleEarPhoneChange(value) {
this.setData({
isEarPhone: value,
});
},
// 监听大屏显示的用户变更回调
handleScreenChange(value) {
if (value === LOCAL_VIDEO) {
this.setData({
bigScreenUserId: true,
});
} else {
this.setData({
bigScreenUserId: false,
});
}
},
// 监听本地用户信息变更回调
handleLocalUserInfoChange(value) {
this.setData({
localUserInfo: value,
});
},
// 监听到邀请者信息变更回调
handleCallerUserInfoChange(value) {
this.setData({
callerUserInfo: value,
});
},
// 监听远端用户信息变更回调
handleRemoteUserInfoListChange(value) {
this.setData({
remoteUserInfoList: value,
});
},
// 监听 TRTC 本地流变更回调
handlePusherChange(value) {
this.setData({
pusher: value,
});
},
// 监听 TRTC 远端流变更回调
handlePlayerListChange(value) {
if (this.data.isGroupCall) {
const convertToPlayer = this.convertToObj(value);
this.setData({
playerProcess: convertToPlayer,
});
} else {
this.setData({
playerList: value,
});
}
},
// 监听到弹窗信息变更回调
handleToastInfoChange(value) {
if (value.text) {
this.showToast(value.text, value.type || "info");
}
},
showToast(value, type) {
switch (type) {
case "info":
wx.showToast({
title: value,
icon: "none",
});
break;
default:
break;
}
},
convertToObj(arr = []) {
const tempObject = {};
for (let i = 0; i < arr.length; i++) {
tempObject[arr[i].userID] = arr[i];
}
return tempObject;
},
},
// 生命周期方法
lifetimes: {
attached() {
let that = this;
TUIStore.watch(
StoreName.CALL,
{
[CALL_STATUS]: this.handleCallStatusChange.bind(that),
[IS_GROUP]: this.handleIsGroupChange.bind(that),
[CALL_MEDIA_TYPE]: this.handleCallMediaTypeChange.bind(that),
[CALL_DURATION]: this.handleCallDurationChange.bind(that),
[CALL_ROLE]: this.handleCallRoleChange.bind(that),
[IS_EAR_PHONE]: this.handleEarPhoneChange.bind(that),
[BIG_SCREEN_USER_ID]: this.handleScreenChange.bind(that),
[LOCAL_USER_INFO]: this.handleLocalUserInfoChange.bind(that),
[CALLER_USER_INFO]: this.handleCallerUserInfoChange.bind(that),
[REMOTE_USER_INFO_LIST]: this.handleRemoteUserInfoListChange.bind(that),
[TOAST_INFO]: this.handleToastInfoChange.bind(that),
[PUSHER]: this.handlePusherChange.bind(that),
[PLAYER]: this.handlePlayerListChange.bind(that),
},
{
notifyRangeWhenWatch: MYSELF,
}
);
},
async detached() {
let that = this;
TUIStore.unwatch(StoreName.CALL, {
[CALL_STATUS]: this.handleCallStatusChange.bind(that),
[IS_GROUP]: this.handleIsGroupChange.bind(that),
[CALL_MEDIA_TYPE]: this.handleCallMediaTypeChange.bind(that),
[CALL_DURATION]: this.handleCallDurationChange.bind(that),
[CALL_ROLE]: this.handleCallRoleChange.bind(that),
[IS_EAR_PHONE]: this.handleEarPhoneChange.bind(that),
[BIG_SCREEN_USER_ID]: this.handleScreenChange.bind(that),
[LOCAL_USER_INFO]: this.handleLocalUserInfoChange.bind(that),
[CALLER_USER_INFO]: this.handleCallerUserInfoChange.bind(that),
[REMOTE_USER_INFO_LIST]: this.handleRemoteUserInfoListChange.bind(that),
[TOAST_INFO]: this.handleToastInfoChange.bind(that),
[PUSHER]: this.handlePusherChange.bind(that),
[PLAYER]: this.handlePlayerListChange.bind(that),
});
},
},
});
@@ -0,0 +1,9 @@
{
"component": true,
"usingComponents": {
"SingleCall":"./component/SingleCall/SingleCall",
"GroupCall":"./component/GroupCall/GroupCall"
},
"navigationStyle": "custom",
"disableScroll": true
}
@@ -0,0 +1,29 @@
<view wx:if="{{callStatus !== 'idle'}}" class="TUICall-container">
<SingleCall
wx:if="{{!isGroupCall}}"
callRole="{{callRole}}"
callStatus ='{{callStatus}}'
pusher="{{pusher}}"
bigScreenUserId="{{bigScreenUserId}}"
playerList="{{playerList}}"
callDuration="{{callDuration}}"
callMediaType="{{callMediaType}}"
localUserInfo="{{localUserInfo}}"
remoteUserInfoList="{{remoteUserInfoList}}"
isEarPhone="{{isEarPhone}}"
></SingleCall>
<GroupCall
wx:if="{{isGroupCall}}"
callRole="{{callRole}}"
callStatus ='{{callStatus}}'
callMediaType="{{callMediaType}}"
callDuration="{{callDuration}}"
pusher="{{pusher}}"
playerList="{{playerList}}"
localUserInfo="{{localUserInfo}}"
callerUserInfo="{{callerUserInfo}}"
remoteUserInfoList="{{remoteUserInfoList}}"
playerProcess="{{playerProcess}}"
isEarPhone="{{isEarPhone}}"
></GroupCall>
</view>
@@ -0,0 +1,10 @@
.TUICall-container {
width: 100vw;
height: 100vh;
overflow: hidden;
position: absolute;
top: 0;
left: 0;
z-index: 10;
margin: 0;
}
@@ -0,0 +1,116 @@
import { TUICallKitServer } from "../../../TUICallService/index";
const PATH = '../../../static';
Component({
properties: {
callRole: {
type: String,
},
callStatus: {
type: String,
},
callMediaType: {
type: Number,
},
callDuration: {
type: String,
},
pusher: {
type: Object,
},
playerList: {
type: Array,
},
localUserInfo: {
type: Object,
},
callerUserInfo: {
type: Object
},
remoteUserInfoList: {
type: Array,
},
playerProcess: {
type: Object,
},
isEarPhone: {
type: Boolean,
},
},
data: {
renderStreamList: [],
IMG_DEFAULT_AVATAR:`${PATH}/default_avatar.png`,
IMG_LOADING:`${PATH}/loading.png`,
IMG_HANGUP:`${PATH}/hangup.png`,
IMG_ACCEPT: `${PATH}/dialing.png`,
IMG_SPEAKER_FALSE:`${PATH}/speaker-false.png`,
IMG_SPEAKER_TRUE:`${PATH}/speaker-true.png`,
IMG_AUDIO_TRUE:`${PATH}/audio-true.png`,
IMG_AUDIO_FALSE:`${PATH}/audio-false.png`,
IMG_CAMERA_TRUE:`${PATH}/camera-true.png`,
IMG_CAMERA_FALSE:`${PATH}/camera-false.png`,
IMG_TRANS:`${PATH}/trans.png`,
IMG_SWITCH_CAMERA:`${PATH}/switch_camera.png`,
},
observers: {
"localUserInfo, remoteUserInfoList": function (
localUserInfo,
remoteUserInfoList
) {
this.setData({
renderStreamList: [localUserInfo, ...remoteUserInfoList],
});
},
},
methods: {
async accept() {
await TUICallKitServer.accept();
},
async hangup() {
await TUICallKitServer.hangup();
},
async reject() {
await TUICallKitServer.reject();
},
async switchCamera() {
await TUICallKitServer.switchCamera();
},
async microPhoneHandler() {
if (this.data.localUserInfo.isAudioAvailable) {
await TUICallKitServer.closeMicrophone();
} else {
await TUICallKitServer.openMicrophone();
}
},
async cameraHandler() {
if (this.data.localUserInfo.isVideoAvailable) {
await TUICallKitServer.closeCamera();
} else {
await TUICallKitServer.openCamera();
}
},
async toggleSoundMode() {
await TUICallKitServer.setSoundMode();
},
pusherStateChangeHandler(e) {
TUICallKitServer._tuiCallEngine._pusherStateChangeHandler(e);
},
pusherNetStatus(e) {
TUICallKitServer._tuiCallEngine._pusherNetStatus(e);
},
pusherErrorHandler(e) {
TUICallKitServer._tuiCallEngine._pusherNetStatus(e);
},
pusherAudioVolumeNotify(e) {
TUICallKitServer._tuiCallEngine._pusherAudioVolumeNotify(e);
},
playerStateChange(e) {
TUICallKitServer._tuiCallEngine._playerStateChange(e);
},
playNetStatus(e) {
TUICallKitServer._tuiCallEngine._playNetStatus(e);
},
playerAudioVolumeNotify(e) {
TUICallKitServer._tuiCallEngine._playerAudioVolumeNotify(e);
},
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,285 @@
<!-- 语音通话 -->
<view class="incoming-call audio-call transition-animation" wx:if="{{callMediaType === 1}}">
<!-- 主叫方呼叫状态 -->
<swiper class="swiper" wx:if="{{callRole === 'caller' && callStatus === 'calling' }}" indicator-dots="{{renderStreamList.length/4 > 1}}" indicator-color="white" indicator-active-color="black">
<block wx:for="{{(renderStreamList.length)/4}}" wx:key="*this" wx:for-index="pos">
<swiper-item class="invite-calling-list">
<view wx:for="{{renderStreamList}}" wx:key="userID" class="invite-calling-item" wx:if="{{index >= pos*4 && index < pos*4+4}}">
<view id="{{item.userId}}" class="invite-calling-item-message" wx:if="{{item.userId !== localUserInfo.userId}}">
<view class="invite-calling-item-loadimg">
<image src="{{IMG_LOADING}}"></image>
</view>
<view class="invite-calling-item-id">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-calling-item-id">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</swiper-item>
</block>
</swiper>
<!-- 被叫方呼叫状态 -->
<view wx:if="{{callRole !== 'caller' && callStatus === 'calling' }}">
<view class="invite-calling-single">
<image class="avatar" src="{{callerUserInfo.avatar || IMG_DEFAULT_AVATAR}}" id="{{renderStreamList[0].userId}}" binderror="handleErrorImage" />
<view class="tips">{{callerUserInfo.nick || callerUserInfo.userId}}</view>
<view class="invite-txt">邀请你参加多人通话</view>
</view>
<view class="invite-other-txt">参与通话的有:</view>
<view class="invite-other-list">
<view class="invite-other-item" wx:for="{{renderStreamList}}" wx:key="item">
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-other-item-name">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</view>
</view>
<!-- 主叫/被叫方通话状态 -->
<view wx:if="{{callStatus === 'connected' }}">
<view>
<swiper class="swiper" indicator-dots="{{renderStreamList.length/4 > 1}}" indicator-color="white" indicator-active-color="black">
<block wx:for="{{(renderStreamList.length)/4}}" wx:key="*this" wx:for-index="pos">
<swiper-item class="invite-calling-list">
<view wx:for="{{renderStreamList}}" wx:key="userID" class="invite-calling-item" wx:if="{{index >= pos*4 && index < pos*4+4}}">
<view id="{{item.userId}}" class="invite-calling-item-message" wx:if="{{!item.isEnter}}">
<view class="invite-calling-item-loadimg">
<image src="{{IMG_LOADING}}"></image>
</view>
<view class="invite-calling-item-id">
{{item.displayUserInfo || item.nick || item.userId}}
</view>
</view>
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="player-control">
<image src="{{item.avatar || IMG_DEFAULT_AVATAR}}"></image>
<view class="name">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</view>
</swiper-item>
</block>
</swiper>
</view>
<view class="pusher-audio">
<swiper class="swiper" indicator-dots="{{renderStreamList.length/4 > 1}}" indicator-color="white" indicator-active-color="black">
<block wx:for="{{(renderStreamList.length)/4}}" wx:key="*this" wx:for-index="pos">
<swiper-item class="invite-calling-list">
<view wx:for="{{renderStreamList}}" wx:key="userID" class="invite-calling-item" wx:if="{{index >= pos*4 && index < pos*4+4}}">
<view id="{{item.userId}}" class="invite-calling-item-message" wx:if="{{!item.isEnter}}">
<view class="invite-calling-item-loadimg">
<image src="{{IMG_LOADING}}"></image>
</view>
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-calling-item-id">
{{item.displayUserInfo || item.nick || item.userId}}
</view>
</view>
<view wx:else>
<!-- 本地流 -->
<view wx:if="{{item.userId===localUserInfo.userId}}" class="pusher-audio" data-screen="pusher" catch:tap="toggleViewSize">
<live-pusher class="pusher-audio" url="{{pusher.url}}" mode="{{pusher.mode}}" autopush="{{true}}" enable-camera="{{pusher.enableCamera}}" enable-mic="{{true}}" muted="{{!pusher.enableMic}}" enable-agc="{{true}}" enable-ans="{{true}}" enable-ear-monitor="{{pusher.enableEarMonitor}}" auto-focus="{{pusher.enableAutoFocus}}" zoom="{{pusher.enableZoom}}" min-bitrate="{{pusher.minBitrate}}" max-bitrate="{{pusher.maxBitrate}}" video-width="{{pusher.videoWidth}}" video-height="{{pusher.videoHeight}}" beauty="{{pusher.beautyLevel}}" whiteness="{{pusher.whitenessLevel}}" orientation="{{pusher.videoOrientation}}" aspect="{{pusher.videoAspect}}" device-position="{{pusher.frontCamera}}" remote-mirror="{{pusher.enableRemoteMirror}}" local-mirror="{{pusher.localMirror}}" background-mute="{{pusher.enableBackgroundMute}}" audio-quality="{{pusher.audioQuality}}" audio-volume-type="{{pusher.audioVolumeType}}" audio-reverb-type="{{pusher.audioReverbType}}" waiting-image="{{pusher.waitingImage}}" beauty-style="{{pusher.beautyStyle}}" filter="{{pusher.filter}}" bindstatechange="pusherStateChangeHandler" bindnetstatus="pusherNetStatus" binderror="pusherErrorHandler" bindaudiovolumenotify="pusherAudioVolumeNotify" />
<view class="player-control">
<image src="{{item.avatar || IMG_DEFAULT_AVATAR}}"></image>
<view class="name">我</view>
</view>
</view>
<!-- 远端流 -->
<view class="pusher-audio" wx:else>
<live-player wx:if="{{playerProcess[item.userId]}}" wx:if="{{ playerProcess[item.userId].hasAudio || playerProcess[item.userId].hasVideo }}" class="{{callMediaType === 1 ? 'pusher-audio' : 'pusher-ownvideo'}}" id="{{playerProcess[item.userId].id}}" data-userid="{{playerProcess[item.userId].userID}}" data-streamid="{{playerProcess[item.userId].streamID}}" data-streamtype="{{playerProcess[item.userId].streamType}}" src="{{playerProcess[item.userId].src}}" mode="RTC" autoplay="{{playerProcess[item.userId].autoplay}}" mute-audio="{{playerProcess[item.userId].muteAudio}}" mute-video="{{playerProcess[item.userId].muteVideo}}" orientation="{{playerProcess[item.userId].orientation}}" object-fit="{{playerProcess[item.userId].objectFit}}" background-mute="{{playerProcess[item.userId].enableBackgroundMute}}" min-cache="{{playerProcess[item.userId].minCache}}" max-cache="{{playerProcess[item.userId].maxCache}}" sound-mode="{{isEarPhone?'ear':'speaker'}}" enable-recv-message="{{playerProcess[item.userId].enableRecvMessage}}" auto-pause-if-navigate="{{playerProcess[item.userId].autoPauseIfNavigate}}" auto-pause-if-open-native="{{playerProcess[item.userId].autoPauseIfOpenNative}}" bindstatechange="playerStateChange" bindfullscreenchange="playerFullscreenChange" bindnetstatus="playNetStatus" bindaudiovolumenotify="playerAudioVolumeNotify" />
</view>
</view>
</view>
</swiper-item>
</block>
</swiper>
</view>
</view>
<!-- 呼叫阶段按钮 -->
<view wx:if="{{callStatus === 'calling'}}" class="footer">
<view wx:if="{{callRole !== 'caller'}}" class="btn-operate">
<view class="button-container">
<view class="call-operate" style="background-color: red" catch:tap="reject">
<image src="{{IMG_HANGUP}}" />
</view>
<view style="margin-top:10px;color: #666666">挂断</view>
</view>
<view class="button-container">
<view class="call-operate" catch:tap="accept">
<image src="{{IMG_ACCEPT}}" />
</view>
<view style="margin-top:10px;color: #666666">接听</view>
</view>
</view>
<view wx:if="{{callRole === 'caller'}}" class="btn-operate">
<view class="button-container">
<view class="call-operate" style="background-color: red" catch:tap="hangup">
<image src="{{IMG_HANGUP}}" />
</view>
<view style="margin-top:10px;color: #666666">挂断</view>
</view>
</view>
</view>
<!-- 通话阶段按钮 -->
<view wx:if="{{callStatus === 'connected'}}" class="handle-btns">
<view class="other-view black">
<text>{{callDuration}}</text>
</view>
<view class="btn-list">
<view class="button-container">
<view class="btn-normal" bindtap="microPhoneHandler">
<image class="btn-image" src="{{localUserInfo.isAudioAvailable? IMG_AUDIO_TRUE: IMG_AUDIO_FALSE}} "></image>
</view>
<view>麦克风</view>
</view>
<view class="button-container">
<view class="btn-hangup" bindtap="hangup">
<image class="btn-image" src="{{IMG_HANGUP}}"></image>
</view>
<view>挂断</view>
</view>
<view class="button-container">
<view class="btn-normal" bindtap="toggleSoundMode">
<image class="btn-image" src="{{isEarPhone ? IMG_SPEAKER_FALSE: IMG_SPEAKER_TRUE}} "></image>
</view>
<text>扬声器</text>
</view>
</view>
</view>
</view>
<!-- 视频通话 -->
<view class="invite-call audio-call transition-animation" wx:if="{{callMediaType === 2}}">
<!-- 通话状态 -->
<view wx:if="{{callStatus === 'connected' }}">
<swiper class="swiper" indicator-dots="{{renderStreamList.length/4 > 1}}" indicator-color="white" indicator-active-color="black">
<block wx:for="{{(renderStreamList.length)/4}}" wx:key="*this" wx:for-index="pos">
<swiper-item class="invite-calling-list">
<view wx:for="{{renderStreamList}}" wx:key="userID" class="invite-calling-item" wx:if="{{index >= pos*4 && index < pos*4+4}}">
<view id="{{item.userId}}" class="invite-calling-item-message" wx:if="{{!item.isEnter}}">
<view class="invite-calling-item-loadimg">
<image src="{{IMG_LOADING}}"></image>
</view>
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-calling-item-id">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
<view wx:else>
<!-- 本地流 -->
<view wx:if="{{item.userId === localUserInfo.userId}}" class="play-item" data-screen="pusher">
<live-pusher class="pusher-ownvideo" url="{{pusher.url}}" mode="{{pusher.mode}}" autopush="{{true}}" enable-camera="{{pusher.enableCamera}}" enable-mic="{{true}}" muted="{{!pusher.enableMic}}" enable-agc="{{true}}" enable-ans="{{true}}" enable-ear-monitor="{{pusher.enableEarMonitor}}" auto-focus="{{pusher.enableAutoFocus}}" zoom="{{pusher.enableZoom}}" min-bitrate="{{pusher.minBitrate}}" max-bitrate="{{pusher.maxBitrate}}" video-width="{{pusher.videoWidth}}" video-height="{{pusher.videoHeight}}" beauty="{{pusher.beautyLevel}}" whiteness="{{pusher.whitenessLevel}}" orientation="{{pusher.videoOrientation}}" aspect="{{pusher.videoAspect}}" device-position="{{pusher.frontCamera}}" remote-mirror="{{pusher.enableRemoteMirror}}" local-mirror="{{pusher.localMirror}}" background-mute="{{pusher.enableBackgroundMute}}" audio-quality="{{pusher.audioQuality}}" audio-volume-type="{{pusher.audioVolumeType}}" audio-reverb-type="{{pusher.audioReverbType}}" waiting-image="{{pusher.waitingImage}}" beauty-style="{{pusher.beautyStyle}}" filter="{{pusher.filter}}" bindstatechange="pusherStateChangeHandler" bindnetstatus="pusherNetStatus" binderror="pusherErrorHandler" bindaudiovolumenotify="pusherAudioVolumeNotify" />
<view class="player-control">
<image src="{{item.avatar || IMG_DEFAULT_AVATAR}}"></image>
<view class="name">我</view>
</view>
</view>
<!-- 远端流 -->
<view class="play-item" wx:else>
<live-player class="pusher-ownvideo" wx:if="{{playerProcess[item.userId]}}" wx:if="{{ playerProcess[item.userId].hasAudio || playerProcess[item.userId].hasVideo }}" id="{{playerProcess[item.userId].id}}" data-userid="{{playerProcess[item.userId].userID}}" data-streamid="{{playerProcess[item.userId].streamID}}" data-streamtype="{{playerProcess[item.userId].streamType}}" src="{{playerProcess[item.userId].src}}" mode="RTC" autoplay="{{playerProcess[item.userId].autoplay}}" mute-audio="{{playerProcess[item.userId].muteAudio}}" mute-video="{{playerProcess[item.userId].muteVideo}}" orientation="{{playerProcess[item.userId].orientation}}" object-fit="{{playerProcess[item.userId].objectFit}}" background-mute="{{playerProcess[item.userId].enableBackgroundMute}}" min-cache="{{playerProcess[item.userId].minCache}}" max-cache="{{playerProcess[item.userId].maxCache}}" sound-mode="{{isEarPhone?'ear':'speaker'}}" enable-recv-message="{{playerProcess[item.userId].enableRecvMessage}}" auto-pause-if-navigate="{{playerProcess[item.userId].autoPauseIfNavigate}}" auto-pause-if-open-native="{{playerProcess[item.userId].autoPauseIfOpenNative}}" bindstatechange="playerStateChange" bindfullscreenchange="playerFullscreenChange" bindnetstatus="playNetStatus" bindaudiovolumenotify="playerAudioVolumeNotify" />
<view class="player-control">
<image src="{{item.avatar || IMG_DEFAULT_AVATAR}}"></image>
<view class="name">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</view>
</view>
</view>
</swiper-item>
</block>
</swiper>
</view>
<!-- 邀请信息 -->
<view class="invite-calling">
<!-- 主叫方 -->
<swiper class="swiper" wx:if="{{callRole === 'caller' && callStatus === 'calling' }}" indicator-dots="{{renderStreamList.length/4 > 1}}" indicator-color="white" indicator-active-color="black">
<block wx:for="{{(renderStreamList.length)/4}}" wx:key="*this" wx:for-index="pos">
<swiper-item class="invite-calling-list">
<view wx:for="{{renderStreamList}}" wx:key="userID" class="invite-calling-item" wx:if="{{index >= pos*4 && index < pos*4+4}}">
<view id="{{item.userId}}" class="invite-calling-item-message" wx:if="{{item.userId !== localUserInfo.userId}}">
<view class="invite-calling-item-loadimg">
<image src="{{IMG_LOADING}}"></image>
</view>
<view class="invite-calling-item-id">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-calling-item-id">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</swiper-item>
</block>
</swiper>
<!-- 被叫方 -->
<view wx:if="{{callRole !== 'caller' && callStatus === 'calling' }}">
<view class="invite-calling-single">
<image class="avatar" src="{{callerUserInfo.avatar || IMG_DEFAULT_AVATAR}}" id="{{renderStreamList[0].userId}}" binderror="handleErrorImage" />
<view class="tips">{{ callerUserInfo.nick || callerUserInfo.userId }}</view>
<view class="invite-txt">邀请你参加多人通话</view>
</view>
<view class="invite-other-txt">参与通话的有:</view>
<view class="invite-other-list">
<view class="invite-other-item" wx:for="{{renderStreamList}}" wx:key="item">
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-other-item-name">{{ item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</view>
</view>
<!-- 呼叫阶段按钮 -->
<view wx:if="{{callStatus === 'calling' }}" class="footer">
<view class="btn-operate" wx:if="{{callRole === 'caller'}}">
<view class="btn-operate-item">
<view class="btn-container">
<view class="call-operate" catch:tap="hangup">
<image src="{{IMG_HANGUP}}" />
</view>
</view>
<text style="color: #666666">挂断</text>
</view>
</view>
<view class="btn-operate" wx:if="{{callRole !== 'caller'}}">
<view class="btn-operate-item">
<view class="call-operate" style="background-color: red" catch:tap="reject">
<image src="{{IMG_HANGUP}}" />
</view>
<text style="color: #666666">挂断</text>
</view>
<view class="btn-operate-item">
<view class="call-operate" catch:tap="accept">
<image src="{{IMG_ACCEPT}}" />
</view>
<text style="color: #666666">接听</text>
</view>
</view>
</view>
<!-- 通话阶段按钮 -->
<view wx:if="{{callStatus === 'connected' }}" class="handle-btns">
<view class="other-view white">
<text>{{callDuration}}</text>
</view>
<view class="btn-list">
<view class="button-container">
<view class="btn-normal" bindtap="microPhoneHandler">
<image class="btn-image" src="{{localUserInfo.isAudioAvailable ? IMG_AUDIO_TRUE: IMG_AUDIO_FALSE}} "></image>
</view>
<view class="white">麦克风</view>
</view>
<view class="button-container">
<view class="btn-normal" bindtap="toggleSoundMode">
<image class="btn-image" src="{{isEarPhone ? IMG_SPEAKER_FALSE: IMG_SPEAKER_TRUE}} "></image>
</view>
<text class="white">扬声器</text>
</view>
<view class="button-container">
<view class="btn-normal" bindtap="cameraHandler">
<image class="btn-image" src="{{localUserInfo.isVideoAvailable? IMG_CAMERA_TRUE: IMG_CAMERA_FALSE}} "></image>
</view>
<text class="white">摄像头</text>
</view>
</view>
<view class="btn-list">
<view class="btn-list-item other-view">
<view class="btn-container">
<view class="btn-hangup" bindtap="hangup">
<image class="btn-image" src="{{IMG_HANGUP}}"></image>
</view>
<view wx:if="{{pusher.enableCamera}}" class="invite-calling-header-left">
<image src="{{IMG_SWITCH_CAMERA}}" data-device="{{pusher.frontCamera}}" catch:tap="toggleSwitchCamera" />
</view>
</view>
<text class="white">挂断</text>
</view>
</view>
</view>
</view>
</view>
@@ -0,0 +1,754 @@
.transition-animation {
transform: translateY(-100%);
animation: slideInDown 0.5s ease forwards;
}
@keyframes slideInDown {
from {
transform: translateY(-100%);
}
to {
transform: translateY(0);
}
}
.footer {
position: absolute;
bottom: 5vh;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.button-container {
display: flex;
flex-direction: column;
text-align: center;
}
.btn-operate {
display: flex;
justify-content: space-between;
}
.btn-operate-item {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
}
.btn-operate-item text {
font-size: 14px;
color: #f0e9e9;
padding: 5px;
letter-spacing: 0;
font-weight: 400;
}
.call-switch text {
padding: 5px;
color: #f0e9e9;
font-size: 14px;
}
.call-operate {
width: 8vh;
height: 8vh;
border-radius: 8vh;
margin: 0 15vw;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
}
.call-switch .call-operate {
width: 4vh;
height: 3vh;
}
.call-operate image {
width: 100%;
height: 100%;
background: none;
}
.tips {
font-size: 20px;
color: #ffffff;
letter-spacing: 0;
margin: 0 auto;
font-weight: 600;
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tips-subtitle {
font-family: PingFangSC-Regular;
font-size: 14px;
color: #ffffff;
letter-spacing: 0;
text-align: right;
font-weight: 400;
}
.invite-call {
position: absolute;
top: 0;
z-index: 100;
width: 100%;
height: 187px;
}
.invite-call .local-video {
width: 100px;
height: 187px;
}
.invite-call .invite-calling {
position: absolute;
top: 0;
z-index: 101;
width: 100vw;
height: 100vh;
}
.invite-calling-header {
margin-top: 107px;
display: flex;
justify-content: flex-end;
padding: 0 16px;
}
.btn-container {
display: flex;
align-items: center;
position: relative;
}
.invite-calling-header-left {
position: absolute;
right: 0;
}
.invite-calling-header-left image {
width: 32px;
height: 32px;
}
.invite-calling-header-right {
display: flex;
align-items: center;
}
.invite-calling-header-message {
display: flex;
flex-direction: column;
padding: 0 16px;
}
.invite-calling-header-right image {
width: 100px;
height: 100px;
border-radius: 12px;
}
.invite-calling .footer {
position: absolute;
bottom: 5vh;
width: 100%;
}
.invite-calling .btn-operate {
display: flex;
justify-content: center;
align-items: center;
}
.hidden {
display: none;
}
.trtc-calling {
width: 100vw;
height: 100vh;
overflow: hidden;
margin: 0;
z-index: 99;
}
.audio-call {
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
z-index: 100;
background: #ffffff;
}
.audio-call > .btn-operate {
display: flex;
justify-content: center;
}
.audio-call > image {
width: 40vw;
height: 40vw;
display: block;
margin: 20vw 30vw;
margin-top: 40vw;
}
.invite-calling-single > image {
width: 120px;
height: 120px;
border-radius: 12px;
display: block;
margin: 120px auto 15px;
}
.invite-calling-single .tips {
width: 100%;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 20px;
color: #333333;
letter-spacing: 0;
font-weight: 500;
}
.invite-calling-single .tips-subtitle {
height: 20px;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #97989c;
letter-spacing: 0;
font-weight: 400;
text-align: center;
}
.swiper {
margin-top: 107px;
min-height: 374px;
}
.invite-calling-list {
display: flex;
flex-wrap: wrap;
width: 100%;
justify-content: flex-start;
}
.invite-calling-item {
flex: 0.5;
min-width: 50%;
height: 187px;
position: relative;
}
.invite-calling-item image {
width: 100%;
height: 100%;
}
.invite-calling-item-message {
position: absolute;
top: 0;
left: 0;
float: left;
background: rgba(0, 0, 0, 0.6);
width: 100%;
height: 100%;
z-index: 2;
}
.invite-calling-item-loadimg {
position: absolute;
left: calc(50% - 20px);
top: calc(50% - 20px);
width: 40px;
height: 40px;
-webkit-transform: rotate(360deg);
animation: rotation 2s linear infinite;
-moz-animation: rotation 2s linear infinite;
-webkit-animation: rotation 2s linear infinite;
-o-animation: rotation 2s linear infinite;
}
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
}
}
.invite-calling-item-loadimg image {
width: 100%;
height: 100%;
}
.invite-calling-item-id {
position: absolute;
left: 2%;
bottom: 2%;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #ffffff;
}
.avatar {
background-color: black;
}
/* 被叫者 */
.invite-txt {
width: 126px;
height: 20px;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 14px;
color: #333333;
letter-spacing: 0;
margin: 16px auto 60px auto;
}
.invite-other-txt {
width: 112px;
height: 20px;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 14px;
color: #333333;
letter-spacing: 0;
margin: 0 auto 24px auto;
}
.invite-other-list {
position: absolute;
left: 14vw;
margin-top: 0 auto;
display: flex;
flex-wrap: wrap;
width: 272px;
justify-content: center;
flex-wrap: wrap;
}
.invite-other-item {
flex: 0.25;
text-align: center;
max-width: 64px;
margin: 2px;
}
.invite-other-item image {
border-radius: 10%;
max-width: 64px;
height: 64px;
}
.invite-other-item-name {
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #666666;
letter-spacing: 0;
line-height: 18px;
}
/* 全屏设置 */
.TUICalling-connected-layout {
width: 100%;
height: 100%;
}
.TUICalling-connected-video {
width: 100%;
height: 180%;
}
/* 本地音频 */
.stream-box {
float: left;
width: 187px;
height: 187px;
position: absolute;
top: 13vh;
}
/* 远端音频列表 */
.swiper {
margin-top: 107px;
min-height: 374px;
}
.invite-calling-list {
display: flex;
flex-wrap: wrap;
width: 100%;
justify-content: flex-start;
}
.invite-calling-item {
flex: 0.5;
min-width: 50%;
height: 187px;
position: relative;
}
.invite-calling-item image {
width: 100%;
height: 100%;
}
/* 本地视频 */
.play-item {
width: 100%;
height: 187px;
position: relative;
background-color: #000000;
}
.pusher-ownvideo {
width: 100%;
height: 100%;
}
/* 远端视频列表 */
.swiper-list {
min-height: 189px;
}
.player-list {
display: flex;
flex-wrap: wrap;
width: 100%;
justify-content: center;
}
.player-item {
flex: 0.5;
min-width: 50%;
min-height: 187px;
}
/* 音量图标 */
.player-control {
background-color: rgba(0, 0, 0, 0.4);
border-radius: 0 6px 6px 0;
color: #fff;
z-index: 999;
position: absolute;
bottom: 0px;
left: 0px;
display: flex;
align-items: center;
height: 32px;
max-width: 50%;
z-index: 99;
}
.player-control image {
width: 32px;
height: 32px;
}
.player-control .name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-left: 10px;
margin-right: 10px;
flex: 1;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #ffffff;
}
.control-buttons {
display: flex;
}
.icon-button {
margin-right: 10px;
}
/* 菜单 */
.handle-btns {
position: absolute;
bottom: 44px;
width: 100vw;
z-index: 3;
display: flex;
flex-direction: column;
}
.handle-btns .btn-list {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
}
.button-container {
display: flex;
flex-direction: column;
text-align: center;
}
.btn-normal {
width: 8vh;
height: 8vh;
box-sizing: border-box;
display: flex;
flex-direction: column;
/* background: white; */
justify-content: center;
align-items: center;
border-radius: 50%;
}
.btn-image {
width: 100%;
height: 100%;
background: none;
}
.btn-hangup {
width: 8vh;
height: 8vh;
/*background: #f75c45;*/
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
}
.btn-hangup > .btn-image {
width: 100%;
height: 100%;
background: none;
}
.TRTCCalling-call-audio {
width: 100%;
height: 100%;
}
.btn-footer {
position: relative;
}
.btn-footer .multi-camera {
width: 32px;
height: 32px;
}
.btn-footer .camera {
width: 64px;
height: 64px;
position: fixed;
left: 16px;
top: 107px;
display: flex;
justify-content: center;
align-items: center;
background: rgba(255, 255, 255, 0.7);
}
.btn-footer .camera .camera-image {
width: 32px;
height: 32px;
}
.audio {
padding-top: 15vh;
background: #ffffff;
}
.pusher-audio {
width: 0;
height: 0;
}
.player-audio {
width: 0;
height: 0;
}
.other-view {
display: flex;
flex-direction: column;
align-items: center;
font-size: 18px;
letter-spacing: 0;
font-weight: 400;
padding: 16px;
}
.white {
font-weight: 400;
font-size: 14px;
color: #666666;
padding: 5px;
}
.black {
color: #000000;
padding: 5px;
}
.TRTCCalling-call-audio-box {
margin-top: 100px;
display: flex;
flex-wrap: wrap;
width: 100%;
justify-content: center;
}
.TRTCCalling-calling-list {
flex: 0.5;
/*设置最小宽度,才会让元素排不下,导致换行排列*/
min-width: 50%;
min-height: 187px;
position: relative;
}
.TRTCCalling-calling-list image {
width: 100%;
height: 100%;
}
.TRTCCalling-calling-item-id {
position: absolute;
left: 2%;
bottom: 2%;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #ffffff;
}
.btn-list-item {
flex: 1;
display: flex;
justify-content: center;
padding: 16px 0;
}
.btn-image-small {
transform: scale(0.7);
}
.avatar {
background: #dddddd;
}
.btn-container {
display: flex;
align-items: center;
position: relative;
}
.invite-calling-header-left {
position: absolute;
right: -88px;
}
.invite-calling-header-left image {
width: 32px;
height: 32px;
}
.call-switch .call-operate {
width: 4vh;
height: 3vh;
}
.call-operate image {
width: 100%;
height: 100%;
background: none;
}
.call-switch text {
padding: 0;
font-size: 14px;
}
.btn-operate-item {
display: flex;
flex-direction: column;
align-items: center;
}
.btn-operate-item text {
padding: 8px 0;
font-size: 18px;
color: #666666;
letter-spacing: 0;
font-weight: 400;
font-size: 14px;
}
.invite-calling-item-message {
position: absolute;
top: 0;
left: 0;
float: left;
background: rgba(0, 0, 0, 0.6);
width: 100%;
height: 100%;
z-index: 2;
}
.invite-calling-item-loadimg {
position: absolute;
left: calc(50% - 20px);
top: calc(50% - 20px);
width: 40px;
height: 40px;
-webkit-transform: rotate(360deg);
animation: rotation 2s linear infinite;
-moz-animation: rotation 2s linear infinite;
-webkit-animation: rotation 2s linear infinite;
-o-animation: rotation 2s linear infinite;
}
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
}
}
.invite-calling-item-loadimg image {
width: 100%;
height: 100%;
}
.invite-calling-item-id {
position: absolute;
left: 2%;
bottom: 2%;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #ffffff;
}
@@ -0,0 +1,107 @@
import { TUICallKitServer } from "../../../TUICallService/index";
const PATH = '../../../static';
Component({
properties: {
callRole: {
type: String,
},
callStatus: {
type: String,
},
callMediaType: {
type: Number,
},
callDuration: {
type: String,
},
pusher: {
type: Object,
},
playerList: {
type: Array,
},
localUserInfo: {
type:Object
},
remoteUserInfoList: {
type: Array,
},
isEarPhone: {
type: Boolean,
},
bigScreenUserId: {
type: Boolean
}
},
data:{
IMG_DEFAULT_AVATAR:`${PATH}/default_avatar.png`,
IMG_HANGUP:`${PATH}/hangup.png`,
IMG_ACCEPT: `${PATH}/dialing.png`,
IMG_SPEAKER_FALSE:`${PATH}/speaker-false.png`,
IMG_SPEAKER_TRUE:`${PATH}/speaker-true.png`,
IMG_AUDIO_TRUE:`${PATH}/audio-true.png`,
IMG_AUDIO_FALSE:`${PATH}/audio-false.png`,
IMG_CAMERA_TRUE:`${PATH}/camera-true.png`,
IMG_CAMERA_FALSE:`${PATH}/camera-false.png`,
IMG_TRANS:`${PATH}/trans.png`,
IMG_SWITCH_CAMERA:`${PATH}/switch_camera.png`,
},
methods: {
async accept() {
await TUICallKitServer.accept();
},
async hangup() {
await TUICallKitServer.hangup();
},
async reject() {
await TUICallKitServer.reject();
},
async switchCamera() {
await TUICallKitServer.switchCamera();
},
async switchCallMediaType() {
await TUICallKitServer.switchCallMediaType();
},
async microPhoneHandler() {
if (this.data.localUserInfo.isAudioAvailable) {
await TUICallKitServer.closeMicrophone();
} else {
await TUICallKitServer.openMicrophone();
}
},
async cameraHandler() {
if (this.data.localUserInfo.isVideoAvailable) {
await TUICallKitServer.closeCamera();
} else {
await TUICallKitServer.openCamera();
}
},
async toggleSoundMode() {
await TUICallKitServer.setSoundMode();
},
toggleViewSize() {
TUICallKitServer.switchScreen(this.data.bigScreenUserId ? 'player':'localVideo')
},
pusherStateChangeHandler(e) {
TUICallKitServer._tuiCallEngine._pusherStateChangeHandler(e);
},
pusherNetStatus(e) {
TUICallKitServer._tuiCallEngine._pusherNetStatus(e);
},
pusherErrorHandler(e) {
TUICallKitServer._tuiCallEngine._pusherNetStatus(e);
},
pusherAudioVolumeNotify(e) {
TUICallKitServer._tuiCallEngine._pusherAudioVolumeNotify(e);
},
playerStateChange(e) {
TUICallKitServer._tuiCallEngine._playerStateChange(e);
},
playNetStatus(e) {
TUICallKitServer._tuiCallEngine._playNetStatus(e);
},
playerAudioVolumeNotify(e) {
TUICallKitServer._tuiCallEngine._playerAudioVolumeNotify(e);
},
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,172 @@
<!-- 音频通话 -->
<view class="{{callMediaType === 1?'audio-call':'invite-call' }} transition-animation">
<!-- 本地流 -->
<view class="{{callMediaType === 1 ? 'pusher-audio' : (bigScreenUserId || callStatus === 'calling' ? 'pusher-video' : 'player')}}" data-screen="pusher" catch:tap="toggleViewSize">
<live-pusher class="{{callMediaType === 1?'pusher-audio':'live'}}" url="{{pusher.url}}" mode="{{pusher.mode}}" autopush="{{true}}" enable-camera="{{pusher.enableCamera}}" enable-mic="{{true}}" muted="{{!pusher.enableMic}}" enable-agc="{{true}}" enable-ans="{{true}}" enable-ear-monitor="{{pusher.enableEarMonitor}}" auto-focus="{{pusher.enableAutoFocus}}" zoom="{{pusher.enableZoom}}" min-bitrate="{{pusher.minBitrate}}" max-bitrate="{{pusher.maxBitrate}}" video-width="{{pusher.videoWidth}}" video-height="{{pusher.videoHeight}}" beauty="{{pusher.beautyLevel}}" whiteness="{{pusher.whitenessLevel}}" orientation="{{pusher.videoOrientation}}" aspect="{{pusher.videoAspect}}" device-position="{{pusher.frontCamera}}" remote-mirror="{{pusher.enableRemoteMirror}}" local-mirror="{{pusher.localMirror}}" background-mute="{{pusher.enableBackgroundMute}}" audio-quality="{{pusher.audioQuality}}" audio-volume-type="{{pusher.audioVolumeType}}" audio-reverb-type="{{pusher.audioReverbType}}" waiting-image="{{pusher.waitingImage}}" beauty-style="{{pusher.beautyStyle}}" filter="{{pusher.filter}}" bindstatechange="pusherStateChangeHandler" bindnetstatus="pusherNetStatus" binderror="pusherErrorHandler" bindaudiovolumenotify="pusherAudioVolumeNotify" />
</view>
<!-- 远端流 -->
<view wx:for="{{playerList}}" wx:key="streamID" class="view-container player-container" catch:tap="toggleViewSize">
<view class="{{callMediaType === 1 ? 'player-audio' : (!bigScreenUserId ? 'pusher-video' : 'player')}}" data-screen="player">
<live-player class="live" wx:if="{{ item.hasAudio || item.hasVideo }}" id="{{item.id}}" data-userid="{{item.userID}}" data-streamid="{{item.streamID}}" data-streamtype="{{item.streamType}}" src="{{item.src}}" mode="RTC" autoplay="{{item.autoplay}}" mute-audio="{{item.muteAudio}}" mute-video="{{item.muteVideo}}" orientation="{{item.orientation}}" object-fit="{{item.objectFit}}" background-mute="{{item.enableBackgroundMute}}" min-cache="{{item.minCache}}" max-cache="{{item.maxCache}}" sound-mode="{{isEarPhone?'ear':'speaker'}}" enable-recv-message="{{item.enableRecvMessage}}" auto-pause-if-navigate="{{item.autoPauseIfNavigate}}" auto-pause-if-open-native="{{item.autoPauseIfOpenNative}}" bindstatechange="playerStateChange" bindfullscreenchange="playerFullscreenChange" bindnetstatus="playNetStatus" bindaudiovolumenotify="playerAudioVolumeNotify" />
</view>
</view>
<!-- 语言通话邀请信息 -->
<view wx:if="{{callMediaType === 1}}" class="invite-calling-single">
<image class="avatar" src="{{remoteUserInfoList[0].avatar || IMG_DEFAULT_AVATAR}}" id="{{remoteUserInfoList[0].userId}}" binderror="handleErrorImage" />
<view class="tips">{{remoteUserInfoList[0].displayUserInfo}}</view>
<view wx:if="{{callRole === 'caller' && callStatus === 'calling' }}" class="tips-subtitle">等待对方接受</view>
</view>
<!-- 视频通话邀请信息 -->
<view wx:if="{{callStatus === 'calling' && callMediaType === 2}}" class="invite-calling">
<view class="invite-calling-header">
<view class="invite-calling-header-right">
<view class="invite-calling-header-message">
<label class="tips">
{{remoteUserInfoList[0].displayUserInfo}}
</label>
<text class="tips-subtitle" wx:if="{{callRole !== 'caller'}}">邀请你视频通话</text>
<text class="tips-subtitle" wx:else>等待对方接受</text>
</view>
<image class="avatar" src="{{remoteUserInfoList[0].avatar || IMG_DEFAULT_AVATAR}}" id="{{remoteUserInfoList[0].userId}}" binderror="handleErrorImage" />
</view>
</view>
</view>
<!-- 语音通话呼叫状态按钮 -->
<view wx:if="{{callStatus === 'calling' && callMediaType === 1}}" class="footer">
<view wx:if="{{callRole !== 'caller'}}" class="btn-operate">
<view class="button-container">
<view class="call-operate" style="background-color: red" catch:tap="reject">
<image src="{{IMG_HANGUP}}" />
</view>
<view style="margin-top:10px">挂断</view>
</view>
<view class="button-container">
<view class="call-operate" catch:tap="accept">
<image src="{{IMG_ACCEPT}}" />
</view>
<view style="margin-top:10px">接听</view>
</view>
</view>
<view wx:if="{{callRole === 'caller'}}" class="btn-operate">
<view class="button-container">
<view class="call-operate" style="background-color: red" catch:tap="hangup">
<image src="{{IMG_HANGUP}}" />
</view>
<view style="margin-top:10px">挂断</view>
</view>
</view>
</view>
<!-- 视频通话呼叫状态按钮 -->
<view wx:if="{{callStatus === 'calling' && callMediaType === 2}}" class="invite-calling">
<view class="footer">
<view class="btn-operate" wx:if="{{callRole === 'caller'}}">
<view class="btn-operate-item call-switch" catch:tap="switchCallMediaType">
<view class="call-operate">
<image src="{{IMG_TRANS}}" />
</view>
<text>切到语音通话</text>
</view>
</view>
<view class="btn-operate" wx:if="{{callRole === 'caller'}}">
<view class="btn-operate-item">
<view class="btn-container">
<view class="call-operate" catch:tap="hangup">
<image src="{{IMG_HANGUP}}" />
</view>
<view class="invite-calling-header-left">
<image src="{{IMG_SWITCH_CAMERA}}" data-device="{{pusher.frontCamera}}" catch:tap="switchCamera" />
</view>
</view>
<text>挂断</text>
</view>
</view>
<view class="btn-operate" wx:if="{{callRole !== 'caller'}}">
<view class="btn-operate-item">
<view class="call-operate" style="background-color: red" catch:tap="reject">
<image src="{{IMG_HANGUP}}" />
</view>
<text>挂断</text>
</view>
<view class="btn-operate-item">
<view class="call-operate" catch:tap="accept">
<image src="{{IMG_ACCEPT}}" />
</view>
<text>接听</text>
</view>
</view>
</view>
</view>
<!-- 语音通话接通状态按钮 -->
<view wx:if="{{callStatus === 'connected' && callMediaType === 1}}" class="handle-btns">
<view class="other-view black">
<text>{{callDuration}}</text>
</view>
<view class="btn-list">
<view class="button-container">
<view class="btn-normal" bindtap="microPhoneHandler">
<image class="btn-image" src="{{localUserInfo.isAudioAvailable? IMG_AUDIO_TRUE: IMG_AUDIO_FALSE}} "></image>
</view>
<view>麦克风</view>
</view>
<view class="button-container">
<view class="btn-hangup" bindtap="hangup">
<image class="btn-image" src="{{IMG_HANGUP}}"></image>
</view>
<view>挂断</view>
</view>
<view class="button-container">
<view class="btn-normal" bindtap="toggleSoundMode">
<image class="btn-image" src="{{isEarPhone ? IMG_SPEAKER_FALSE: IMG_SPEAKER_TRUE}} "></image>
</view>
<text>扬声器</text>
</view>
</view>
</view>
<!-- 视频通话接听状态按钮 -->
<view wx:if="{{callStatus === 'connected' && callMediaType === 2}}" class="invite-calling">
<view class="handle-btns">
<view class="other-view white">
<text>{{callDuration}}</text>
</view>
<view class="btn-operate-item call-switch" catch:tap="switchCallMediaType">
<view class="call-operate">
<image src="{{IMG_TRANS}}" />
</view>
<text>切到语音通话</text>
</view>
<view class="btn-list">
<view class="button-container" bindtap="microPhoneHandler">
<view class="btn-normal">
<image class="btn-image" src="{{localUserInfo.isAudioAvailable? IMG_AUDIO_TRUE: IMG_AUDIO_FALSE}} "></image>
</view>
<view class="white">麦克风</view>
</view>
<view class="button-container" bindtap="toggleSoundMode">
<view class="btn-normal">
<image class="btn-image" src="{{isEarPhone ? IMG_SPEAKER_FALSE: IMG_SPEAKER_TRUE}} "></image>
</view>
<text class="white">扬声器</text>
</view>
<view class="button-container" bindtap="cameraHandler">
<view class="btn-normal">
<image class="btn-image" src="{{localUserInfo.isVideoAvailable ? IMG_CAMERA_TRUE: IMG_CAMERA_FALSE}} "></image>
</view>
<text class="white">摄像头</text>
</view>
</view>
<view class="btn-list">
<view class="btn-list-item other-view">
<view class="btn-container">
<view class="btn-hangup" bindtap="hangup">
<image class="btn-image" src="{{IMG_HANGUP}}"></image>
</view>
<view wx:if="{{localUserInfo.isVideoAvailable}}" class="invite-calling-connected">
<image src="{{IMG_SWITCH_CAMERA}}" data-device="{{pusher.frontCamera}}" catch:tap="switchCamera" />
</view>
</view>
<text class="white">挂断</text>
</view>
</view>
</view>
</view>
</view>
@@ -0,0 +1,508 @@
.footer {
position: absolute;
bottom: 5vh;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.button-container {
display: flex;
flex-direction: column;
text-align: center;
}
.btn-operate {
display: flex;
justify-content: space-between;
z-index: 9999;
}
.btn-operate-item {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
}
.btn-operate-item text {
font-size: 14px;
color: #f0e9e9;
padding: 5px;
letter-spacing: 0;
font-weight: 400;
}
.call-switch text {
padding: 5px;
color: #f0e9e9;
font-size: 14px;
}
.call-operate {
width: 8vh;
height: 8vh;
border-radius: 8vh;
margin: 0 15vw;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
}
.call-switch .call-operate {
width: 4vh;
height: 3vh;
}
.call-operate image {
width: 100%;
height: 100%;
background: none;
}
.tips {
font-size: 20px;
color: #ffffff;
letter-spacing: 0;
margin: 0 auto;
/* text-shadow: 0 1px 2px rgba(0,0,0,0.40); */
font-weight: 600;
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tips-subtitle {
font-family: PingFangSC-Regular;
font-size: 14px;
color: #ffffff;
letter-spacing: 0;
text-align: right;
/* text-shadow: 0 1px 2px rgba(0,0,0,0.30); */
font-weight: 400;
}
.invite-call {
position: absolute;
top: 0;
z-index: 100;
width: 100vw;
height: 100vh;
background-color: black;
}
.transition-animation {
transform: translateY(-100%);
animation: slideInDown 0.5s ease forwards;
}
@keyframes slideInDown {
from {
transform: translateY(-100%);
}
to {
transform: translateY(0);
}
}
.invite-call .local-video {
width: 100vw;
height: 100vh;
}
.invite-call .invite-calling {
position: absolute;
top: 0;
z-index: 101;
width: 100vw;
height: 100vh;
}
.invite-calling-header {
margin-top: 107px;
display: flex;
justify-content: flex-end;
padding: 0 16px;
}
.btn-container {
display: flex;
align-items: center;
position: relative;
}
.invite-calling-header-left {
position: absolute;
right: 0;
}
.invite-calling-header-left image {
width: 32px;
height: 32px;
}
.invite-calling-header-right {
display: flex;
align-items: center;
}
.invite-calling-header-message {
display: flex;
flex-direction: column;
padding: 0 16px;
}
.invite-calling-header-right image {
width: 100px;
height: 100px;
border-radius: 12px;
}
.invite-calling .footer {
position: absolute;
bottom: 5vh;
width: 100%;
}
.invite-calling .btn-operate {
display: flex;
justify-content: center;
align-items: center;
}
.hidden {
display: none;
}
.trtc-calling {
width: 100vw;
height: 100vh;
overflow: hidden;
margin: 0;
z-index: 99;
}
.audio-call {
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
z-index: 100;
background: #ffffff;
}
.audio-call > .btn-operate {
display: flex;
justify-content: center;
}
.audio-call > image {
width: 40vw;
height: 40vw;
display: block;
margin: 20vw 30vw;
margin-top: 40vw;
}
.invite-calling-single > image {
width: 120px;
height: 120px;
border-radius: 12px;
display: block;
margin: 140px auto 15px;
/* margin: 20vw 30vw; */
}
.invite-calling-single .tips {
width: 100%;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 20px;
color: #333333;
letter-spacing: 0;
font-weight: 500;
}
.invite-calling-single .tips-subtitle {
height: 20px;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #97989c;
letter-spacing: 0;
font-weight: 400;
text-align: center;
}
.invite-calling-list {
justify-content: flex-start;
}
.invite-calling-item {
position: relative;
margin: 0 12px;
}
.invite-calling-item image {
width: 100px;
height: 100px;
border-radius: 12px;
}
.invite-calling-item-message {
position: absolute;
background: rgba(0, 0, 0, 0.5);
width: 100px;
height: 100px;
top: 0;
left: 0;
z-index: 2;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.avatar {
background: #dddddd;
}
.player {
position: absolute;
right: 16px;
top: 107px;
width: 100px;
height: 178px;
padding: 16px;
z-index: 999;
}
.pusher-video {
position: absolute;
width: 100%;
height: 100%;
z-index: 1;
}
.stream-box {
position: relative;
float: left;
width: 50vw;
height: 260px;
/* background-color: #f75c45; */
z-index: 3;
}
.handle-btns {
position: absolute;
bottom: 44px;
width: 100vw;
z-index: 3;
display: flex;
flex-direction: column;
}
.handle-btns .btn-list {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
}
.button-container {
display: flex;
flex-direction: column;
text-align: center;
}
.btn-normal {
width: 8vh;
height: 8vh;
box-sizing: border-box;
display: flex;
flex-direction: column;
/* background: white; */
justify-content: center;
align-items: center;
border-radius: 50%;
}
.btn-image {
width: 100%;
height: 100%;
background: none;
}
.btn-hangup {
width: 8vh;
height: 8vh;
/*background: #f75c45;*/
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
}
.btn-hangup > .btn-image {
width: 100%;
height: 100%;
background: none;
}
.TRTCCalling-call-audio {
width: 100%;
height: 100%;
}
.btn-footer {
position: relative;
}
.btn-footer .multi-camera {
width: 32px;
height: 32px;
}
.btn-footer .camera {
width: 64px;
height: 64px;
position: fixed;
left: 16px;
top: 107px;
display: flex;
justify-content: center;
align-items: center;
background: rgba(255, 255, 255, 0.7);
}
.btn-footer .camera .camera-image {
width: 32px;
height: 32px;
}
.TUICalling-connected-layout {
width: 100%;
height: 100%;
}
.audio {
padding-top: 15vh;
background: #ffffff;
}
.pusher-audio {
width: 0;
height: 0;
}
.player-audio {
width: 0;
height: 0;
}
.live {
width: 100%;
height: 100%;
}
.other-view {
display: flex;
flex-direction: column;
align-items: center;
font-size: 18px;
letter-spacing: 0;
font-weight: 400;
padding: 16px;
}
.white {
color: #f0e9e9;
padding: 5px;
font-size: 14px;
}
.black {
color: #000000;
padding: 5px;
}
.TRTCCalling-call-audio-box {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.mutil-img {
justify-content: flex-start !important;
}
.TRTCCalling-call-audio-img {
display: flex;
flex-direction: column;
align-items: center;
}
.TRTCCalling-call-audio-img > image {
width: 25vw;
height: 25vw;
margin: 0 4vw;
border-radius: 4vw;
position: relative;
}
.TRTCCalling-call-audio-img text {
font-size: 20px;
color: #333333;
letter-spacing: 0;
font-weight: 500;
}
.btn-list-item {
flex: 1;
display: flex;
justify-content: center;
padding: 16px 0;
}
.btn-image-small {
transform: scale(0.7);
}
.avatar {
background: #dddddd;
}
.btn-container {
display: flex;
align-items: center;
position: relative;
}
.invite-calling-connected {
position: absolute;
right: -88px;
}
.invite-calling-connected image {
width: 32px;
height: 32px;
}
.call-switch .call-operate {
width: 4vh;
height: 3vh;
}
.call-operate image {
width: 100%;
height: 100%;
background: none;
}
.call-switch text {
padding: 0;
font-size: 14px;
}
.btn-operate-item {
display: flex;
flex-direction: column;
align-items: center;
}
.btn-operate-item text {
padding: 8px 0;
font-size: 18px;
color: #ffffff;
letter-spacing: 0;
font-weight: 400;
font-size: 14px;
}
@@ -0,0 +1,15 @@
import { IBellParams } from '../interface/index';
export declare class BellContext {
private _bellContext;
private _isMuteBell;
private _calleeBellFilePath;
private _callRole;
private _callStatus;
constructor();
setBellSrc(): void;
setBellProperties(bellParams: IBellParams): void;
play(): Promise<void>;
stop(): Promise<void>;
setBellMute(enable: boolean): Promise<void>;
destroy(): void;
}
@@ -0,0 +1,106 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BellContext = void 0;
const index_1 = require("../const/index");
const common_utils_1 = require("../utils/common-utils");
const DEFAULT_CALLER_BELL_FILEPATH = '/TUICallKit/static/phone_dialing.mp3';
const DEFAULT_CALLEE_BELL_FILEPATH = '/TUICallKit/static/phone_ringing.mp3';
class BellContext {
constructor() {
this._bellContext = null;
this._isMuteBell = false;
this._calleeBellFilePath = DEFAULT_CALLEE_BELL_FILEPATH;
this._callRole = index_1.CallRole.UNKNOWN;
this._callStatus = index_1.CallStatus.IDLE;
// @ts-ignore
this._bellContext = wx.createInnerAudioContext();
this._bellContext.loop = true;
}
setBellSrc() {
// @ts-ignore
const fs = wx.getFileSystemManager();
try {
let playBellFilePath = DEFAULT_CALLER_BELL_FILEPATH;
if (this._callRole === index_1.CallRole.CALLEE) {
playBellFilePath = this._calleeBellFilePath || DEFAULT_CALLEE_BELL_FILEPATH;
}
fs.readFileSync(playBellFilePath, 'utf8', 0);
this._bellContext.src = playBellFilePath;
}
catch (error) {
console.warn(`${index_1.NAME.PREFIX}Failed to setBellSrc, ${error}`);
}
}
setBellProperties(bellParams) {
this._callRole = bellParams.callRole || this._callRole;
this._callStatus = bellParams.callStatus || this._callStatus;
this._calleeBellFilePath = bellParams.calleeBellFilePath || this._calleeBellFilePath;
// undefined/false || isMuteBell => isMuteBell (不符合预期)
this._isMuteBell = (0, common_utils_1.isUndefined)(bellParams.isMuteBell) ? this._isMuteBell : bellParams.isMuteBell;
}
play() {
return __awaiter(this, void 0, void 0, function* () {
try {
if (this._callStatus !== index_1.CallStatus.CALLING) {
return;
}
this.setBellSrc();
if (this._callRole === index_1.CallRole.CALLEE && !this._isMuteBell) {
yield this._bellContext.play();
}
if (this._callRole === index_1.CallRole.CALLER) {
yield this._bellContext.play();
}
}
catch (error) {
console.warn(`${index_1.NAME.PREFIX}Failed to play audio file, ${error}`);
}
});
}
stop() {
return __awaiter(this, void 0, void 0, function* () {
try {
this._bellContext.stop();
}
catch (error) {
console.warn(`${index_1.NAME.PREFIX}Failed to stop audio file, ${error}`);
}
});
}
setBellMute(enable) {
return __awaiter(this, void 0, void 0, function* () {
if (this._callStatus !== index_1.CallStatus.CALLING && this._callRole !== index_1.CallRole.CALLEE) {
return;
}
if (enable) {
yield this.stop();
}
else {
yield this.play();
}
});
}
destroy() {
try {
this._isMuteBell = false;
this._calleeBellFilePath = '';
this._callRole = index_1.CallRole.UNKNOWN;
this._callStatus = index_1.CallStatus.IDLE;
this._bellContext.destroy();
this._bellContext = null;
}
catch (error) {
console.warn(`${index_1.NAME.PREFIX}Failed to destroy, ${error}`);
}
}
}
exports.BellContext = BellContext;
@@ -0,0 +1,97 @@
import { ITUICallService, ICallParams, IGroupCallParams, ICallbackParam, ISelfInfoParams, IInviteUserParams, IJoinInGroupCallParams, IInitParams } from '../interface/ICallService';
import { LanguageType, LOG_LEVEL, VideoDisplayMode, VideoResolution } from '../const/index';
import { ITUIGlobal } from '../interface/ITUIGlobal';
import { ITUIStore } from '../interface/ITUIStore';
declare const TUIGlobal: ITUIGlobal;
declare const TUIStore: ITUIStore;
export { TUIGlobal, TUIStore };
export default class TUICallService implements ITUICallService {
static instance: TUICallService;
_tuiCallEngine: any;
private _tim;
private _TUICore;
private _timerId;
private _bellContext;
constructor();
static getInstance(): TUICallService;
init(params: IInitParams): Promise<void>;
destroyed(): Promise<void>;
call(callParams: ICallParams): Promise<void>;
groupCall(groupCallParams: IGroupCallParams): Promise<void>;
inviteUser(params: IInviteUserParams): Promise<void>;
joinInGroupCall(params: IJoinInGroupCallParams): Promise<void>;
getTUICallEngineInstance(): any;
setLogLevel(level: LOG_LEVEL): void;
setLanguage(language: LanguageType): void;
enableFloatWindow(enable: boolean): void;
setSelfInfo(params: ISelfInfoParams): Promise<void>;
setCallingBell(filePath?: string): Promise<void>;
enableMuteMode(enable: boolean): Promise<void>;
accept(): Promise<void>;
hangup(): Promise<void>;
reject(): Promise<void>;
openCamera(videoViewDomID: string): Promise<void>;
closeCamera(): Promise<void>;
openMicrophone(): Promise<void>;
closeMicrophone(): Promise<void>;
switchScreen(userId: string): void;
switchCallMediaType(): Promise<void>;
switchCamera(): Promise<void>;
setSoundMode(type?: string): void;
getTim(): any;
private _addListenTuiCallEngineEvent;
private _removeListenTuiCallEngineEvent;
private _handleError;
private _handleNewInvitationReceived;
private _handleUserAccept;
private _handleUserEnter;
private _callerChangeToConnected;
private _handleUserLeave;
private _unNormalEventsManager;
private _handleInviteeReject;
private _handleNoResponse;
private _handleLineBusy;
private _handleCallingCancel;
private _handleCallingEnd;
private _handleSDKReady;
private _handleKickedOut;
private _handleCallTypeChange;
private _messageSentByMe;
private _handleUserUpdate;
private _handleCallError;
beforeCalling: ((...args: any[]) => void) | undefined;
afterCalling: ((...args: any[]) => void) | undefined;
onMinimized: ((...args: any[]) => void) | undefined;
onMessageSentByMe: ((...args: any[]) => void) | undefined;
kickedOut: ((...args: any[]) => void) | undefined;
statusChanged: ((...args: any[]) => void) | undefined;
setCallback(params: ICallbackParam): void;
toggleMinimize(): void;
private _executeExternalBeforeCalling;
private _executeExternalAfterCalling;
setVideoDisplayMode(displayMode: VideoDisplayMode): void;
setVideoResolution(resolution: VideoResolution): Promise<void>;
private _handleExceptionExit;
private _setLocalUserInfoAudioVideoAvailable;
private _updateCallStoreBeforeCall;
private _updateCallStoreAfterCall;
private _resetCurrentDevice;
private _resetCallStore;
private _noDevicePermissionToast;
private _startTimer;
private _updateCallDuration;
private _stopTimer;
private _deleteRemoteUser;
private _analyzeEventData;
getGroupMemberList(count: number, offset: number): Promise<any>;
getGroupProfile(): Promise<any>;
private _handleCallStatusChange;
private _watchTUIStore;
private _unwatchTUIStore;
bindTUICore(TUICore: any): void;
private _callTUIService;
onNotifyEvent(eventName: string, subKey: string): Promise<void>;
onCall(method: String, params: any): Promise<void>;
private _handleTUICoreOnClick;
onGetExtension(extensionID: string, params: any): any[];
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,6 @@
import { CallMediaType, CallStatus } from '../const/index';
export declare function initialUI(): void;
export declare function checkRunPlatform(): void;
export declare function initAndCheckRunEnv(): void;
export declare function beforeCall(type: CallMediaType, that: any): Promise<CallStatus.IDLE | CallStatus.CALLING>;
export declare function handlePackageError(error: any): void;
@@ -0,0 +1,76 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.handlePackageError = exports.beforeCall = exports.initAndCheckRunEnv = exports.checkRunPlatform = exports.initialUI = void 0;
const index_1 = require("../const/index");
function initialUI() {
// 收起键盘
// @ts-ignore
wx.hideKeyboard({
complete: () => { },
});
}
exports.initialUI = initialUI;
;
// 检测运行时环境, 当是微信开发者工具时, 提示用户需要手机调试
function checkRunPlatform() {
// @ts-ignore
const systemInfo = wx.getSystemInfoSync();
if (systemInfo.platform === 'devtools') {
// 当前运行在微信开发者工具里
// @ts-ignore
wx.showModal({
icon: 'none',
title: '运行环境提醒',
content: '微信开发者工具不支持原生推拉流组件(即 <live-pusher> 和 <live-player> 标签),请使用真机调试或者扫码预览。',
showCancel: false,
});
}
}
exports.checkRunPlatform = checkRunPlatform;
;
function initAndCheckRunEnv() {
initialUI(); // miniProgram 收起键盘, 隐藏 tabBar
checkRunPlatform(); // miniProgram 检测运行时环境
}
exports.initAndCheckRunEnv = initAndCheckRunEnv;
function beforeCall(type, that) {
return __awaiter(this, void 0, void 0, function* () {
try {
initAndCheckRunEnv();
// 检查设备权限
const deviceMap = {
microphone: true,
camera: type === index_1.CallMediaType.VIDEO,
};
const hasDevicePermission = yield that._tuiCallEngine.deviceCheck(deviceMap); // miniProgram 检查设备权限
return hasDevicePermission ? index_1.CallStatus.CALLING : index_1.CallStatus.IDLE;
}
catch (error) {
console.debug(error);
return index_1.CallStatus.IDLE;
}
});
}
exports.beforeCall = beforeCall;
// 套餐问题提示, 小程序最低需要群组通话版, 1v1 通话版本使用 TRTC 就会报错
function handlePackageError(error) {
if ((error === null || error === void 0 ? void 0 : error.code) === -1002) {
// @ts-ignore
wx.showModal({
icon: 'none',
title: 'error',
content: (error === null || error === void 0 ? void 0 : error.message) || '',
showCancel: false,
});
}
}
exports.handlePackageError = handlePackageError;
@@ -0,0 +1,9 @@
import { IUserInfo } from '../interface/ICallService';
import { ITUIStore } from '../interface/ITUIStore';
export declare function setDefaultUserInfo(userId: string, domId?: string): IUserInfo;
export declare function getMyProfile(myselfUserId: string, tim: any, TUIStore: any): Promise<IUserInfo>;
export declare function getRemoteUserProfile(userIdList: Array<string>, tim: any, TUIStore: any): Promise<any>;
export declare function generateText(TUIStore: ITUIStore, key: string, prefix?: string, suffix?: string): string;
export declare function generateStatusChangeText(TUIStore: ITUIStore): string;
export declare function getGroupMemberList(groupID: string, tim: any, count: any, offset: any): Promise<any>;
export declare function getGroupProfile(groupID: string, tim: any): Promise<any>;
@@ -0,0 +1,167 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getGroupProfile = exports.getGroupMemberList = exports.generateStatusChangeText = exports.generateText = exports.getRemoteUserProfile = exports.getMyProfile = exports.setDefaultUserInfo = void 0;
const index_1 = require("../const/index");
const index_2 = require("../locales/index");
// 设置默认的 UserInfo 信息
function setDefaultUserInfo(userId, domId) {
const userInfo = {
userId,
nick: '',
avatar: '',
remark: '',
displayUserInfo: '',
isAudioAvailable: false,
isVideoAvailable: false,
isEnter: false,
domId: domId || userId,
};
return domId ? userInfo : Object.assign(Object.assign({}, userInfo), { isEnter: false }); // localUserInfo 没有 isEnter, remoteUserInfoList 有 isEnter
}
exports.setDefaultUserInfo = setDefaultUserInfo;
// 获取个人用户信息
function getMyProfile(myselfUserId, tim, TUIStore) {
var _a, _b, _c;
return __awaiter(this, void 0, void 0, function* () {
let localUserInfo = setDefaultUserInfo(myselfUserId, index_1.NAME.LOCAL_VIDEO);
try {
if (!tim)
return localUserInfo;
const res = yield tim.getMyProfile();
const currentLocalUserInfo = TUIStore === null || TUIStore === void 0 ? void 0 : TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO); // localUserInfo may have been updated
if ((res === null || res === void 0 ? void 0 : res.code) === 0) {
localUserInfo = Object.assign(Object.assign(Object.assign({}, localUserInfo), currentLocalUserInfo), { userId: (_a = res === null || res === void 0 ? void 0 : res.data) === null || _a === void 0 ? void 0 : _a.userID, nick: (_b = res === null || res === void 0 ? void 0 : res.data) === null || _b === void 0 ? void 0 : _b.nick, avatar: (_c = res === null || res === void 0 ? void 0 : res.data) === null || _c === void 0 ? void 0 : _c.avatar });
}
return localUserInfo;
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}getMyProfile failed, error: ${error}.`);
return localUserInfo;
}
});
}
exports.getMyProfile = getMyProfile;
// 获取远端用户列表信息
function getRemoteUserProfile(userIdList, tim, TUIStore) {
return __awaiter(this, void 0, void 0, function* () {
let remoteUserInfoList = userIdList.map((userId) => setDefaultUserInfo(userId));
try {
if (!tim)
return remoteUserInfoList;
const res = yield tim.getFriendProfile({ userIDList: userIdList });
if (res.code === 0) {
const { friendList = [], failureUserIDList = [] } = res.data;
let unFriendList = failureUserIDList.map((obj) => obj.userID);
if (failureUserIDList.length > 0) {
const res = yield tim.getUserProfile({ userIDList: failureUserIDList.map((obj) => obj.userID) });
if ((res === null || res === void 0 ? void 0 : res.code) === 0) {
unFriendList = (res === null || res === void 0 ? void 0 : res.data) || [];
}
}
const currentRemoteUserInfoList = TUIStore === null || TUIStore === void 0 ? void 0 : TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.REMOTE_USER_INFO_LIST); // remoteUserInfoList may have been updated
const tempFriendIdList = friendList.map((obj) => obj.userID);
const tempUnFriendIdList = unFriendList.map((obj) => obj.userID);
remoteUserInfoList = userIdList.map((userId) => {
var _a, _b, _c, _d, _e, _f, _g;
const defaultUserInfo = setDefaultUserInfo(userId);
const friendListIndex = tempFriendIdList.indexOf(userId);
const unFriendListIndex = tempUnFriendIdList.indexOf(userId);
let remark = '';
let nick = '';
let displayUserInfo = '';
let avatar = '';
if (friendListIndex !== -1) {
remark = ((_a = friendList[friendListIndex]) === null || _a === void 0 ? void 0 : _a.remark) || '';
nick = ((_c = (_b = friendList[friendListIndex]) === null || _b === void 0 ? void 0 : _b.profile) === null || _c === void 0 ? void 0 : _c.nick) || '';
displayUserInfo = remark || nick || defaultUserInfo.userId || '';
avatar = ((_e = (_d = friendList[friendListIndex]) === null || _d === void 0 ? void 0 : _d.profile) === null || _e === void 0 ? void 0 : _e.avatar) || '';
}
if (unFriendListIndex !== -1) {
nick = ((_f = unFriendList[unFriendListIndex]) === null || _f === void 0 ? void 0 : _f.nick) || '';
displayUserInfo = nick || defaultUserInfo.userId || '';
avatar = ((_g = unFriendList[unFriendListIndex]) === null || _g === void 0 ? void 0 : _g.avatar) || '';
}
const userInfo = currentRemoteUserInfoList.find(subObj => subObj.userId === userId) || {};
return Object.assign(Object.assign(Object.assign({}, defaultUserInfo), userInfo), { remark, nick, displayUserInfo, avatar });
});
}
return remoteUserInfoList;
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}getRemoteUserProfile failed, error: ${error}.`);
return remoteUserInfoList;
}
});
}
exports.getRemoteUserProfile = getRemoteUserProfile;
// 生成弹框提示文案
function generateText(TUIStore, key, prefix, suffix) {
const isGroup = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.IS_GROUP);
let callTips = `${(0, index_2.t)(key)}`;
if (isGroup) {
callTips = prefix ? `${prefix} ${callTips}` : callTips;
callTips = suffix ? `${callTips} ${suffix}` : callTips;
}
return callTips;
}
exports.generateText = generateText;
// 生成 statusChange 抛出的字符串
function generateStatusChangeText(TUIStore) {
const callStatus = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS);
if (callStatus === index_1.CallStatus.IDLE) {
return index_1.StatusChange.IDLE;
}
const isGroup = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.IS_GROUP);
if (callStatus === index_1.CallStatus.CALLING) {
return isGroup ? index_1.StatusChange.DIALING_GROUP : index_1.StatusChange.DIALING_C2C;
}
const callMediaType = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_MEDIA_TYPE);
if (isGroup) {
return callMediaType === index_1.CallMediaType.AUDIO ? index_1.StatusChange.CALLING_GROUP_AUDIO : index_1.StatusChange.CALLING_GROUP_VIDEO;
}
return callMediaType === index_1.CallMediaType.AUDIO ? index_1.StatusChange.CALLING_C2C_AUDIO : index_1.StatusChange.CALLING_C2C_VIDEO;
}
exports.generateStatusChangeText = generateStatusChangeText;
// 获取群组[offset, count + offset]区间成员
function getGroupMemberList(groupID, tim, count, offset) {
return __awaiter(this, void 0, void 0, function* () {
let groupMemberList = [];
try {
const res = yield tim.getGroupMemberList({ groupID, count, offset });
if (res.code === 0) {
return res.data.memberList || groupMemberList;
}
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}getGroupMember failed, error: ${error}.`);
return groupMemberList;
}
});
}
exports.getGroupMemberList = getGroupMemberList;
// 获取 IM 群信息
function getGroupProfile(groupID, tim) {
return __awaiter(this, void 0, void 0, function* () {
let groupProfile = {};
try {
const res = yield tim.getGroupProfile({ groupID });
if (res.code === 0) {
return res.data.group || groupProfile;
}
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}getGroupProfile failed, error: ${error}.`);
return groupProfile;
}
});
}
exports.getGroupProfile = getGroupProfile;
@@ -0,0 +1,21 @@
import { ITUIGlobal } from '../interface/ITUIGlobal';
export default class TUIGlobal implements ITUIGlobal {
static instance: TUIGlobal;
global: any;
isPC: boolean;
isH5: boolean;
isWeChat: boolean;
isApp: boolean;
isUniPlatform: boolean;
isOfficial: boolean;
isWIN: boolean;
isMAC: boolean;
constructor();
/**
* 获取 TUIGlobal 实例
* @returns {TUIGlobal}
*/
static getInstance(): TUIGlobal;
initEnv(): void;
initOfficial(SDKAppID: number): void;
}
@@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const env_1 = require("../utils/env");
class TUIGlobal {
constructor() {
this.global = env_1.APP_NAMESPACE;
this.isPC = false;
this.isH5 = false;
this.isWeChat = false;
this.isApp = false;
this.isUniPlatform = false;
this.isOfficial = false;
this.isWIN = false;
this.isMAC = false;
this.initEnv();
}
/**
* 获取 TUIGlobal 实例
* @returns {TUIGlobal}
*/
static getInstance() {
if (!TUIGlobal.instance) {
TUIGlobal.instance = new TUIGlobal();
}
return TUIGlobal.instance;
}
initEnv() {
this.isPC = env_1.IS_PC;
this.isH5 = env_1.IS_H5;
this.isWeChat = env_1.IN_WX_MINI_APP;
this.isApp = env_1.IN_UNI_NATIVE_APP && !env_1.IN_WX_MINI_APP; // uniApp 打包小程序时 IN_UNI_NATIVE_APP 为 true,所以此处需要增加条件
this.isUniPlatform = env_1.IN_UNI_APP;
this.isWIN = env_1.IS_WIN;
this.isMAC = env_1.IS_MAC;
}
initOfficial(SDKAppID) {
this.isOfficial = (SDKAppID === 1400187352 || SDKAppID === 1400188366);
}
}
exports.default = TUIGlobal;
@@ -0,0 +1,8 @@
import { ICallStore } from '../interface/ICallStore';
export default class CallStore {
defaultStore: ICallStore;
store: ICallStore;
update(key: keyof ICallStore, data: any): void;
getData(key: string | undefined): any;
reset(keyList?: Array<string>): void;
}
@@ -0,0 +1,62 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const index_1 = require("../const/index");
const common_utils_1 = require("../utils/common-utils");
class CallStore {
constructor() {
this.defaultStore = {
callStatus: index_1.CallStatus.IDLE,
callRole: index_1.CallRole.UNKNOWN,
callMediaType: index_1.CallMediaType.UNKNOWN,
localUserInfo: { userId: '' },
remoteUserInfoList: [],
callerUserInfo: { userId: '' },
isGroup: false,
callDuration: '00:00:00',
callTips: '',
toastInfo: { text: '' },
isMinimized: false,
enableFloatWindow: false,
bigScreenUserId: '',
language: (0, common_utils_1.getLanguage)(),
isClickable: false,
deviceList: { cameraList: [], microphoneList: [], currentCamera: {}, currentMicrophone: {} },
showPermissionTip: false,
groupID: '',
roomID: 0,
// TUICallKit 组件上的属性
displayMode: index_1.VideoDisplayMode.COVER,
videoResolution: index_1.VideoResolution.RESOLUTION_480P,
showSelectUser: false,
// 小程序相关属性
pusher: {},
player: [],
isEarPhone: false, // 是否是听筒, 默认: false
};
this.store = Object.assign({}, this.defaultStore);
}
;
update(key, data) {
switch (key) {
// case 'callTips':
// break;
default:
// resolve "Type 'any' is not assignable to type 'never'.ts", ref: https://github.com/microsoft/TypeScript/issues/31663
this.store[key] = data;
}
}
getData(key) {
if (!key)
return this.store;
return this.store[key];
}
// reset call store
reset(keyList = []) {
if (keyList.length === 0) {
keyList = Object.keys(this.store);
}
const resetToDefault = keyList.reduce((acc, key) => (Object.assign(Object.assign({}, acc), { [key]: this.defaultStore[key] })), {});
this.store = Object.assign(Object.assign(Object.assign({}, this.defaultStore), this.store), resetToDefault);
}
}
exports.default = CallStore;
@@ -0,0 +1,50 @@
import { ITUIStore, IOptions, Task } from '../interface/ITUIStore';
import { StoreName } from '../const/index';
export default class TUIStore implements ITUIStore {
static instance: TUIStore;
task: Task;
private storeMap;
private timerId;
constructor();
/**
* 获取 TUIStore 实例
* @returns {TUIStore}
*/
static getInstance(): TUIStore;
/**
* UI 组件注册监听回调
* @param {StoreName} storeName store 名称
* @param {IOptions} options 监听信息
* @param {Object} params 扩展参数
* @param {String} params.notifyRangeWhenWatch 注册时监听时的通知范围, 'all' - 通知所有注册该 key 的监听; 'myself' - 通知本次注册该 key 的监听; 默认不通知
*/
watch(storeName: StoreName, options: IOptions, params?: any): void;
/**
* UI 取消组件监听回调
* @param {StoreName} storeName store 名称
* @param {IOptions} options 监听信息,包含需要取消的回掉等
*/
unwatch(storeName: StoreName, options: IOptions): void;
/**
* 通用 store 数据更新,messageList 的变更需要单独处理
* @param {StoreName} storeName store 名称
* @param {string} key 变更的 key
* @param {unknown} data 变更的数据
*/
update(storeName: StoreName, key: string, data: unknown): void;
/**
* 获取 Store 数据
* @param {StoreName} storeName store 名称
* @param {string} key 待获取的 key
* @returns {Any}
*/
getData(storeName: StoreName, key: string): any;
/**
* UI 组件注册监听回调
* @param {StoreName} storeName store 名称
* @param {string} key 变更的 key
*/
private notify;
reset(storeName: StoreName, keyList?: Array<string>, isNotificationNeeded?: boolean): void;
updateStore(params: any, name?: StoreName): void;
}
@@ -0,0 +1,155 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const index_1 = require("../const/index");
const callStore_1 = __importDefault(require("./callStore"));
const common_utils_1 = require("../utils/common-utils");
class TUIStore {
constructor() {
this.timerId = -1;
this.storeMap = {
[index_1.StoreName.CALL]: new callStore_1.default(),
};
// todo 此处后续优化结构后调整
this.task = {}; // 保存监听回调列表
}
/**
* 获取 TUIStore 实例
* @returns {TUIStore}
*/
static getInstance() {
if (!TUIStore.instance) {
TUIStore.instance = new TUIStore();
}
return TUIStore.instance;
}
/**
* UI 组件注册监听回调
* @param {StoreName} storeName store 名称
* @param {IOptions} options 监听信息
* @param {Object} params 扩展参数
* @param {String} params.notifyRangeWhenWatch 注册时监听时的通知范围, 'all' - 通知所有注册该 key 的监听; 'myself' - 通知本次注册该 key 的监听; 默认不通知
*/
watch(storeName, options, params) {
if (!this.task[storeName]) {
this.task[storeName] = {};
}
const watcher = this.task[storeName];
Object.keys(options).forEach((key) => {
const callback = options[key];
if (!watcher[key]) {
watcher[key] = new Map();
}
watcher[key].set(callback, 1);
const { notifyRangeWhenWatch } = params || {};
// 注册监听后, 通知所有注册该 key 的监听,使用 'all' 时要特别注意是否对其他地方的监听产生影响
if (notifyRangeWhenWatch === index_1.NAME.ALL) {
this.notify(storeName, key);
}
// 注册监听后, 仅通知自己本次监听该 key 的数据
if (notifyRangeWhenWatch === index_1.NAME.MYSELF) {
const data = this.getData(storeName, key);
callback.call(this, data);
}
});
}
/**
* UI 取消组件监听回调
* @param {StoreName} storeName store 名称
* @param {IOptions} options 监听信息,包含需要取消的回掉等
*/
unwatch(storeName, options) {
// todo 该接口暂未支持,unwatch掉同一类,如仅传入store注销掉该store下的所有callback,同样options仅传入key注销掉该key下的所有callback
// options的callback function为必填参数,后续修改
if (!this.task[storeName]) {
return;
}
;
// if (isString(options)) {
// // 移除所有的监听
// if (options === '*') {
// const watcher = this.task[storeName];
// Object.keys(watcher).forEach(key => {
// watcher[key].clear();
// });
// } else {
// console.warn(`${NAME.PREFIX}unwatch warning: options is ${options}`);
// }
// return;
// }
const watcher = this.task[storeName];
Object.keys(options).forEach((key) => {
watcher[key].delete(options[key]);
});
}
/**
* 通用 store 数据更新,messageList 的变更需要单独处理
* @param {StoreName} storeName store 名称
* @param {string} key 变更的 key
* @param {unknown} data 变更的数据
*/
update(storeName, key, data) {
var _a;
// 基本数据类型时, 如果相等, 就不进行更新, 减少不必要的 notify
if ((0, common_utils_1.isString)(data) || (0, common_utils_1.isNumber)(data) || (0, common_utils_1.isBoolean)(data)) {
const currentData = this.storeMap[storeName]['store'][key]; // eslint-disable-line
if (currentData === data)
return;
}
(_a = this.storeMap[storeName]) === null || _a === void 0 ? void 0 : _a.update(key, data);
this.notify(storeName, key);
}
/**
* 获取 Store 数据
* @param {StoreName} storeName store 名称
* @param {string} key 待获取的 key
* @returns {Any}
*/
getData(storeName, key) {
var _a;
return (_a = this.storeMap[storeName]) === null || _a === void 0 ? void 0 : _a.getData(key);
}
/**
* UI 组件注册监听回调
* @param {StoreName} storeName store 名称
* @param {string} key 变更的 key
*/
notify(storeName, key) {
if (!this.task[storeName]) {
return;
}
const watcher = this.task[storeName];
if (watcher[key]) {
const callbackMap = watcher[key];
const data = this.getData(storeName, key);
for (const [callback] of callbackMap.entries()) {
callback.call(this, data);
}
}
}
reset(storeName, keyList = [], isNotificationNeeded = false) {
if (storeName in this.storeMap) {
const store = this.storeMap[storeName];
// reset all
if (keyList.length === 0) {
keyList = Object.keys(store === null || store === void 0 ? void 0 : store.store);
}
store.reset(keyList);
if (isNotificationNeeded) {
keyList.forEach((key) => {
this.notify(storeName, key);
});
}
}
}
// 批量修改多个 key-value
updateStore(params, name) {
const storeName = name ? name : index_1.StoreName.CALL;
Object.keys(params).forEach((key) => {
this.update(storeName, key, params[key]);
});
}
}
exports.default = TUIStore;
@@ -0,0 +1,93 @@
/**
* @property {String} call 1v1 通话 + 群组通话
* @property {String} CUSTOM 自定义 Store
*/
export declare enum StoreName {
CALL = "call",
CUSTOM = "custom"
}
/**
* @property {String} idle 空闲
* @property {String} connecting 呼叫等待中
* @property {String} connected 通话中
*/
export declare enum CallMediaType {
UNKNOWN = 0,
AUDIO = 1,
VIDEO = 2
}
/**
* @property {String} caller 主叫
* @property {String} callee 被叫
*/
export declare enum CallRole {
UNKNOWN = "unknown",
CALLEE = "callee",
CALLER = "caller"
}
/**
* @property {String} idle 空闲
* @property {String} calling 呼叫等待中
* @property {String} connected 通话中
*/
export declare enum CallStatus {
IDLE = "idle",
CALLING = "calling",
CONNECTED = "connected"
}
/**
* 视频画面显示模式
* 播放视频流默认使用 cover 模式; 播放屏幕共享流默认使用 contain 模式。
* @property {String} contain 优先保证视频内容全部显示。视频尺寸等比缩放,直至视频窗口的一边与视窗边框对齐。如果视频尺寸与显示视窗尺寸不一致,在保持长宽比的前提下,将视频进行缩放后填满视窗,缩放后的视频四周会有一圈黑边。
* @property {String} cover 优先保证视窗被填满。视频尺寸等比缩放,直至整个视窗被视频填满。如果视频长宽与显示窗口不同,则视频流会按照显示视窗的比例进行周边裁剪或图像拉伸后填满视窗
* @property {String} fill 保证视窗被填满的同时保证视频内容全部显示,但是不保证视频尺寸比例不变。视频的宽高会被拉伸至和视窗尺寸一致。(该选项值自 v4.12.1 开始支持)
*/
export declare enum VideoDisplayMode {
CONTAIN = "contain",
COVER = "cover",
FILL = "fill"
}
/**
* 视频分辨率
* @property {String} 480p
* @property {String} 720p
* @property {String} 1080p
*/
export declare enum VideoResolution {
RESOLUTION_480P = "480p",
RESOLUTION_720P = "720p",
RESOLUTION_1080P = "1080p"
}
export declare enum LanguageType {
EN = "en",
'ZH-CN' = "zh-cn",
JA_JP = "ja_JP"
}
export type TDeviceList = {
cameraList: any[];
microphoneList: any[];
currentCamera: any;
currentMicrophone: any;
};
export declare const StatusChange: {
readonly IDLE: "idle";
readonly BE_INVITED: "be-invited";
readonly DIALING_C2C: "dialing-c2c";
readonly DIALING_GROUP: "dialing-group";
readonly CALLING_C2C_AUDIO: "calling-c2c-audio";
readonly CALLING_C2C_VIDEO: "calling-c2c-video";
readonly CALLING_GROUP_AUDIO: "calling-group-audio";
readonly CALLING_GROUP_VIDEO: "calling-group-video";
};
/**
* @property {String} ear 听筒
* @property {String} speaker 免提
*/
export declare enum AudioPlayBackDevice {
EAR = "ear",
SPEAKER = "speaker"
}
export declare enum DeviceType {
MICROPHONE = "microphone",
CAMERA = "camera"
}
@@ -0,0 +1,104 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DeviceType = exports.AudioPlayBackDevice = exports.StatusChange = exports.LanguageType = exports.VideoResolution = exports.VideoDisplayMode = exports.CallStatus = exports.CallRole = exports.CallMediaType = exports.StoreName = void 0;
/**
* @property {String} call 1v1 通话 + 群组通话
* @property {String} CUSTOM 自定义 Store
*/
var StoreName;
(function (StoreName) {
StoreName["CALL"] = "call";
StoreName["CUSTOM"] = "custom";
})(StoreName = exports.StoreName || (exports.StoreName = {}));
/**
* @property {String} idle 空闲
* @property {String} connecting 呼叫等待中
* @property {String} connected 通话中
*/
var CallMediaType;
(function (CallMediaType) {
CallMediaType[CallMediaType["UNKNOWN"] = 0] = "UNKNOWN";
CallMediaType[CallMediaType["AUDIO"] = 1] = "AUDIO";
CallMediaType[CallMediaType["VIDEO"] = 2] = "VIDEO";
})(CallMediaType = exports.CallMediaType || (exports.CallMediaType = {}));
/**
* @property {String} caller 主叫
* @property {String} callee 被叫
*/
var CallRole;
(function (CallRole) {
CallRole["UNKNOWN"] = "unknown";
CallRole["CALLEE"] = "callee";
CallRole["CALLER"] = "caller";
})(CallRole = exports.CallRole || (exports.CallRole = {}));
/**
* @property {String} idle 空闲
* @property {String} calling 呼叫等待中
* @property {String} connected 通话中
*/
var CallStatus;
(function (CallStatus) {
CallStatus["IDLE"] = "idle";
CallStatus["CALLING"] = "calling";
CallStatus["CONNECTED"] = "connected";
})(CallStatus = exports.CallStatus || (exports.CallStatus = {}));
/**
* 视频画面显示模式
* 播放视频流默认使用 cover 模式; 播放屏幕共享流默认使用 contain 模式。
* @property {String} contain 优先保证视频内容全部显示。视频尺寸等比缩放,直至视频窗口的一边与视窗边框对齐。如果视频尺寸与显示视窗尺寸不一致,在保持长宽比的前提下,将视频进行缩放后填满视窗,缩放后的视频四周会有一圈黑边。
* @property {String} cover 优先保证视窗被填满。视频尺寸等比缩放,直至整个视窗被视频填满。如果视频长宽与显示窗口不同,则视频流会按照显示视窗的比例进行周边裁剪或图像拉伸后填满视窗
* @property {String} fill 保证视窗被填满的同时保证视频内容全部显示,但是不保证视频尺寸比例不变。视频的宽高会被拉伸至和视窗尺寸一致。(该选项值自 v4.12.1 开始支持)
*/
var VideoDisplayMode;
(function (VideoDisplayMode) {
VideoDisplayMode["CONTAIN"] = "contain";
VideoDisplayMode["COVER"] = "cover";
VideoDisplayMode["FILL"] = "fill";
})(VideoDisplayMode = exports.VideoDisplayMode || (exports.VideoDisplayMode = {}));
/**
* 视频分辨率
* @property {String} 480p
* @property {String} 720p
* @property {String} 1080p
*/
var VideoResolution;
(function (VideoResolution) {
VideoResolution["RESOLUTION_480P"] = "480p";
VideoResolution["RESOLUTION_720P"] = "720p";
VideoResolution["RESOLUTION_1080P"] = "1080p";
})(VideoResolution = exports.VideoResolution || (exports.VideoResolution = {}));
// 支持的语言
var LanguageType;
(function (LanguageType) {
LanguageType["EN"] = "en";
LanguageType["ZH-CN"] = "zh-cn";
LanguageType["JA_JP"] = "ja_JP";
})(LanguageType = exports.LanguageType || (exports.LanguageType = {}));
/* === 【原来 TUICallKit 对外暴露】=== */
// 原来 web callKit 定义通知外部状态变更的变量, 对外暴露
exports.StatusChange = {
IDLE: "idle",
BE_INVITED: "be-invited",
DIALING_C2C: "dialing-c2c",
DIALING_GROUP: "dialing-group",
CALLING_C2C_AUDIO: "calling-c2c-audio",
CALLING_C2C_VIDEO: "calling-c2c-video",
CALLING_GROUP_AUDIO: "calling-group-audio",
CALLING_GROUP_VIDEO: "calling-group-video",
};
/* === 【小程序使用】=== */
/**
* @property {String} ear 听筒
* @property {String} speaker 免提
*/
var AudioPlayBackDevice;
(function (AudioPlayBackDevice) {
AudioPlayBackDevice["EAR"] = "ear";
AudioPlayBackDevice["SPEAKER"] = "speaker";
})(AudioPlayBackDevice = exports.AudioPlayBackDevice || (exports.AudioPlayBackDevice = {}));
;
var DeviceType;
(function (DeviceType) {
DeviceType["MICROPHONE"] = "microphone";
DeviceType["CAMERA"] = "camera";
})(DeviceType = exports.DeviceType || (exports.DeviceType = {}));
@@ -0,0 +1,2 @@
export declare const ErrorCode: any;
export declare const ErrorMessage: any;
@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ErrorMessage = exports.ErrorCode = void 0;
// 错误码
exports.ErrorCode = {
SWITCH_TO_AUDIO_CALL_FAILED: 60001,
SWITCH_TO_VIDEO_CALL_FAILED: 60002,
MICROPHONE_UNAVAILABLE: 60003,
CAMERA_UNAVAILABLE: 60004,
BAN_DEVICE: 60005,
NOT_SUPPORTED_WEBRTC: 60006,
ERROR_BLACKLIST: 20007,
};
exports.ErrorMessage = {
SWITCH_TO_AUDIO_CALL_FAILED: 'switchToAudioCall-call-failed',
SWITCH_TO_VIDEO_CALL_FAILED: 'switchToVideoCall-call-failed',
MICROPHONE_UNAVAILABLE: 'microphone-unavailable',
CAMERA_UNAVAILABLE: 'camera-unavailable',
BAN_DEVICE: 'ban-device',
NOT_SUPPORTED_WEBRTC: 'not-supported-webrtc',
ERROR_BLACKLIST: 'blacklist-user-tips'
};
@@ -0,0 +1,12 @@
export * from './call';
export * from './error';
export * from './log';
export declare const CALL_DATA_KEY: any;
export declare const NAME: any;
export declare const AudioCallIcon = "https://web.sdk.qcloud.com/component/TUIKit/assets/call.png";
export declare const VideoCallIcon = "https://web.sdk.qcloud.com/component/TUIKit/assets/call-video-reverse.svg";
export declare const MAX_NUMBER_ROOM_ID = 2147483647;
export declare enum PLATFORM {
MAC = "mac",
WIN = "win"
}
@@ -0,0 +1,60 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PLATFORM = exports.MAX_NUMBER_ROOM_ID = exports.VideoCallIcon = exports.AudioCallIcon = exports.NAME = exports.CALL_DATA_KEY = void 0;
__exportStar(require("./call"), exports);
__exportStar(require("./error"), exports);
__exportStar(require("./log"), exports);
// import { keys } from 'ts-transformer-keys';
// import { ICallStore } from '../interface/store';
// console.warn('--> ', keys<ICallStore>())
exports.CALL_DATA_KEY = {
CALL_STATUS: 'callStatus',
CALL_ROLE: 'callRole',
CALL_MEDIA_TYPE: 'callMediaType',
LOCAL_USER_INFO: 'localUserInfo',
REMOTE_USER_INFO_LIST: 'remoteUserInfoList',
CALLER_USER_INFO: 'callerUserInfo',
IS_GROUP: 'isGroup',
CALL_DURATION: 'callDuration',
CALL_TIPS: 'callTips',
TOAST_INFO: 'toastInfo',
IS_MINIMIZED: 'isMinimized',
ENABLE_FLOAT_WINDOW: 'enableFloatWindow',
BIG_SCREEN_USER_ID: 'bigScreenUserId',
LANGUAGE: 'language',
IS_CLICKABLE: 'isClickable',
DISPLAY_MODE: 'displayMode',
VIDEO_RESOLUTION: 'videoResolution',
PUSHER: 'pusher',
PLAYER: 'player',
IS_EAR_PHONE: 'isEarPhone',
SHOW_PERMISSION_TIP: 'SHOW_PERMISSION_TIP',
GROUP_ID: 'groupID',
ROOM_ID: 'roomID',
SHOW_SELECT_USER: 'showSelectUser',
};
exports.NAME = Object.assign({ PREFIX: '【CallService】', AUDIO: 'audio', VIDEO: 'video', LOCAL_VIDEO: 'localVideo', ERROR: 'error', TIMEOUT: 'timeout', RAF: 'raf', INTERVAL: 'interval', DEFAULT: 'default', BOOLEAN: 'boolean', STRING: 'string', NUMBER: 'number', OBJECT: 'object', ARRAY: 'array', FUNCTION: 'function', UNDEFINED: "undefined", ALL: 'all', MYSELF: 'myself', DEVICE_LIST: 'deviceList' }, exports.CALL_DATA_KEY);
exports.AudioCallIcon = 'https://web.sdk.qcloud.com/component/TUIKit/assets/call.png';
exports.VideoCallIcon = 'https://web.sdk.qcloud.com/component/TUIKit/assets/call-video-reverse.svg';
exports.MAX_NUMBER_ROOM_ID = 2147483647;
var PLATFORM;
(function (PLATFORM) {
// eslint-disable-next-line no-unused-vars
PLATFORM["MAC"] = "mac";
// eslint-disable-next-line no-unused-vars
PLATFORM["WIN"] = "win";
})(PLATFORM = exports.PLATFORM || (exports.PLATFORM = {}));
@@ -0,0 +1,7 @@
export declare enum LOG_LEVEL {
NORMAL = 0,
RELEASE = 1,
WARNING = 2,
ERROR = 3,
NONE = 4
}
@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LOG_LEVEL = void 0;
/* eslint-disable */
// 唯一一个变量格式有问题的, 但是为了和原来 TUICallKit 对外暴露的保持一致
var LOG_LEVEL;
(function (LOG_LEVEL) {
LOG_LEVEL[LOG_LEVEL["NORMAL"] = 0] = "NORMAL";
LOG_LEVEL[LOG_LEVEL["RELEASE"] = 1] = "RELEASE";
LOG_LEVEL[LOG_LEVEL["WARNING"] = 2] = "WARNING";
LOG_LEVEL[LOG_LEVEL["ERROR"] = 3] = "ERROR";
LOG_LEVEL[LOG_LEVEL["NONE"] = 4] = "NONE";
})(LOG_LEVEL = exports.LOG_LEVEL || (exports.LOG_LEVEL = {}));
@@ -0,0 +1,5 @@
import TUICallService, { TUIGlobal, TUIStore } from './CallService/index';
import { StoreName, NAME, CallRole, CallMediaType, CallStatus, StatusChange, VideoResolution, VideoDisplayMode, AudioPlayBackDevice } from './const/index';
import { t } from './locales/index';
declare const TUICallKitServer: TUICallService;
export { TUIGlobal, TUIStore, StoreName, TUICallKitServer, NAME, CallStatus, CallRole, CallMediaType, StatusChange, VideoResolution, VideoDisplayMode, AudioPlayBackDevice, t, };
@@ -0,0 +1,44 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.t = exports.AudioPlayBackDevice = exports.VideoDisplayMode = exports.VideoResolution = exports.StatusChange = exports.CallMediaType = exports.CallRole = exports.CallStatus = exports.NAME = exports.TUICallKitServer = exports.StoreName = exports.TUIStore = exports.TUIGlobal = void 0;
const index_1 = __importStar(require("./CallService/index"));
Object.defineProperty(exports, "TUIGlobal", { enumerable: true, get: function () { return index_1.TUIGlobal; } });
Object.defineProperty(exports, "TUIStore", { enumerable: true, get: function () { return index_1.TUIStore; } });
const index_2 = require("./const/index");
Object.defineProperty(exports, "StoreName", { enumerable: true, get: function () { return index_2.StoreName; } });
Object.defineProperty(exports, "NAME", { enumerable: true, get: function () { return index_2.NAME; } });
Object.defineProperty(exports, "CallRole", { enumerable: true, get: function () { return index_2.CallRole; } });
Object.defineProperty(exports, "CallMediaType", { enumerable: true, get: function () { return index_2.CallMediaType; } });
Object.defineProperty(exports, "CallStatus", { enumerable: true, get: function () { return index_2.CallStatus; } });
Object.defineProperty(exports, "StatusChange", { enumerable: true, get: function () { return index_2.StatusChange; } });
Object.defineProperty(exports, "VideoResolution", { enumerable: true, get: function () { return index_2.VideoResolution; } });
Object.defineProperty(exports, "VideoDisplayMode", { enumerable: true, get: function () { return index_2.VideoDisplayMode; } });
Object.defineProperty(exports, "AudioPlayBackDevice", { enumerable: true, get: function () { return index_2.AudioPlayBackDevice; } });
const index_3 = require("./locales/index");
Object.defineProperty(exports, "t", { enumerable: true, get: function () { return index_3.t; } });
// 实例化
const TUICallKitServer = index_1.default.getInstance();
exports.TUICallKitServer = TUICallKitServer;
@@ -0,0 +1,93 @@
import { CallStatus, CallRole } from '../const/index';
/**
* @interface ITUICallService
*/
export interface ITUICallService {
/**
* 初始化 Service
* @function
* @private
*/
init(params: any): void;
/**
* 1v1 通话
* @function
* @param {SwitchUserStatusParams} options 用户状态控制参数
*/
call(callParams: ICallParams): void;
}
type SDKAppID = {
SDKAppID: number;
} | {
sdkAppID: number;
};
export interface IInitParamsBase {
userID: string;
userSig: string;
tim?: any;
isFromChat?: boolean;
}
export type IInitParams = IInitParamsBase & SDKAppID;
export interface ICallParams {
userID: string;
type: number;
roomID?: number;
userData?: string;
timeout?: number;
offlinePushInfo?: IOfflinePushInfo;
}
export interface IGroupCallParams {
userIDList: Array<string>;
type: number;
groupID: string;
roomID?: number;
userData?: string;
timeout?: number;
offlinePushInfo?: IOfflinePushInfo;
}
export interface IUserInfo {
userId: string;
nick?: string;
avatar?: string;
remark?: string;
displayUserInfo?: string;
isAudioAvailable?: boolean;
isVideoAvailable?: boolean;
volume?: number;
isEnter?: boolean;
domId?: string;
}
export interface IOfflinePushInfo {
title?: string;
description?: string;
androidOPPOChannelID?: string;
extension: String;
}
export interface ICallbackParam {
beforeCalling?: (...args: any[]) => void;
afterCalling?: (...args: any[]) => void;
onMinimized?: (...args: any[]) => void;
onMessageSentByMe?: (...args: any[]) => void;
kickedOut?: (...args: any[]) => void;
statusChanged?: (...args: any[]) => void;
}
export interface ISelfInfoParams {
nickName: string;
avatar: string;
}
export interface IBellParams {
callRole?: CallRole;
callStatus?: CallStatus;
isMuteBell?: boolean;
calleeBellFilePath?: string;
}
export interface IInviteUserParams {
userIDList: string[];
offlinePushInfo?: IOfflinePushInfo;
}
export interface IJoinInGroupCallParams {
type: number;
groupID: string;
roomID: number;
}
export {};
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,33 @@
import { CallStatus, CallRole, CallMediaType, VideoDisplayMode, VideoResolution, TDeviceList } from '../const/index';
import { IUserInfo } from './index';
export interface IToastInfo {
text: string;
type?: string;
}
export interface ICallStore {
callStatus: CallStatus;
callRole: CallRole;
callMediaType: CallMediaType;
localUserInfo: IUserInfo;
remoteUserInfoList: Array<IUserInfo>;
callerUserInfo: IUserInfo;
isGroup: boolean;
callDuration: string;
callTips: string;
toastInfo: IToastInfo;
isMinimized: boolean;
enableFloatWindow: boolean;
bigScreenUserId: string;
language: string;
isClickable: boolean;
showPermissionTip: boolean;
deviceList: TDeviceList;
groupID: string;
roomID: number;
displayMode: VideoDisplayMode;
videoResolution: VideoResolution;
pusher: any;
player: any[];
isEarPhone: boolean;
showSelectUser: boolean;
}
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,36 @@
/**
* @interface TUIGlobal
* @property {Object} global 根据运行环境代理 wx、uni、window
* @property {Boolean} isPC true 标识是 pc 网页
* @property {Boolean} isH5 true 标识是 手机 H5
* @property {Boolean} isWeChat true 标识是 微信小程序
* @property {Boolean} isApp true 标识是 uniapp 打包的 native app
* @property {Boolean} isUniPlatform true 标识当前应用是通过 uniapp 平台打包的产物
* @property {Boolean} isOfficial true 标识是腾讯云官网 Demo 应用
* @property {Boolean} isWIN true 标识是window系统pc
* @property {Boolean} isMAC true 标识是mac os系统pc
*/
export interface ITUIGlobal {
global: any;
isPC: boolean;
isH5: boolean;
isWeChat: boolean;
isApp: boolean;
isUniPlatform: boolean;
isOfficial: boolean;
isWIN: boolean;
isMAC: boolean;
/**
* 初始化 TUIGlobal 环境变量
* @function
* @private
*/
initEnv(): void;
/**
* 初始化 isOfficial
* @function
* @param {number} SDKAppID 当前实例的应用 SDKAppID
* @private
*/
initOfficial(SDKAppID: number): void;
}
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,75 @@
import { StoreName } from '../const/index';
export type Task = Record<StoreName, Record<string, Map<(data?: unknown) => void, 1>>>;
export interface IOptions {
[key: string]: (newData?: any) => void;
}
/**
* @class TUIStore
* @property {ICustomStore} customStore 自定义 store,可根据业务需要通过以下 API 进行数据操作。
*/
export interface ITUIStore {
task: Task;
/**
* UI 组件注册监听
* @function
* @param {StoreName} storeName store 名称
* @param {IOptions} options UI 组件注册的监听信息
* @param {Object} params 扩展参数
* @param {String} params.notifyRangeWhenWatch 注册时监听时的通知范围
* @example
* // UI 层监听会话列表更新通知
* let onConversationListUpdated = function(conversationList) {
* console.warn(conversationList);
* }
* TUIStore.watch(StoreName.CONV, {
* conversationList: onConversationListUpdated,
* })
*/
watch(storeName: StoreName, options: IOptions, params?: any): void;
/**
* UI 组件取消监听回调
* @function
* @param {StoreName} storeName store 名称
* @param {IOptions} options 监听信息,包含需要取消的回调等
* @example
* // UI 层取消监听会话列表更新通知
* TUIStore.unwatch(StoreName.CONV, {
* conversationList: onConversationListUpdated,
* })
*/
unwatch(storeName: StoreName, options: IOptions | string): void;
/**
* 获取 store 中的数据
* @function
* @param {StoreName} storeName store 名称
* @param {String} key 需要获取的 key
* @private
*/
getData(storeName: StoreName, key: string): any;
/**
* 更新 store
* - 需要使用自定义 store 时可以用此 API 更新自定义数据
* @function
* @param {StoreName} storeName store 名称
* @param {String} key 需要更新的 key
* @example
* // UI 层更新自定义 Store 数据
* TUIStore.update(StoreName.CUSTOM, 'customKey', 'customData')
*/
update(storeName: StoreName, key: string, data: unknown): void;
/**
* 重置 store 内数据
* @function
* @param {StoreName} storeName store 名称
* @param {Array<string>} keyList 需要 reset 的 keyList
* @param {boolean} isNotificationNeeded 是否需要触发更新
* @private
*/
reset: (storeName: StoreName, keyList?: Array<string>, isNotificationNeeded?: boolean) => void;
/**
* 修改多个 key-value
* @param {Object} params 多个 key-value 组成的 object
* @param {StoreName} storeName store 名称
*/
updateStore: (params: any, name?: StoreName) => void;
}
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,4 @@
export * from './ICallService';
export * from './ICallStore';
export * from './ITUIGlobal';
export * from './ITUIStore';
@@ -0,0 +1,20 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./ICallService"), exports);
__exportStar(require("./ICallStore"), exports);
__exportStar(require("./ITUIGlobal"), exports);
__exportStar(require("./ITUIStore"), exports);
@@ -0,0 +1,90 @@
export declare const en: {
hangup: string;
reject: string;
'other side reject call': string;
'reject call': string;
accept: string;
cancel: string;
'other side line busy': string;
'in busy': string;
'call timeout': string;
'no response from the other side': string;
'end call': string;
timeout: string;
'kick out': string;
'caller calling message': string;
'callee calling video message': string;
'callee calling audio message': string;
'no microphone access': string;
'no camera access': string;
'invite member': string;
speaker: string;
'Invited group call': string;
'Those involved': string;
call: string;
'video-call': string;
'audio-call': string;
search: string;
'search-result': string;
'no-user': string;
'member-not-added': string;
'input-phone-userID': string;
'not-login': string;
'login-status-expire': string;
'experience-multi-call': string;
'not-support-multi-call': string;
userID: string;
'already-enter': string;
waiting: string;
'camera-opened': string;
'camera-closed': string;
'microphone-opened': string;
'microphone-closed': string;
camera: string;
microphone: string;
'image-resolution': string;
'default-image-resolution': string;
'invited-person': string;
'video-to-audio': string;
me: string;
'be-rejected': string;
'be-no-response': string;
'be-line-busy': string;
'be-canceled': string;
'voice-call-end': string;
'video-call-end': string;
'method-call-failed': string;
'failed-to-obtain-permission': string;
'environment-detection-failed': string;
'switchToAudioCall-call-failed': string;
'switchToVideoCall-call-failed': string;
'microphone-unavailable': string;
'camera-unavailable': string;
'ban-device': string;
'not-supported-webrtc': string;
'blacklist-user-tips': string;
'is-already-calling': string;
'need-init': string;
"can't call yourself": string;
'Use-phone-and-computer': string;
'Wechat scan right QR code': string;
'Scan the QR code above': string;
'accept-error': string;
'accept-device-error': string;
'call-error': string;
'browser-authorization': string;
'mac-privacy': string;
'win-privacy': string;
'mac-preferences': string;
'win-preferences': string;
'open camera': string;
'close camera': string;
'open microphone': string;
'close microphone': string;
'Please enter userID': string;
'View more': string;
'people selected': string;
'Select all': string;
Cancel: string;
Done: string;
};
@@ -0,0 +1,93 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.en = void 0;
exports.en = {
'hangup': 'Hang up',
'reject': 'Decline',
'other side reject call': 'other side reject call',
'reject call': 'Reject Call',
'accept': 'Accept',
'cancel': 'Cancel Call',
'other side line busy': 'other side line busy',
'in busy': 'in busy',
'call timeout': 'call timeout',
'no response from the other side': 'no response from the other side',
'end call': 'end call',
'timeout': 'timeout',
'kick out': 'kick out',
'caller calling message': 'Waiting for the callee to accept the invitation...',
'callee calling video message': 'You are invited to a video call...',
'callee calling audio message': 'You are invited to a audio call...',
'no microphone access': 'no microphone access',
'no camera access': 'no camera access',
'invite member': 'Invite Member',
'speaker': 'speaker',
'Invited group call': 'Invited you to a group call',
'Those involved': 'Those involved in the call are',
'call': 'call',
'video-call': 'video call',
'audio-call': 'audio call',
'search': 'search',
'search-result': 'search result',
'no-user': 'user not found',
'member-not-added': 'member not added',
'input-phone-userID': 'phone number or userID',
'not-login': 'not logged in',
'login-status-expire': 'login status is invalid, please refresh the page and try again',
'experience-multi-call': 'experience multi-person calls, please download the full-featured demo: ',
'not-support-multi-call': 'multi-person call interface is not open',
'userID': 'userID',
'already-enter': 'entered the call',
'waiting': 'Calling...',
'camera-opened': 'Camera on',
'camera-closed': 'Camera off',
'microphone-opened': 'Mic on',
'microphone-closed': 'Mic off',
'camera': 'Camera',
'microphone': 'Microphone',
'image-resolution': 'Resolution',
'default-image-resolution': 'Default',
'invited-person': 'Invite',
'video-to-audio': 'Switch to audio',
'me': '(me)',
'be-rejected': 'Call declined, ',
'be-no-response': 'No response, ',
'be-line-busy': 'Line busy, ',
'be-canceled': 'The call is canceled, ',
'voice-call-end': 'Voice call ended',
'video-call-end': 'Video call ended',
'method-call-failed': 'Failed to sync the operation',
'failed-to-obtain-permission': 'Failed to obtain permissions',
'environment-detection-failed': 'Failed to check the environment',
'switchToAudioCall-call-failed': 'switch to audio call method failed',
'switchToVideoCall-call-failed': 'switch to video call method failed',
'microphone-unavailable': 'No mic found',
'camera-unavailable': 'No camera found',
'ban-device': 'Device access denied',
'not-supported-webrtc': 'Your current environment does not support WebRTC',
'blacklist-user-tips': 'The identifier is in blacklist. Failed to send this message!',
'is-already-calling': 'TUICallKit is already on a call',
'need-init': 'Before initiating a call with TUICallKit, ensure that the TUICallKitServer.init() method has executed successfully. ',
"can't call yourself": "Can't call yourself",
'Use-phone-and-computer': 'Use your mobile phone and computer to experience video calls',
'Wechat scan right QR code': 'Wechat scan right QR code',
'Scan the QR code above': 'Scan the QR code above',
'accept-error': 'Accept failed',
'accept-device-error': 'Accept failed, unable to auth calling device',
'call-error': 'Start call failed',
'browser-authorization': 'Browser authorization',
'mac-privacy': 'System Preferences -> Security and Privacy -> Privacy',
'win-privacy': 'Setting -> Privacy and Security -> App permissions',
'mac-preferences': 'Open System Preferences',
'win-preferences': 'Open Setting',
'open camera': 'Open Camera',
'close camera': 'Close Camera',
'open microphone': 'Open Microphone',
'close microphone': 'Close Microphone',
'Please enter userID': 'Please enter userID',
'View more': 'View more',
'people selected': 'people selected',
'Select all': 'Select all',
'Cancel': 'Cancel',
'Done': 'Done',
};
@@ -0,0 +1,10 @@
export declare const CallTips: any;
export declare const languageData: languageDataType;
export declare function t(key: any): string;
interface languageItemType {
[key: string]: string;
}
interface languageDataType {
[key: string]: languageItemType;
}
export {};
@@ -0,0 +1,51 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.t = exports.languageData = exports.CallTips = void 0;
const index_1 = require("../CallService/index");
const index_2 = require("../const/index");
const en_1 = require("./en");
const zh_cn_1 = require("./zh-cn");
const ja_JP_1 = require("./ja_JP");
exports.CallTips = {
OTHER_SIDE: 'other side',
CANCEL: 'cancel',
OTHER_SIDE_REJECT_CALL: 'other side reject call',
REJECT_CALL: 'reject call',
OTHER_SIDE_LINE_BUSY: 'other side line busy',
IN_BUSY: 'in busy',
CALL_TIMEOUT: 'call timeout',
END_CALL: 'end call',
TIMEOUT: 'timeout',
KICK_OUT: 'kick out',
CALLER_CALLING_MSG: 'caller calling message',
CALLEE_CALLING_VIDEO_MSG: 'callee calling video message',
CALLEE_CALLING_AUDIO_MSG: 'callee calling audio message',
NO_MICROPHONE_DEVICE_PERMISSION: 'no microphone access',
NO_CAMERA_DEVICE_PERMISSION: 'no camera access',
};
exports.languageData = {
en: en_1.en,
'zh-cn': zh_cn_1.zh,
ja_JP: ja_JP_1.ja_JP,
};
// language translate
function t(key) {
var _a;
const language = index_1.TUIStore.getData(index_2.StoreName.CALL, index_2.NAME.LANGUAGE);
// eslint-disable-next-line
for (const langKey in exports.languageData) {
if (langKey === language) {
const currentLanguage = exports.languageData[langKey];
// eslint-disable-next-line
for (const sentenceKey in currentLanguage) {
if (sentenceKey === key) {
return currentLanguage[sentenceKey];
}
}
}
}
const enString = (_a = key['en']) === null || _a === void 0 ? void 0 : _a.key; // eslint-disable-line
console.error(`${index_2.NAME.PREFIX}translation is not found: ${key}.`);
return enString;
}
exports.t = t;
@@ -0,0 +1,85 @@
export declare const ja_JP: {
hangup: string;
reject: string;
'other side reject call': string;
'reject call': string;
accept: string;
cancel: string;
'other side line busy': string;
'in busy': string;
'call timeout': string;
'end call': string;
timeout: string;
'kick out': string;
'caller calling message': string;
'callee calling video message': string;
'callee calling audio message': string;
'no microphone access': string;
'no camera access': string;
'invite member': string;
speaker: string;
'Invited group call': string;
'Those involved': string;
call: string;
'video-call': string;
'audio-call': string;
search: string;
'search-result': string;
'Wechat scan right QR code': string;
'Use-phone-and-computer': string;
'Scan the QR code above': string;
'no-user': string;
'member-not-added': string;
'not-login': string;
'login-status-expire': string;
'experience-multi-call': string;
'not-support-multi-call': string;
'input-phone-userID': string;
userID: string;
'already-enter': string;
waiting: string;
'camera-opened': string;
'camera-closed': string;
'microphone-opened': string;
'microphone-closed': string;
camera: string;
microphone: string;
'image-resolution': string;
'default-image-resolution': string;
'invited-person': string;
'video-to-audio': string;
me: string;
'be-rejected': string;
'be-no-response': string;
'be-line-busy': string;
'be-canceled': string;
'voice-call-end': string;
'video-call-end': string;
'method-call-failed': string;
'failed-to-obtain-permission': string;
'environment-detection-failed': string;
'switchToAudioCall-call-failed': string;
'switchToVideoCall-call-failed': string;
'microphone-unavailable': string;
'camera-unavailable': string;
'ban-device': string;
'not-supported-webrtc': string;
'blacklist-user-tips': string;
'is-already-calling': string;
'need-init': string;
"can't call yourself": string;
'accept-error': string;
'accept-device-error': string;
'call-error': string;
'browser-authorization': string;
'mac-privacy': string;
'win-privacy': string;
'mac-preferences': string;
'win-preferences': string;
'Please enter userID': string;
'View more': string;
'people selected': string;
'Select all': string;
Cancel: string;
Done: string;
};
@@ -0,0 +1,88 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ja_JP = void 0;
exports.ja_JP = {
'hangup': '通話終了',
'reject': '拒否',
'other side reject call': '通話が拒否されました',
'reject call': '通話拒否',
'accept': '応答',
'cancel': '通話をキャンセル',
'other side line busy': '相手が通話中です',
'in busy': '通話中',
'call timeout': '呼び出しタイムアウト',
'end call': '通話終了',
'timeout': 'タイムアウト',
'kick out': 'キックアウトされました',
'caller calling message': '相手が招待を承諾するのを待っています。',
'callee calling video message': 'ビデオ通話に招待されました。',
'callee calling audio message': '音声通話に招待されました。',
'no microphone access': 'マイクにアクセスできません',
'no camera access': 'カメラにアクセスできません',
'invite member': 'メンバーを招待する',
'speaker': 'スピーカー',
'Invited group call': 'グループ通話に招待されました。',
'Those involved': '参加者:',
'call': '通話',
'video-call': 'ビデオ通話',
'audio-call': '音声通話',
'search': '検索',
'search-result': '検索結果',
'Wechat scan right QR code': 'WeChatで右側にあるQRコードを読み取ります。',
'Use-phone-and-computer': '携帯電話とコンピュータを使用してビデオ通話を体験してください',
'Scan the QR code above': '上のQRコードを読み取ります。',
'no-user': 'ユーザーが見つかりませんでした',
'member-not-added': 'メンバーが追加されていません',
'not-login': 'ログインしていません',
'login-status-expire': 'ログインの有効期限が過ぎています。ページを更新してもう一度お試しください',
'experience-multi-call': '複数人で同時に音声通話できるグループ通話機能を体験するには、全機能のデモをダウンロードしてください',
'not-support-multi-call': 'グループ通話インターフェイスが開いていません',
'input-phone-userID': '携帯電話番号/ユーザーIDを入力してください',
'userID': 'ユーザーID',
'already-enter': 'すでに通話に参加しています',
'waiting': '応答を待っています...',
'camera-opened': 'カメラがオンになっています',
'camera-closed': 'カメラがオフになっています',
'microphone-opened': 'マイクがオンになっています',
'microphone-closed': 'マイクがオフになっています',
'camera': 'カメラ',
'microphone': 'マイク',
'image-resolution': '解像度',
'default-image-resolution': 'デフォルト解像度',
'invited-person': 'メンバーを招待',
'video-to-audio': '音声通話に切り替えます',
'me': '(自分)',
'be-rejected': '通話が拒否されました, ',
'be-no-response': '応答なし, ',
'be-line-busy': '相手が通話中です, ',
'be-canceled': '相手が通話をキャンセルしました',
'voice-call-end': '音声通話が終了しました',
'video-call-end': 'ビデオ通話が終了しました',
'method-call-failed': '操作の同期に失敗しました',
'failed-to-obtain-permission': '権限の取得に失敗しました',
'environment-detection-failed': '環境の検出に失敗しました',
'switchToAudioCall-call-failed': '音声通話に切り替えることはできません',
'switchToVideoCall-call-failed': 'ビデオ通話に切り替えることはできません',
'microphone-unavailable': '使用できるマイクがありません',
'camera-unavailable': '使用できるカメラがありません',
'ban-device': 'デバイスへのアクセスが拒否されました',
'not-supported-webrtc': '現在の環境はWebRTCをサポートしていません',
'blacklist-user-tips': 'ユーザーはブラックリストに登録され、通話が開始できませんでした',
'is-already-calling': 'TUICallKit はすでに通話中です',
'need-init': 'TUICallKitで通話を開始する前に、TUICallKitServer.init() メソッドが正常に実行されたことを確認してください。',
"can't call yourself": '自分に電話をかけることができません',
'accept-error': '接続できませんでした',
'accept-device-error': '接続できませんでした。発信側デバイスを認証できません',
'call-error': '通話が開始できませんでした',
'browser-authorization': 'ブラウザ認証',
'mac-privacy': 'システム環境設定 -> セキュリティとプライバシー ->プライバシー',
'win-privacy': '設定 -> セキュリティとプライバシー ->アプリのアクセス許可',
'mac-preferences': 'システム環境設定を開く',
'win-preferences': 'システム設定を開く',
'Please enter userID': 'ユーザーIDを入力してください',
'View more': 'もっと見る',
'people selected': '人が選択されました',
'Select all': 'すべて選択',
'Cancel': 'キャンセル',
'Done': '完了',
};
@@ -0,0 +1,89 @@
export declare const zh: {
hangup: string;
reject: string;
'other side reject call': string;
'reject call': string;
accept: string;
cancel: string;
'other side line busy': string;
'in busy': string;
'call timeout': string;
'end call': string;
timeout: string;
'kick out': string;
'caller calling message': string;
'callee calling video message': string;
'callee calling audio message': string;
'no microphone access': string;
'no camera access': string;
'invite member': string;
speaker: string;
'Invited group call': string;
'Those involved': string;
call: string;
'video-call': string;
'audio-call': string;
search: string;
'search-result': string;
'Wechat scan right QR code': string;
'Use-phone-and-computer': string;
'Scan the QR code above': string;
'no-user': string;
'member-not-added': string;
'not-login': string;
'login-status-expire': string;
'experience-multi-call': string;
'not-support-multi-call': string;
'input-phone-userID': string;
userID: string;
'already-enter': string;
waiting: string;
'camera-opened': string;
'camera-closed': string;
'microphone-opened': string;
'microphone-closed': string;
camera: string;
microphone: string;
'image-resolution': string;
'default-image-resolution': string;
'invited-person': string;
'video-to-audio': string;
me: string;
'be-rejected': string;
'be-no-response': string;
'be-line-busy': string;
'be-canceled': string;
'voice-call-end': string;
'video-call-end': string;
'method-call-failed': string;
'failed-to-obtain-permission': string;
'environment-detection-failed': string;
'switchToAudioCall-call-failed': string;
'switchToVideoCall-call-failed': string;
'microphone-unavailable': string;
'camera-unavailable': string;
'ban-device': string;
'not-supported-webrtc': string;
'blacklist-user-tips': string;
'is-already-calling': string;
'need-init': string;
"can't call yourself": string;
'accept-error': string;
'accept-device-error': string;
'call-error': string;
'browser-authorization': string;
'mac-privacy': string;
'win-privacy': string;
'mac-preferences': string;
'win-preferences': string;
'open camera': string;
'close camera': string;
'open microphone': string;
'close microphone': string;
'Please enter userID': string;
'View more': string;
'people selected': string;
'Select all': string;
Cancel: string;
Done: string;
};
@@ -0,0 +1,92 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.zh = void 0;
exports.zh = {
'hangup': '挂断',
'reject': '拒绝',
'other side reject call': '对方已拒绝',
'reject call': '拒绝通话',
'accept': '接受',
'cancel': '取消通话',
'other side line busy': '对方忙线',
'in busy': '正在忙',
'call timeout': '呼叫超时',
'end call': '结束通话',
'timeout': '超时',
'kick out': '被踢',
'caller calling message': '正在等待对方接受邀请…',
'callee calling video message': '邀请您进行视频通话…',
'callee calling audio message': '邀请您进行语音通话…',
'no microphone access': '没有麦克风权限',
'no camera access': '没有摄像头权限',
'invite member': '邀请成员',
'speaker': '扬声器',
'Invited group call': '邀请你参加多人通话',
'Those involved': '参与通话的有:',
'call': '通话',
'video-call': '视频通话',
'audio-call': '音频通话',
'search': '搜索',
'search-result': '搜索结果',
'Wechat scan right QR code': '微信扫右二维码',
'Use-phone-and-computer': '用手机与电脑互打体验视频通话',
'Scan the QR code above': '扫描上方二维码',
'no-user': '未搜索到用户',
'member-not-added': '未添加成员',
'not-login': '未登录',
'login-status-expire': '登录状态已失效,请刷新网页重试',
'experience-multi-call': '体验多人通话请下载全功能demo:',
'not-support-multi-call': '多人通话接口未开放',
'input-phone-userID': '请输入手机号/用户ID',
'userID': '用户ID',
'already-enter': '已经进入当前通话',
'waiting': '等待接听...',
'camera-opened': '摄像头已开',
'camera-closed': '摄像头已关',
'microphone-opened': '麦克风已开',
'microphone-closed': '麦克风已关',
'camera': '摄像头',
'microphone': '麦克风',
'image-resolution': '分辨率',
'default-image-resolution': '默认分辨率',
'invited-person': '添加成员',
'video-to-audio': '切到语音通话',
'me': '(我)',
'be-rejected': '对方已拒绝,',
'be-no-response': '对方无应答,',
'be-line-busy': '对方忙线中,',
'be-canceled': '对方已取消',
'voice-call-end': '语音通话结束',
'video-call-end': '视频通话结束',
'method-call-failed': '同步操作失败',
'failed-to-obtain-permission': '权限获取失败',
'environment-detection-failed': '环境检测失败',
'switchToAudioCall-call-failed': '切语音调用失败',
'switchToVideoCall-call-failed': '切视频调用失败',
'microphone-unavailable': '没有可用的麦克风设备',
'camera-unavailable': '没有可用的摄像头设备',
'ban-device': '用户禁止使用设备',
'not-supported-webrtc': '当前环境不支持 WebRTC',
'blacklist-user-tips': '发起通话失败,被对方拉入黑名单,禁止发起!',
'is-already-calling': 'TUICallKit 已在通话状态',
'need-init': 'TUICallKit 发起通话前需保证 TUICallKitServer.init() 方法执行成功',
"can't call yourself": '不能呼叫自己',
'accept-error': '接通失败',
'accept-device-error': '接通失败,通话设备获取失败',
'call-error': '发起通话失败',
'browser-authorization': '浏览器授权',
'mac-privacy': '系统偏好设置 -> 安全与隐私 -> 隐私',
'win-privacy': '设置 -> 隐私和安全性 -> 应用权限',
'mac-preferences': '打开系统偏好设置',
'win-preferences': '打开系统设置',
'open camera': '打开摄像头',
'close camera': '关闭摄像头',
'open microphone': '打开麦克风',
'close microphone': '关闭麦克风',
'Please enter userID': '请输入 userID',
'View more': '查看更多',
'people selected': '人已选中',
'Select all': '全选',
'Cancel': '取消',
'Done': '完成',
};
@@ -0,0 +1,11 @@
export declare class CallManager {
private _globalCallPagePath;
private _isPageRedirected;
init(params: any): Promise<void>;
private _watchTUIStore;
private _unwatchTUIStore;
private _handleCallStatusChange;
private _handleCallStatusToCalling;
private _handleCallStatusToIdle;
destroyed(): Promise<void>;
}
@@ -0,0 +1,133 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CallManager = void 0;
const index_1 = require("../../index");
const index_2 = require("../const/index");
const index_3 = require("../utils/validate/index");
/**
* @param {Number} sdkAppID 用户的sdkAppID 必传
* @param {String} userID 用户的userID 必传
* @param {String} userSig 用户的userSig 必传
* @param {String} globalCallPagePath 跳转的路径 必传
* @param {ChatSDK} tim tim实例 非必传
*/
const PREFIX = 'callManager';
class CallManager {
constructor() {
this._globalCallPagePath = '';
this._isPageRedirected = false;
this._handleCallStatusChange = (value) => __awaiter(this, void 0, void 0, function* () {
switch (value) {
case index_2.CallStatus.CALLING:
case index_2.CallStatus.CONNECTED:
this._handleCallStatusToCalling();
break;
case index_2.CallStatus.IDLE:
this._handleCallStatusToIdle();
break;
}
});
}
init(params) {
return __awaiter(this, void 0, void 0, function* () {
const { sdkAppID, userID, userSig, globalCallPagePath, tim } = params;
if (!globalCallPagePath) {
console.error(`${PREFIX} globalCallPagePath Can not be empty!`);
return;
}
;
this._globalCallPagePath = globalCallPagePath;
try {
yield index_1.TUICallKitServer.init({
sdkAppID,
userID,
userSig,
tim,
});
this._watchTUIStore();
// 全局监听下,关闭悬浮窗
index_1.TUICallKitServer.enableFloatWindow(false);
console.log(`${PREFIX} init Ready!`);
}
catch (error) {
console.error(`${PREFIX} init fail!`);
}
});
}
// =========================【监听 TUIStore 中的状态】=========================
_watchTUIStore() {
index_1.TUIStore === null || index_1.TUIStore === void 0 ? void 0 : index_1.TUIStore.watch(index_1.StoreName.CALL, {
[index_1.NAME.CALL_STATUS]: this._handleCallStatusChange,
}, {
notifyRangeWhenWatch: index_1.NAME.MYSELF,
});
}
_unwatchTUIStore() {
index_1.TUIStore === null || index_1.TUIStore === void 0 ? void 0 : index_1.TUIStore.unwatch(index_1.StoreName.CALL, {
[index_1.NAME.CALL_STATUS]: this._handleCallStatusChange,
});
}
_handleCallStatusToCalling() {
if (this._isPageRedirected)
return;
// @ts-ignore
wx.navigateTo({
url: `/${this._globalCallPagePath}`,
success: () => {
this._isPageRedirected = true;
},
fail: () => {
console.error(`${PREFIX} navigateTo fail!`);
},
complete: () => { },
});
}
_handleCallStatusToIdle() {
if (!this._isPageRedirected)
return;
// @ts-ignore
wx.navigateBack({
success: () => {
this._isPageRedirected = false;
},
fail: () => {
console.error(`${PREFIX} navigateBack fail!`);
},
complete: () => { },
});
}
// 卸载 callManger
destroyed() {
return __awaiter(this, void 0, void 0, function* () {
this._globalCallPagePath = '';
this._isPageRedirected = false;
this._unwatchTUIStore();
yield index_1.TUICallKitServer.destroyed();
});
}
}
__decorate([
(0, index_3.avoidRepeatedCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], CallManager.prototype, "init", null);
exports.CallManager = CallManager;
@@ -0,0 +1,53 @@
export declare const isUndefined: (input: any) => boolean;
export declare const isPlainObject: (input: any) => boolean;
export declare const isArray: (input: any) => boolean;
export declare const isPrivateKey: (key: string) => boolean;
export declare const isUrl: (url: string) => boolean;
/**
* 检测input类型是否为string
* @param {*} input 任意类型的输入
* @returns {Boolean} true->string / false->not a string
*/
export declare const isString: (input: any) => boolean;
export declare const isBoolean: (input: any) => boolean;
export declare const isNumber: (input: any) => boolean;
export declare function formatTime(secondTime: number): string;
export declare function formatTimeInverse(stringTime: string): number;
export declare function isJSON(str: string): boolean;
export declare const JSONToObject: (str: string) => any;
/**
* 重试函数, catch 时,重试
* @param {Promise} promise 需重试的函数
* @param {number} num 需要重试的次数
* @param {number} time 间隔时间(s
* @returns {Promise<any>} im 接口的 response 原样返回
*/
export declare const retryPromise: (promise: Promise<any>, num?: number, time?: number) => Promise<any>;
/**
* web call engine 重复调用时的错误, 这种错误在 TUICallKit 应该忽略
* @param {any} error 错误信息
* @returns {Boolean}
*/
export declare function handleRepeatedCallError(error: any): boolean;
/**
* 设备无权限时的错误处理
* @param {any} error 错误信息
* @returns {Boolean}
*/
export declare function handleNoDevicePermissionError(error: any): boolean;
export declare function performanceNow(): number;
/**
* 检测input类型是否为function
* @param {*} input 任意类型的输入
* @returns {Boolean} true->input is a function
*/
export declare const isFunction: (input: any) => boolean;
export declare const getLanguage: () => string;
export declare function noop(e: any): void;
/**
* Get the object type string
* @param {*} input 任意类型的输入
* @returns {String} the object type string
*/
export declare const getType: (input: any) => any;
export declare function modifyObjectKey(obj: any, oldKey: any, newKey: any): any;
@@ -0,0 +1,249 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.modifyObjectKey = exports.getType = exports.noop = exports.getLanguage = exports.isFunction = exports.performanceNow = exports.handleNoDevicePermissionError = exports.handleRepeatedCallError = exports.retryPromise = exports.JSONToObject = exports.isJSON = exports.formatTimeInverse = exports.formatTime = exports.isNumber = exports.isBoolean = exports.isString = exports.isUrl = exports.isPrivateKey = exports.isArray = exports.isPlainObject = exports.isUndefined = void 0;
const index_1 = require("../const/index");
const tuiGlobal_1 = __importDefault(require("../TUIGlobal/tuiGlobal"));
const isUndefined = function (input) {
return typeof input === index_1.NAME.UNDEFINED;
};
exports.isUndefined = isUndefined;
const isPlainObject = function (input) {
// 注意不能使用以下方式判断,因为IE9/IE10下,对象的__proto__是 undefined
// return isObject(input) && input.__proto__ === Object.prototype;
if (typeof input !== index_1.NAME.OBJECT || input === null) {
return false;
}
const proto = Object.getPrototypeOf(input);
if (proto === null) { // edge case Object.create(null)
return true;
}
let baseProto = proto;
while (Object.getPrototypeOf(baseProto) !== null) {
baseProto = Object.getPrototypeOf(baseProto);
}
// 原型链第一个和最后一个比较
return proto === baseProto;
};
exports.isPlainObject = isPlainObject;
const isArray = function (input) {
if (typeof Array.isArray === index_1.NAME.FUNCTION) {
return Array.isArray(input);
}
return Object.prototype.toString.call(input).match(/^\[object (.*)\]$/)[1].toLowerCase() === index_1.NAME.ARRAY;
};
exports.isArray = isArray;
const isPrivateKey = function (key) {
return key.startsWith('_');
};
exports.isPrivateKey = isPrivateKey;
const isUrl = function (url) {
return /^(https?:\/\/(([a-zA-Z0-9]+-?)+[a-zA-Z0-9]+\.)+[a-zA-Z]+)(:\d+)?(\/.*)?(\?.*)?(#.*)?$/.test(url);
};
exports.isUrl = isUrl;
/**
* 检测input类型是否为string
* @param {*} input 任意类型的输入
* @returns {Boolean} true->string / false->not a string
*/
const isString = function (input) {
return typeof input === index_1.NAME.STRING;
};
exports.isString = isString;
const isBoolean = function (input) {
return typeof input === index_1.NAME.BOOLEAN;
};
exports.isBoolean = isBoolean;
const isNumber = function (input) {
return (
// eslint-disable-next-line
input !== null &&
((typeof input === index_1.NAME.NUMBER && !isNaN(input - 0)) || (typeof input === index_1.NAME.OBJECT && input.constructor === Number)));
};
exports.isNumber = isNumber;
function formatTime(secondTime) {
const hours = Math.floor(secondTime / 3600);
const minutes = Math.floor((secondTime % 3600) / 60);
const seconds = Math.floor(secondTime % 60);
let callDurationStr = hours > 9 ? `${hours}` : `0${hours}`;
callDurationStr += minutes > 9 ? `:${minutes}` : `:0${minutes}`;
callDurationStr += seconds > 9 ? `:${seconds}` : `:0${seconds}`;
return callDurationStr;
}
exports.formatTime = formatTime;
function formatTimeInverse(stringTime) {
const list = stringTime.split(':');
return parseInt(list[0]) * 3600 + parseInt(list[1]) * 60 + parseInt(list[2]); // eslint-disable-line
}
exports.formatTimeInverse = formatTimeInverse;
// Determine if it is a JSON string
function isJSON(str) {
if (typeof str === index_1.NAME.STRING) {
try {
const data = JSON.parse(str);
if (data) {
return true;
}
return false;
}
catch (error) {
console.debug(error);
return false;
}
}
return false;
}
exports.isJSON = isJSON;
// Determine if it is a JSON string
const JSONToObject = function (str) {
if (!str || !isJSON(str)) {
return str;
}
return JSON.parse(str);
};
exports.JSONToObject = JSONToObject;
/**
* 重试函数, catch 时,重试
* @param {Promise} promise 需重试的函数
* @param {number} num 需要重试的次数
* @param {number} time 间隔时间(s
* @returns {Promise<any>} im 接口的 response 原样返回
*/
const retryPromise = (promise, num = 6, time = 0.5) => {
let n = num;
const func = () => promise;
return func()
.catch((error) => {
if (n === 0) {
throw error;
}
const timer = setTimeout(() => {
func();
clearTimeout(timer);
n = n - 1;
}, time * 1000);
});
};
exports.retryPromise = retryPromise;
// /**
// * 节流函数(目前 TUICallKit 增加防重调用装饰器,该方法可删除)
// * @param {Function} func 传入的函数
// * @param {wait} time 间隔时间(ms
// */
// export const throttle = (func: Function, wait: number) => {
// let previousTime = 0;
// return function () {
// const now = Date.now();
// const args = [...arguments];
// if (now - previousTime > wait) {
// func.apply(this, args);
// previousTime = now;
// }
// };
// }
/**
* web call engine 重复调用时的错误, 这种错误在 TUICallKit 应该忽略
* @param {any} error 错误信息
* @returns {Boolean}
*/
function handleRepeatedCallError(error) {
if ((error === null || error === void 0 ? void 0 : error.message.indexOf('is ongoing, please avoid repeated calls')) !== -1) {
return true;
}
return false;
}
exports.handleRepeatedCallError = handleRepeatedCallError;
/**
* 设备无权限时的错误处理
* @param {any} error 错误信息
* @returns {Boolean}
*/
function handleNoDevicePermissionError(error) {
const { message } = error;
if (message.indexOf('NotAllowedError: Permission denied') !== -1) {
return true;
}
return false;
}
exports.handleNoDevicePermissionError = handleNoDevicePermissionError;
/*
* 获取向下取整的 performance.now() 值
* @export
* @return {Number}
*/
function performanceNow() {
// 在不支持 performance.now 的浏览器中,使用 Date.now()
// 例如 ie 9ie 10,避免加载 sdk 时报错
if (!performance || !performance.now) {
return Date.now();
}
return Math.floor(performance.now());
}
exports.performanceNow = performanceNow;
/**
* 检测input类型是否为function
* @param {*} input 任意类型的输入
* @returns {Boolean} true->input is a function
*/
const isFunction = function (input) {
return typeof input === index_1.NAME.FUNCTION;
};
exports.isFunction = isFunction;
/*
* 获取浏览器语言
* @export
* @return {zh-cn | en}
*/
const getLanguage = () => {
if (tuiGlobal_1.default.getInstance().isWeChat) {
return 'zh-cn';
}
// @ts-ignore
const lang = ((navigator === null || navigator === void 0 ? void 0 : navigator.language) || (navigator === null || navigator === void 0 ? void 0 : navigator.userLanguage) || '').substr(0, 2);
let language = 'en';
switch (lang) {
case 'zh':
language = 'zh-cn';
break;
case 'ja':
language = 'ja_JP';
break;
default:
language = 'en';
}
return language;
};
exports.getLanguage = getLanguage;
function noop(e) { }
exports.noop = noop;
/**
* Get the object type string
* @param {*} input 任意类型的输入
* @returns {String} the object type string
*/
const getType = function (input) {
return Object.prototype.toString
.call(input)
.match(/^\[object (.*)\]$/)[1]
.toLowerCase();
};
exports.getType = getType;
// 修改对象键名
function modifyObjectKey(obj, oldKey, newKey) {
if (!obj.hasOwnProperty(oldKey)) {
return obj;
}
const newObj = {};
Object.keys(obj).forEach(key => {
if (key === oldKey) {
newObj[newKey] = obj[key];
}
else {
newObj[key] = obj[key];
}
});
return newObj;
}
exports.modifyObjectKey = modifyObjectKey;
@@ -0,0 +1,10 @@
export declare const IN_WX_MINI_APP: boolean;
export declare const IN_UNI_NATIVE_APP: boolean;
export declare const IN_MINI_APP: boolean;
export declare const IN_UNI_APP: boolean;
export declare const IN_BROWSER: boolean;
export declare const APP_NAMESPACE: any;
export declare const IS_H5: boolean;
export declare const IS_PC: boolean;
export declare const IS_WIN: any;
export declare const IS_MAC: any;
@@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IS_MAC = exports.IS_WIN = exports.IS_PC = exports.IS_H5 = exports.APP_NAMESPACE = exports.IN_BROWSER = exports.IN_UNI_APP = exports.IN_MINI_APP = exports.IN_UNI_NATIVE_APP = exports.IN_WX_MINI_APP = void 0;
// 在 uniApp 框架下,打包 H5、ios app、android app 时存在 wx/qq/tt/swan/my 等变量会导致引入 web sdk 环境判断失效
// 小程序 getSystemInfoSync 返回的 fontSizeSetting 在 H5 和 app 中为 undefined,所以通过 fontSizeSetting 增强小程序环境判断
// wx 小程序
exports.IN_WX_MINI_APP = (typeof wx !== 'undefined' && typeof wx.getSystemInfoSync === 'function' && Boolean(wx.getSystemInfoSync().fontSizeSetting));
// 用 uni-app 打包 native app,此时运行于 js core,无 window 等对象,此时调用 api 都得 uni.xxx,由于风格跟小程序类似,就归为 IN_MINI_APP 的一种
exports.IN_UNI_NATIVE_APP = (typeof uni !== 'undefined' && typeof uni === 'undefined');
exports.IN_MINI_APP = exports.IN_WX_MINI_APP || exports.IN_UNI_NATIVE_APP;
exports.IN_UNI_APP = (typeof uni !== 'undefined');
// 在 uniApp 框架下,由于客户打包 ios app、android app 时 window 不一定存在,所以通过 !IN_MINI_APP 进行判断
// 非 uniApp 框架下,仍然通过 window 结合 IN_MINI_APP 进行判断,可兼容 Taro3.0+ 暴露 window 对象引起的 IN_BROWSER 判断失效问题
exports.IN_BROWSER = (function () {
if (typeof uni !== 'undefined') {
return !exports.IN_MINI_APP;
}
return (typeof window !== 'undefined') && !exports.IN_MINI_APP;
}());
// 命名空间
exports.APP_NAMESPACE = (function () {
if (exports.IN_WX_MINI_APP) {
return wx;
}
if (exports.IN_UNI_APP) {
return uni;
}
return window;
}());
// eslint-disable-next-line no-mixed-operators
const USER_AGENT = exports.IN_BROWSER && window && window.navigator && window.navigator.userAgent || '';
const IS_ANDROID = /Android/i.test(USER_AGENT);
const IS_WIN_PHONE = /(?:Windows Phone)/.test(USER_AGENT);
const IS_SYMBIAN = /(?:SymbianOS)/.test(USER_AGENT);
const IS_IOS = /iPad/i.test(USER_AGENT) || /iPhone/i.test(USER_AGENT) || /iPod/i.test(USER_AGENT);
exports.IS_H5 = IS_ANDROID || IS_WIN_PHONE || IS_SYMBIAN || IS_IOS;
exports.IS_PC = exports.IN_BROWSER && !exports.IS_H5;
exports.IS_WIN = exports.IS_PC && USER_AGENT.includes('Windows NT');
exports.IS_MAC = exports.IS_PC && USER_AGENT.includes('Mac');
@@ -0,0 +1 @@
export declare function checkLocalMP3FileExists(src: string): Promise<boolean>;
@@ -0,0 +1,33 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkLocalMP3FileExists = void 0;
function checkLocalMP3FileExists(src) {
return __awaiter(this, void 0, void 0, function* () {
if (!src)
return false;
try {
const response = yield new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('HEAD', src, true);
xhr.onload = () => resolve(xhr);
xhr.onerror = () => reject(xhr);
xhr.send();
});
return response.status === 200 && response.getResponseHeader('Content-Type') === 'audio/mpeg';
}
catch (error) {
console.warn(error);
return false;
}
});
}
exports.checkLocalMP3FileExists = checkLocalMP3FileExists;
@@ -0,0 +1,2 @@
declare const isEmpty: (input: any) => boolean;
export default isEmpty;
@@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const common_utils_1 = require("./common-utils");
const isEmpty = function (input) {
// Null and Undefined...
if (input === null || typeof (input) === 'undefined')
return true;
// Booleans...
if (typeof input === 'boolean')
return false;
// Numbers...
if (typeof input === 'number')
return input === 0;
// Strings...
if (typeof input === 'string')
return input.length === 0;
// Functions...
if (typeof input === 'function')
return input.length === 0;
// Arrays...
if (Array.isArray(input))
return input.length === 0;
// Errors...
if (input instanceof Error)
return input.message === '';
// plain object
if ((0, common_utils_1.isPlainObject)(input)) {
// eslint-disable-next-line
for (const key in input) {
if (Object.prototype.hasOwnProperty.call(input, key)) {
return false;
}
}
return true;
}
return false;
};
exports.default = isEmpty;
@@ -0,0 +1,64 @@
/// <reference types="node" />
/**
* 定时器,功能:
* 1. 支持定时执行回调 [1,n] 次,用于常规延迟、定时操作
* @example
* // 默认嵌套执行,count=0 无限次
* timer.run(callback, {delay: 2000});
* // count=1 等同于 原始setTimeout
* timer.run(callback, {delay: 2000, count:0});
* 2. 支持 RAF 执行回调,用于小流渲染,audio音量获取等任务,需要渲染频率稳定,支持页面退后台后,用 setTimeout 接管,最短 1s 执行一次
* @example
* // 默认60fps,可以根据单位时长换算,默认开启后台执行
* timer.run('raf', callback, {fps: 60});
* // 设置执行次数
* timer.run('raf', callback, {fps: 60, count: 300, backgroundTask: false});
* 3. 支持空闲任务执行回调, requestIdleCallback 在帧渲染的空闲时间执行任务,可以用于 storage 写入等低优先级的任务
* @example
* // 支持原生setInterval 但不推荐使用,定时任务推荐用 timeout
* timer.run('interval', callback, {delay:2000, count:10})
*/
declare class Timer {
static taskMap: Map<any, any>;
static currentTaskID: number;
static generateTaskID(): number;
/**
*
* @param {string} taskName 'interval' 'timeout'
* @param {function} callback
* @param {object} options include:
* @param {number} options.delay millisecond
* @param {number} options.count 定时器回调执行次数,0 无限次 or n次
* @param {boolean} options.backgroundTask 在页面静默后是否继续执行定时器
*/
static run(taskName: any, callback: any, options: any): any;
/**
* 定时循环执行回调函数
* 可以指定循环的时间间隔
* 可以指定循环次数
* @param {object} taskItem
* @param {function} callback
* @param {*} delay
* @param {*} count
* @returns ID
*/
static interval(taskItem: any): NodeJS.Timer;
/**
* 延迟执行回调
* count = 0,循环
* count = n, 执行n次
* @param {object} taskItem
*
*/
static timeout(taskItem: any): NodeJS.Timeout;
static hasTask(taskID: any): boolean;
static clearTask(taskID: any): boolean;
/**
* 1. 如果已移除出定时队列,退出当前任务
* 2. 如果当前任务已满足次数限制,则退出当前任务
* @param {object} taskItem
* @returns
*/
static isBreakLoop(taskItem: any): boolean;
}
export default Timer;
@@ -0,0 +1,154 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-disable */
const common_utils_1 = require("./common-utils");
const index_1 = require("../const/index");
/**
* 定时器,功能:
* 1. 支持定时执行回调 [1,n] 次,用于常规延迟、定时操作
* @example
* // 默认嵌套执行,count=0 无限次
* timer.run(callback, {delay: 2000});
* // count=1 等同于 原始setTimeout
* timer.run(callback, {delay: 2000, count:0});
* 2. 支持 RAF 执行回调,用于小流渲染,audio音量获取等任务,需要渲染频率稳定,支持页面退后台后,用 setTimeout 接管,最短 1s 执行一次
* @example
* // 默认60fps,可以根据单位时长换算,默认开启后台执行
* timer.run('raf', callback, {fps: 60});
* // 设置执行次数
* timer.run('raf', callback, {fps: 60, count: 300, backgroundTask: false});
* 3. 支持空闲任务执行回调, requestIdleCallback 在帧渲染的空闲时间执行任务,可以用于 storage 写入等低优先级的任务
* @example
* // 支持原生setInterval 但不推荐使用,定时任务推荐用 timeout
* timer.run('interval', callback, {delay:2000, count:10})
*/
class Timer {
static generateTaskID() {
return this.currentTaskID++;
}
/**
*
* @param {string} taskName 'interval' 'timeout'
* @param {function} callback
* @param {object} options include:
* @param {number} options.delay millisecond
* @param {number} options.count 定时器回调执行次数,0 无限次 or n次
* @param {boolean} options.backgroundTask 在页面静默后是否继续执行定时器
*/
static run(taskName = index_1.NAME.TIMEOUT, callback, options) {
// default options
if (taskName === index_1.NAME.INTERVAL) {
options = Object.assign({ delay: 2000, count: 0, backgroundTask: true }, options);
}
else {
options = Object.assign({ delay: 2000, count: 0, backgroundTask: true }, options);
}
// call run(function, {...})
if ((0, common_utils_1.isPlainObject)(callback)) {
options = Object.assign(Object.assign({}, options), callback);
}
if ((0, common_utils_1.isFunction)(taskName)) {
callback = taskName;
taskName = index_1.NAME.TIMEOUT;
}
// 1. 创建 taskID,作为 timer task 的唯一标识,在本函数执行完后返回,用于在调用的地方实现互斥逻辑
// 2. 根据 taskName 执行相应的函数
const taskItem = Object.assign({ taskID: this.generateTaskID(), loopCount: 0, intervalID: null, timeoutID: null, taskName,
callback }, options);
this.taskMap.set(taskItem.taskID, taskItem);
// console.log(`timer run task:${taskItem.taskName}, task queue size: ${this.taskMap.size}`);
if (taskName === index_1.NAME.INTERNAL) {
this.interval(taskItem);
}
else {
this.timeout(taskItem);
}
return taskItem.taskID;
}
/**
* 定时循环执行回调函数
* 可以指定循环的时间间隔
* 可以指定循环次数
* @param {object} taskItem
* @param {function} callback
* @param {*} delay
* @param {*} count
* @returns ID
*/
static interval(taskItem) {
// setInterval 缺点,浏览器退后台会降频,循环执行间隔时间不可靠
// 创建进入定时器循环的任务函数,函数内:1. 判断是否满足执行条件,2.执行 callback
// 通过 setInterval 执行任务函数
// 将 intervalID 记录到 taskMap 对应的 item
const task = () => {
taskItem.callback();
taskItem.loopCount += 1;
if (this.isBreakLoop(taskItem)) {
return;
}
};
return taskItem.intervalID = setInterval(task, taskItem.delay);
}
/**
* 延迟执行回调
* count = 0,循环
* count = n, 执行n次
* @param {object} taskItem
*
*/
static timeout(taskItem) {
// setTimeout 浏览器退后台,延迟变为至少1s
const task = () => {
// 执行回调
taskItem.callback();
taskItem.loopCount += 1;
if (this.isBreakLoop(taskItem)) {
return;
}
// 不修正延迟,每次callback间隔平均
return taskItem.timeoutID = setTimeout(task, taskItem.delay);
};
return taskItem.timeoutID = setTimeout(task, taskItem.delay);
}
static hasTask(taskID) {
return this.taskMap.has(taskID);
}
static clearTask(taskID) {
// console.log('timer clearTask start', `| taskID:${taskID} | size:${this.taskMap.size}`);
if (!this.taskMap.has(taskID)) {
return true;
}
const { intervalID, timeoutID, onVisibilitychange } = this.taskMap.get(taskID);
if (intervalID) {
clearInterval(intervalID);
}
if (timeoutID) {
clearTimeout(timeoutID);
}
if (onVisibilitychange) {
document.removeEventListener('visibilitychange', onVisibilitychange);
}
this.taskMap.delete(taskID);
// console.log('timer clearTask end ', `| size:${this.taskMap.size}`);
return true;
}
/**
* 1. 如果已移除出定时队列,退出当前任务
* 2. 如果当前任务已满足次数限制,则退出当前任务
* @param {object} taskItem
* @returns
*/
static isBreakLoop(taskItem) {
if (!this.taskMap.has(taskItem.taskID)) {
return true;
}
if (taskItem.count !== 0 && taskItem.loopCount >= taskItem.count) {
this.clearTask(taskItem.taskID);
return true;
}
return false;
}
}
Timer.taskMap = new Map();
Timer.currentTaskID = 1;
exports.default = Timer;
@@ -0,0 +1,10 @@
/**
* 装饰器:阻止函数重复调用
* @export
* @param {Object} options 入参
* @param {Function} options.fn 函数
* @param {Object} options.context 上下文对象
* @param {String} options.name 函数名
* @returns {Function} 封装后的函数
*/
export declare function avoidRepeatedCall(): (target: any, name: string, descriptor: any) => any;
@@ -0,0 +1,49 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.avoidRepeatedCall = void 0;
const index_1 = require("../../const/index");
/**
* 装饰器:阻止函数重复调用
* @export
* @param {Object} options 入参
* @param {Function} options.fn 函数
* @param {Object} options.context 上下文对象
* @param {String} options.name 函数名
* @returns {Function} 封装后的函数
*/
function avoidRepeatedCall() {
return function (target, name, descriptor) {
const oldFn = descriptor.value;
const isCallingSet = new Set();
descriptor.value = function (...args) {
return __awaiter(this, void 0, void 0, function* () {
if (isCallingSet.has(this)) {
console.warn((`${index_1.NAME.PREFIX}previous ${name}() is ongoing, please avoid repeated calls`));
// throw new Error(`previous ${name}() is ongoing, please avoid repeated calls`);
return;
}
try {
isCallingSet.add(this);
const result = yield oldFn.apply(this, args);
isCallingSet.delete(this);
return result;
}
catch (error) {
isCallingSet.delete(this);
throw error;
}
});
};
return descriptor;
};
}
exports.avoidRepeatedCall = avoidRepeatedCall;
@@ -0,0 +1,4 @@
import { avoidRepeatedCall } from './avoidRepeatedCall';
import { paramValidate } from './validateParams';
import { VALIDATE_PARAMS } from './validateConfig';
export { VALIDATE_PARAMS, paramValidate, avoidRepeatedCall, };
@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.avoidRepeatedCall = exports.paramValidate = exports.VALIDATE_PARAMS = void 0;
const avoidRepeatedCall_1 = require("./avoidRepeatedCall");
Object.defineProperty(exports, "avoidRepeatedCall", { enumerable: true, get: function () { return avoidRepeatedCall_1.avoidRepeatedCall; } });
const validateParams_1 = require("./validateParams");
Object.defineProperty(exports, "paramValidate", { enumerable: true, get: function () { return validateParams_1.paramValidate; } });
const validateConfig_1 = require("./validateConfig");
Object.defineProperty(exports, "VALIDATE_PARAMS", { enumerable: true, get: function () { return validateConfig_1.VALIDATE_PARAMS; } });
@@ -0,0 +1,173 @@
import { VideoResolution, VideoDisplayMode } from "../../const/index";
export declare const VALIDATE_PARAMS: {
init: {
SDKAppID: {
required: boolean;
rules: any[];
allowEmpty: boolean;
};
userID: {
required: boolean;
rules: any[];
allowEmpty: boolean;
};
userSig: {
required: boolean;
rules: any[];
allowEmpty: boolean;
};
tim: {
required: boolean;
rules: any[];
};
};
call: {
userID: {
required: boolean;
rules: any[];
allowEmpty: boolean;
};
type: {
required: boolean;
rules: any[];
range: number[];
allowEmpty: boolean;
};
roomID: {
required: boolean;
rules: any[];
range: string;
allowEmpty: boolean;
};
userData: {
required: boolean;
rules: any[];
allowEmpty: boolean;
};
timeout: {
required: boolean;
rules: any[];
allowEmpty: boolean;
};
};
groupCall: {
userIDList: {
required: boolean;
rules: any[];
allowEmpty: boolean;
};
type: {
required: boolean;
rules: any[];
range: number[];
allowEmpty: boolean;
};
groupID: {
required: boolean;
rules: any[];
allowEmpty: boolean;
};
roomID: {
required: boolean;
rules: any[];
range: string;
allowEmpty: boolean;
};
timeout: {
required: boolean;
rules: any[];
allowEmpty: boolean;
};
userData: {
required: boolean;
rules: any[];
allowEmpty: boolean;
};
offlinePushInfo: {
required: boolean;
rules: any[];
allowEmpty: boolean;
};
};
joinInGroupCall: {
type: {
required: boolean;
rules: any[];
range: number[];
allowEmpty: boolean;
};
groupID: {
required: boolean;
rules: any[];
allowEmpty: boolean;
};
roomID: {
required: boolean;
rules: any[];
allowEmpty: boolean;
};
};
inviteUser: {
userIDList: {
required: boolean;
rules: any[];
allowEmpty: boolean;
};
};
setSelfInfo: {
nickName: {
required: boolean;
rules: any[];
allowEmpty: boolean;
};
avatar: {
required: boolean;
rules: any[];
allowEmpty: boolean;
};
};
enableFloatWindow: {
key: string;
required: boolean;
rules: any[];
allowEmpty: boolean;
}[];
enableAIVoice: {
key: string;
required: boolean;
rules: any[];
allowEmpty: boolean;
}[];
enableMuteMode: {
key: string;
required: boolean;
rules: any[];
allowEmpty: boolean;
}[];
setCallingBell: {
key: string;
required: boolean;
rules: any[];
allowEmpty: boolean;
}[];
setLanguage: {
key: string;
required: boolean;
rules: any[];
allowEmpty: boolean;
}[];
setVideoDisplayMode: {
key: string;
required: boolean;
rules: any[];
range: VideoDisplayMode[];
allowEmpty: boolean;
}[];
setVideoResolution: {
key: string;
required: boolean;
rules: any[];
range: VideoResolution[];
allowEmpty: boolean;
}[];
};
@@ -0,0 +1,190 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.VALIDATE_PARAMS = void 0;
const index_1 = require("../../const/index");
exports.VALIDATE_PARAMS = {
init: {
SDKAppID: {
required: true,
rules: [index_1.NAME.NUMBER],
allowEmpty: false,
},
userID: {
required: true,
rules: [index_1.NAME.STRING],
allowEmpty: false,
},
userSig: {
required: true,
rules: [index_1.NAME.STRING],
allowEmpty: false,
},
tim: {
required: false,
rules: [index_1.NAME.OBJECT],
},
},
call: {
userID: {
required: true,
rules: [index_1.NAME.STRING],
allowEmpty: false
},
type: {
required: true,
rules: [index_1.NAME.NUMBER],
range: [1, 2],
allowEmpty: false
},
roomID: {
required: false,
rules: [index_1.NAME.NUMBER],
range: `1~${index_1.MAX_NUMBER_ROOM_ID}`,
allowEmpty: false,
},
userData: {
required: false,
rules: [index_1.NAME.STRING],
allowEmpty: false,
},
timeout: {
required: false,
rules: [index_1.NAME.NUMBER],
allowEmpty: false
}
},
groupCall: {
userIDList: {
required: true,
rules: [index_1.NAME.ARRAY],
allowEmpty: false
},
type: {
required: true,
rules: [index_1.NAME.NUMBER],
range: [1, 2],
allowEmpty: false
},
groupID: {
required: true,
rules: [index_1.NAME.STRING],
allowEmpty: false
},
roomID: {
required: false,
rules: [index_1.NAME.NUMBER],
range: `1~${index_1.MAX_NUMBER_ROOM_ID}`,
allowEmpty: false
},
timeout: {
required: false,
rules: [index_1.NAME.NUMBER],
allowEmpty: false
},
userData: {
required: false,
rules: [index_1.NAME.STRING],
allowEmpty: false,
},
offlinePushInfo: {
required: false,
rules: [index_1.NAME.OBJECT],
allowEmpty: false,
},
},
joinInGroupCall: {
type: {
required: true,
rules: [index_1.NAME.NUMBER],
range: [1, 2],
allowEmpty: false
},
groupID: {
required: true,
rules: [index_1.NAME.STRING],
allowEmpty: false
},
roomID: {
required: true,
rules: [index_1.NAME.NUMBER],
allowEmpty: false,
},
},
inviteUser: {
userIDList: {
required: true,
rules: [index_1.NAME.ARRAY],
allowEmpty: false
},
},
setSelfInfo: {
nickName: {
required: false,
rules: [index_1.NAME.STRING],
allowEmpty: false,
},
avatar: {
required: false,
rules: [index_1.NAME.STRING],
allowEmpty: false,
}
},
enableFloatWindow: [
{
key: "enable",
required: false,
rules: [index_1.NAME.BOOLEAN],
allowEmpty: false,
}
],
enableAIVoice: [
{
key: "enable",
required: true,
rules: [index_1.NAME.BOOLEAN],
allowEmpty: false,
}
],
enableMuteMode: [
{
key: "enable",
required: true,
rules: [index_1.NAME.BOOLEAN],
allowEmpty: false,
}
],
setCallingBell: [
{
key: "filePath",
required: false,
rules: [index_1.NAME.STRING],
allowEmpty: true,
}
],
setLanguage: [
{
key: "language",
required: true,
rules: [index_1.NAME.STRING],
allowEmpty: false
}
],
setVideoDisplayMode: [
{
key: "displayMode",
required: true,
rules: [index_1.NAME.STRING],
range: [index_1.VideoDisplayMode.CONTAIN, index_1.VideoDisplayMode.COVER, index_1.VideoDisplayMode.FILL],
allowEmpty: false
}
],
setVideoResolution: [
{
key: "resolution",
required: true,
rules: [index_1.NAME.STRING],
range: [index_1.VideoResolution.RESOLUTION_1080P, index_1.VideoResolution.RESOLUTION_480P, index_1.VideoResolution.RESOLUTION_720P],
allowEmpty: false
}
]
};
@@ -0,0 +1 @@
export declare function paramValidate(config: any): (target: any, propertyName: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
@@ -0,0 +1,86 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.paramValidate = void 0;
const common_utils_1 = require("../common-utils");
const index_1 = require("../../const/index");
const PREFIX = index_1.NAME.PREFIX + "API";
function paramValidate(config) {
return function (target, propertyName, descriptor) {
let method = descriptor.value;
descriptor.value = function (...args) {
doValidate.call(this, config, args, propertyName);
return method.apply(this, args);
};
return descriptor;
};
}
exports.paramValidate = paramValidate;
function doValidate(config, args, name) {
try {
// 兼容 init 方法中: SDKAppID sdkAppID 两种写法的参数校验判断
if (!args[0].SDKAppID) {
config = (0, common_utils_1.modifyObjectKey)(config, "SDKAppID", "sdkAppID");
}
if ((0, common_utils_1.isArray)(config)) {
for (let i = 0; i < config.length; i++) {
check.call(this, Object.assign(Object.assign({}, config[i]), { value: args[i], name }));
}
}
else {
for (const key in config) {
if (config.hasOwnProperty(key)) {
check.call(this, Object.assign(Object.assign({}, config[key]), { value: args[0][key], name,
key }));
}
}
}
}
catch (error) {
console.error(error);
throw error;
}
}
function check({ required, rules, range, value, allowEmpty, name, key }) {
// 用户没传指定参数
if ((0, common_utils_1.isUndefined)(value)) {
// 检查必填参数, 若配置是必填则报错
if (required) {
throw new Error(`${PREFIX}<${name}>: ${key} is required.`);
}
else {
return;
}
}
// 判断参数类型是否正确
const result = rules.some((item) => item === (0, common_utils_1.getType)(value));
let type = '';
if (!result) {
for (let i = 0; i < rules.length; i++) {
let str = rules[i];
str = str.replace(str[0], str[0].toUpperCase());
type += `${str}/`;
}
type = type.substring(0, type.length - 1);
throw new Error(`${PREFIX}<${name}>: ${key} must be ${type}, current ${key} is ${typeof value}.`);
}
// 不允许传空值, 例如: '', ' '
if (allowEmpty === false) {
const isEmptyString = (0, common_utils_1.isString)(value) && value.trim() === '';
if (isEmptyString) {
throw new Error(`${PREFIX}<${name}>: ${key} is blank.`);
}
}
// 判断是否符合限制条件
if ((0, common_utils_1.isArray)(range)) {
if (range && range.indexOf(value) === -1) {
throw new Error(`${PREFIX}<${name}>: ${key} error, only be ${range}, current ${key} is ${value}.`);
}
}
// 取值范围, 前闭后闭
if ((0, common_utils_1.isString)(range) && range.indexOf('~') !== -1) {
const valueList = range.split('~');
if (value < +valueList[0] || value > +valueList[1] || ((0, common_utils_1.isNumber)(value) && Number.isNaN(value))) {
throw new Error(`${PREFIX}<${name}>: ${key} error, only be ${range}, current ${key} is ${value}.`);
}
}
}
@@ -0,0 +1,2 @@
export * from '@vue/composition-api';
export * from 'vue';
+23
View File
@@ -0,0 +1,23 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
// #ifndef VUE3
// @ts-ignore
__exportStar(require("@vue/composition-api"), exports);
// #endif
// #ifdef VUE3
__exportStar(require("vue"), exports);
// #endif
@@ -0,0 +1,63 @@
import LibGenerateTestUserSig from './lib-generate-test-usersig-es.min.js';
/**
* 腾讯云 SDKAppId,需要替换为您自己账号下的 SDKAppId。
*
* 进入腾讯云实时音视频[控制台](https://console.cloud.tencent.com/rav ) 创建应用,即可看到 SDKAppId,
* 它是腾讯云用于区分客户的唯一标识。
*/
let SDKAPPID = 0;
/**
* 签名过期时间,建议不要设置的过短
* <p>
* 时间单位:秒
* 默认时间:7 x 24 x 60 x 60 = 604800 = 7 天
*/
const EXPIRETIME = 604800;
/**
* 计算签名用的加密密钥,获取步骤如下:
*
* step1. 进入腾讯云实时音视频[控制台](https://console.cloud.tencent.com/rav ),如果还没有应用就创建一个,
* step2. 单击“应用配置”进入基础配置页面,并进一步找到“帐号体系集成”部分。
* step3. 点击“查看密钥”按钮,就可以看到计算 UserSig 使用的加密的密钥了,请将其拷贝并复制到如下的变量中
*
* 注意:该方案仅适用于调试Demo,正式上线前请将 UserSig 计算代码和密钥迁移到您的后台服务器上,以避免加密密钥泄露导致的流量盗用。
* 文档:https://cloud.tencent.com/document/product/647/17275#Server
*/
let SECRETKEY = '';
/*
* Module: GenerateTestUserSig
*
* Function: 用于生成测试用的 UserSig,UserSig 是腾讯云为其云服务设计的一种安全保护签名。
* 其计算方法是对 SDKAppID、UserID 和 EXPIRETIME 进行加密,加密算法为 HMAC-SHA256。
*
* Attention: 请不要将如下代码发布到您的线上正式版本的 App 中,原因如下:
*
* 本文件中的代码虽然能够正确计算出 UserSig,但仅适合快速调通 SDK 的基本功能,不适合线上产品,
* 这是因为客户端代码中的 SECRETKEY 很容易被反编译逆向破解,尤其是 Web 端的代码被破解的难度几乎为零。
* 一旦您的密钥泄露,攻击者就可以计算出正确的 UserSig 来盗用您的腾讯云流量。
*
* 正确的做法是将 UserSig 的计算代码和加密密钥放在您的业务服务器上,然后由 App 按需向您的服务器获取实时算出的 UserSig。
* 由于破解服务器的成本要高于破解客户端 App,所以服务器计算的方案能够更好地保护您的加密密钥。
*
* Referencehttps://cloud.tencent.com/document/product/647/17275#Server
*/
export function genTestUserSig({ userID, SDKAppID, SecretKey }) {
if (SDKAppID) SDKAPPID = SDKAppID;
if (SecretKey) SECRETKEY = SecretKey;
const generator = new LibGenerateTestUserSig(SDKAPPID, SECRETKEY, EXPIRETIME);
const userSig = generator.genTestUserSig(userID);
return {
SDKAppID: SDKAPPID,
userSig,
};
}
export default genTestUserSig;
File diff suppressed because one or more lines are too long
+3
View File
@@ -0,0 +1,3 @@
import { TUIGlobal, TUIStore, StoreName, TUICallKitServer, NAME, StatusChange as STATUS, CallRole, CallMediaType, VideoResolution, VideoDisplayMode, t } from './TUICallService/index';
declare const Version = "2.1.1";
export { TUIGlobal, TUIStore, StoreName, TUICallKitServer, NAME, STATUS, CallRole, CallMediaType, VideoResolution, VideoDisplayMode, Version, t, };
+17
View File
@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.t = exports.Version = exports.VideoDisplayMode = exports.VideoResolution = exports.CallMediaType = exports.CallRole = exports.STATUS = exports.NAME = exports.TUICallKitServer = exports.StoreName = exports.TUIStore = exports.TUIGlobal = void 0;
const index_1 = require("./TUICallService/index");
Object.defineProperty(exports, "TUIGlobal", { enumerable: true, get: function () { return index_1.TUIGlobal; } });
Object.defineProperty(exports, "TUIStore", { enumerable: true, get: function () { return index_1.TUIStore; } });
Object.defineProperty(exports, "StoreName", { enumerable: true, get: function () { return index_1.StoreName; } });
Object.defineProperty(exports, "TUICallKitServer", { enumerable: true, get: function () { return index_1.TUICallKitServer; } });
Object.defineProperty(exports, "NAME", { enumerable: true, get: function () { return index_1.NAME; } });
Object.defineProperty(exports, "STATUS", { enumerable: true, get: function () { return index_1.StatusChange; } });
Object.defineProperty(exports, "CallRole", { enumerable: true, get: function () { return index_1.CallRole; } });
Object.defineProperty(exports, "CallMediaType", { enumerable: true, get: function () { return index_1.CallMediaType; } });
Object.defineProperty(exports, "VideoResolution", { enumerable: true, get: function () { return index_1.VideoResolution; } });
Object.defineProperty(exports, "VideoDisplayMode", { enumerable: true, get: function () { return index_1.VideoDisplayMode; } });
Object.defineProperty(exports, "t", { enumerable: true, get: function () { return index_1.t; } });
const Version = '2.1.1'; // basic-demo 原来上报使用
exports.Version = Version;
+40
View File
@@ -0,0 +1,40 @@
{
"name": "@tencentcloud/call-uikit-wechat",
"version": "2.1.1",
"main": "./tuicall-uikit-vue.umd.js",
"module": "./tuicall-uikit-vue.es.js",
"types": "./types/index.d.ts",
"description": "An Open-source Voice & Video Calling UI Component Based on Tencent Cloud Service.",
"homepage": "https://cloud.tencent.com/document/product/647/78912",
"license": "ISC",
"repository": {
"type": "git",
"url": "https://github.com/tencentyun/TUICallKit/tree/main/uni-app/TUICallKit-Miniprogram",
"directory": "Web"
},
"dependencies": {
"@tencentcloud/tui-core": "latest",
"tim-upload-plugin": "^1.3.0",
"tuicall-engine-wx": "latest"
},
"bugs": {
"url": "https://github.com/tencentyun/TUICallKit/issues"
},
"keywords": [
"uikit",
"call",
"vue3",
"tencent",
"chat",
"vue",
"vue2",
"video",
"audio",
"voice",
"callkit",
"语音",
"视频",
"电话",
"通话"
]
}
@@ -0,0 +1,4 @@
Page({
data: {},
onShow() {},
});
@@ -0,0 +1,7 @@
{
"usingComponents": {
"TUICallKit": "../../TUICallKit/TUICallKit"
},
"navigationStyle": "custom",
"disableScroll": true
}
@@ -0,0 +1,3 @@
<view>
<TUICallKit></TUICallKit>
</view>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1017 B

Some files were not shown because too many files have changed in this diff Show More