<template> <div v-show="visible" class="home-container" ref="home-container" v-drag> <ElectronWindowsControlButtonView style="position: absolute; top: 0; right: 0" v-if="sharedMiscState.isElectronWindowsOrLinux" /> <div class="home"> <section class="menu-container"> <i class="icon-close el-icon-close" @click="close"></i> <div> <!-- todo tippy example --> <tippy v-if="sharedContactState.selfUserInfo" to="infoTrigger" interactive :animate-fill="false" placement="right" distant="7" theme="light" animation="fade" trigger="click" arrow > <UserCardView v-on:close="closeUserCard" :enable-update-portrait="true" :user-info="sharedContactState.selfUserInfo" /> </tippy> <a href="#" ><img v-if="sharedContactState.selfUserInfo" ref="userCardTippy" name="infoTrigger" class="avatar" draggable="false" @click="onClickPortrait()" :src="sharedContactState.selfUserInfo.portrait" alt="" /></a> </div> <nav class="menu"> <ul> <!-- 浼氳瘽 --> <li v-if="!single"> <div class="menu-item"> <i class="icon-ion-ios-chatboxes" v-bind:class="{ active: pageName === 'ConversationPage' }" @click="go2Conversation" ></i> <em v-show="unread > 0" class="badge">{{ unread > 99 ? "路路路" : unread }}</em> </div> </li> <!-- 鑱旂郴浜� --> <li v-if="showContact"> <div class="menu-item"> <i class="icon-ion-android-contact" v-bind:class="{ active: pageName === 'ContactPage' }" @click="go2Contact"></i> <em v-show="sharedContactState.unreadFriendRequestCount > 0" class="badge">{{ sharedContactState.unreadFriendRequestCount > 99 ? "99" : sharedContactState.unreadFriendRequestCount }}</em> </div> </li> <li v-if="sharedMiscState.isElectron"> <i class="icon-ion-android-favorite" v-bind:class="{ active: this.$router.currentRoute.path === '/home/fav' }" @click="go2Fav" ></i> </li> <!-- <li v-if="sharedMiscState.isElectron && sharedMiscState.wfc.isCommercialServer()"> <i class="icon-ion-ios-folder" v-bind:class="{ active: this.$router.currentRoute.path === '/home/files' }" @click="go2Files" ></i> </li>--> <li v-if="sharedMiscState.isElectron && sharedMiscState.enableOpenWorkSpace"> <i class="icon-ion-code-working" v-bind:class="{ active: this.$router.currentRoute.path === '/home/workspace' }" @click="go2Workspace" ></i> </li> <!-- 浼氳 --> <li v-if="supportConference"> <i class="icon-ion-speakerphone" v-bind:class="{ active: pageName === 'ConferencePortalPage' }" @click="go2Conference" ></i> </li> <li v-if="showSetting"> <i class="icon-ion-android-settings" v-bind:class="{ active: pageName === 'SettingPage' }" @click="go2Setting"></i> </li> </ul> </nav> </section> <conversation-page v-if="pageName === 'ConversationPage'" :single="single"></conversation-page> <contact-page v-if="pageName === 'ContactPage'"></contact-page> <conference-portal-page v-if="pageName === 'ConferencePortalPage'"></conference-portal-page> <setting-page v-if="pageName === 'SettingPage'"></setting-page> <div v-if="sharedMiscState.connectionStatus === -1" class="unconnected">缃戠粶杩炴帴鏂紑</div> <div class="drag-area" :style="dragAreaLeft"></div> </div> </div> </template> <script> import UserCardView from "./user/UserCardView.vue"; import store from "@/store.js"; import wfc from "../../wfc/client/wfc"; import EventType from "../../wfc/client/wfcEvent"; import ConnectionStatus from "../../wfc/client/connectionStatus"; import ElectronWindowsControlButtonView from "@/ui/common/ElectronWindowsControlButtonView.vue"; import { removeItem } from "../util/storageHelper"; // import { ipcRenderer } from "@/platform"; import avenginekit from "../../wfc/av/internal/engine.min"; import avenginekitproxy from "../../wfc/av/engine/avenginekitproxy"; import { Draggable } from "draggable-vue-directive"; // import IpcEventType from "@/wfc/ipcEventType"; import { isElectron } from "@/platform"; import ConversationPage from "@/ui/main/ConversationPage"; import ContactPage from "./ContactPage.vue"; import ConferencePortalPage from "@/ui/voip/conference/ConferencePortalPage"; import SettingPage from "@/ui/main/setting/SettingPage"; import { mapGetters } from "vuex"; export default { components: { UserCardView, ElectronWindowsControlButtonView, ConversationPage, ContactPage, ConferencePortalPage, SettingPage, }, props: { visible: { type: Boolean, }, single: Boolean, }, directives: { Draggable, }, data() { return { showContact: false, showSetting: false, sharedContactState: store.state.contact, sharedMiscState: store.state.misc, shareConversationState: store.state.conversation, supportConference: false, // supportConference: avenginekit.startConference !== undefined, isSetting: false, fileWindow: null, voipProxy: avenginekitproxy, }; }, methods: { onClickPortrait() { wfc.getUserInfo(this.sharedContactState.selfUserInfo.uid, true); }, go2Conversation() { if (this.pageName === "ConversationPage") { return; } // this.pageName = "ConversationPage"; this.$store.commit("SET_PAGE_NAME", "ConversationPage"); this.isSetting = false; }, go2Contact() { if (this.pageName === "ContactPage") { return; } // this.pageName = "ContactPage"; this.$store.commit("SET_PAGE_NAME", "ContactPage"); this.isSetting = false; }, go2Fav() { if (this.$router.currentRoute.path === "/home/fav") { return; } this.$router.replace("/home/fav"); this.isSetting = false; }, // go2Files() { // let hash = window.location.hash; // let url = window.location.origin; // if (hash) { // url = window.location.href.replace(hash, "#/files"); // } else { // url += "/files"; // } // ipcRenderer.send(IpcEventType.SHOW_FILE_WINDOW, { // url: url, // source: "file", // }); // console.log("show-file-window", url); // }, go2Workspace() { if (this.$router.currentRoute.path === "/home/workspace") { return; } this.$router.replace("/home/workspace"); this.isSetting = false; }, go2Conference() { if (this.pageName === "ConferencePortalPage") { return; } // this.pageName = "ConferencePortalPage"; this.$store.commit("SET_PAGE_NAME", "ConferencePortalPage"); this.isSetting = true; }, go2Setting() { if (this.pageName === "SettingPage") { return; } // this.pageName = "SettingPage"; this.$store.commit("SET_PAGE_NAME", "SettingPage"); this.isSetting = true; }, closeUserCard() { console.log("closeUserCard"); this.$refs["userCardTippy"]._tippy.hide(); }, onConnectionStatusChange(status) { if ( status === ConnectionStatus.ConnectionStatusRejected || status === ConnectionStatus.ConnectionStatusLogout || status === ConnectionStatus.ConnectionStatusSecretKeyMismatch || status === ConnectionStatus.ConnectionStatusTokenIncorrect || status === ConnectionStatus.ConnectionStatusKickedOff || // TODO 鏂綉鏃讹紝鏄剧ず缃戠粶鏂紑鐘舵€� // || status === ConnectionStatus.ConnectionStatusUnconnected wfc.getUserId() === "" ) { // if (this.$router.currentRoute.path !== "/") { // this.$router.replace({ path: "/" }); // } if (this.pageName !== "ConversationPage") { // this.pageName = "ConversationPage"; this.$store.commit("SET_PAGE_NAME", "ConversationPage"); } if ( status === ConnectionStatus.ConnectionStatusSecretKeyMismatch || status === ConnectionStatus.ConnectionStatusLogout || status === ConnectionStatus.ConnectionStatusTokenIncorrect || status === ConnectionStatus.ConnectionStatusKickedOff || status === ConnectionStatus.ConnectionStatusRejected ) { removeItem("wfcUserId"); removeItem("wfcToken"); avenginekitproxy.forceCloseVoipWindow(); console.error("杩炴帴澶辫触", ConnectionStatus.desc(status)); } } }, close() { this.$emit("update:visible", false); }, }, computed: { ...mapGetters(["pageName"]), unread() { let count = 0; this.shareConversationState.conversationInfoList.forEach((info) => { if (info.isSilent) { return; } let unreadCount = info.unreadCount; count += unreadCount.unread; }); return count; }, dragAreaLeft() { // 60涓哄乏杈硅彍鍗曟爮鐨勫搴︼紝261涓轰細璇濆垪琛ㄧ殑瀹藉害 if (this.isSetting) { return { left: "60px", }; } else { return { left: "calc(60px + 261px)", }; } }, }, created() { console.log("avenginekit.startConference", avenginekit.startConference); wfc.eventEmitter.on(EventType.ConnectionStatusChanged, this.onConnectionStatusChange); }, mounted() { if (avenginekitproxy.useIframe) { let voipIframe = this.$refs["voip-iframe"]; avenginekitproxy.setVoipIframe(voipIframe); } avenginekitproxy.onVoipCallErrorCallback = (errorCode) => { if (errorCode === -1) { this.$notify({ title: "涓嶈兘鍙戣捣鎴栨帴鍚柊鐨勯煶瑙嗛閫氳瘽", text: "鐩墠鏈夐煶瑙嗛閫氳瘽姝e湪杩涜涓�", type: "warn", }); } else if (errorCode === -2) { if (isElectron()) { console.error(`涓嶆敮鎸侀煶瑙嗛閫氳瘽锛屽師鍥犲彲鑳芥槸: 1. 鍙€氳繃杩欎釜缃戦〉娴嬭瘯娴忚鍣ㄥ闊宠棰戦€氳瘽鐨勬敮鎸佹儏鍐碉紝https://docs.wildfirechat.cn/webrtc/abilitytest/ 2. 纭繚绯荤粺宸叉巿浜堝綋鍓嶅簲鐢� 鎽勫儚澶� 鍜� 楹﹀厠椋� 鏉冮檺 `); } else { console.error(`涓嶆敮鎸侀煶瑙嗛閫氳瘽锛屽師鍥犲彲鑳芥槸: 1. 娴忚鍣ㄤ笂锛屽彧鏈夐€氳繃 http://localhost 鎴� https://xxxx 璁块棶鐨勭綉椤垫墠鏀寔闊宠棰戦€氳瘽 2. 鍙€氳繃杩欎釜缃戦〉娴嬭瘯娴忚鍣ㄥ闊宠棰戦€氳瘽鐨勬敮鎸佹儏鍐碉紝https://docs.wildfirechat.cn/webrtc/abilitytest/ 3. 纭繚娴忚鍣ㄥ凡鎺堜簣缃戦〉 鎽勫儚澶� 鍜� 楹﹀厠椋� 鏉冮檺 4. 纭繚绯荤粺宸叉巿浜堟祻瑙堝櫒 鎽勫儚澶� 鍜岄害鍏嬮 鏉冮檺 5. 閰嶇疆 https锛岃鍙傝€冿細https://docs.wildfirechat.cn/faq/web/https.html `); } this.$notify({ title: "涓嶆敮鎸侀煶瑙嗛閫氳瘽", text: "璇峰埌鎵嬫満涓婃帴鍚煶瑙嗛閫氳瘽", type: "warn", }); } }; }, destroyed() { wfc.eventEmitter.removeListener(EventType.ConnectionStatusChanged, this.onConnectionStatusChange); console.log("home destroy"); }, }; </script> <style lang="scss" scoped> .home-container { position: absolute; top: 100px; left: 100px; z-index: 9; } .menu-container { width: 60px; min-width: 60px; height: 100%; display: flex; flex-direction: column; align-items: center; /*background: linear-gradient(180deg, #292a2c 0%, #483a3a 100%);*/ background: #e9e4e0; border-top-left-radius: var(--main-border-radius); border-bottom-left-radius: var(--main-border-radius); padding: var(--home-menu-padding-top) 0 20px 0; -webkit-app-region: drag; position: relative; } .icon-close { position: absolute; top: 10px; left: 10px; z-index: 99; } .home { display: flex; width: calc(65vw - var(--main-margin-left) - var(--main-margin-right)); height: calc(100vh - var(--main-margin-top) - var(--main-margin-bottom)); justify-content: center; align-items: center; background-color: white; border-radius: var(--main-border-radius); } .avatar { background-color: gray; width: 35px; height: 35px; display: block; margin: 10px auto; border-radius: 3px; } .menu { flex: 1; } .menu ul { height: 100%; display: flex; flex-direction: column; -webkit-app-region: drag; } .menu ul li { margin: 10px; height: 40px; line-height: 50px; } .menu ul li:last-of-type { //margin-top: auto; margin-bottom: 20px; } .menu .menu-item { position: relative; } .menu .menu-item .badge { position: absolute; color: white; font-size: 10px; background-color: red; border-radius: 8px; min-width: 16px; height: 16px; padding: 0 5px; line-height: 16px; font-style: normal; text-align: center; right: -12px; top: 4px; } i { font-size: 26px; color: #868686; cursor: pointer; } i:hover { color: #1f64e4; } i.active { color: #3f64e4; } .drag-area { position: absolute; top: 0; height: 60px; right: 140px; z-index: -1; -webkit-app-region: drag; } .unconnected { position: absolute; top: 0; left: 60px; right: 0; color: red; padding: 15px 0; text-align: center; background: #f2f2f280; /*box-shadow: 0 0 1px #000;*/ } .voip-div-container { background: #292929; position: absolute; margin: auto; border-radius: 5px; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); display: flex; flex-direction: column; } //.voip-div-container.single { // width: 360px; // height: 640px; //} .voip-div-container.multi { width: 960px; height: 600px; } .voip-div-container.conference { width: 960px; height: 600px; } .voip-div-container .title { text-align: center; padding: 5px 0; background: #b6b6b6; display: flex; justify-content: center; align-content: center; } .voip-div-container .title i { pointer-events: none; } .voip-div-container .title i:hover { color: #868686; } .voip-div-container .title i:active { color: #868686; } .voip-div-container .content { flex: 1; border: none; } </style>