import m from 'http://devtalk.kospo.co.kr:3000/static/bundle/mithril-2.2.2/mithril.js' import {global} from "http://devtalk.kospo.co.kr:3000/static/js/module/variable.js"; import {talkMapping} from "http://devtalk.kospo.co.kr:3000/static/js/module/render.js"; import SockJS from "http://devtalk.kospo.co.kr:3000/static/bundle/sockjs-client-1.6.1/sockjs.js"; import {getTalkList, getMessageAll, getMoreMessageAll} from "http://devtalk.kospo.co.kr:3000/static/js/module/apis.js"; import {HubEvent} from "http://devtalk.kospo.co.kr:3000/static/js/module/hubEvent.js"; import {getApInfo} from "http://devtalk.kospo.co.kr:3000/static/js/module/apis.js"; import Common from "http://devtalk.kospo.co.kr:3000/static/js/utils.js"; import Hub from "http://devtalk.kospo.co.kr:3000/static/js/module/hub.js"; // 톡방 기능 let reConnectAttempts = 0; let reConnectCnt = 0; let errCnt = 0; const talkEvent = { // 소켓 연결 connect() { const _this_ = this; /*현재 서버 정보 호출*/ getApInfo().then((data) => { global.currentServer = data.server }) return new Promise((resolve, reject) => { /*소켓연결*/ global.sockJS = new SockJS( `${global.apiUrl}/stomp/talk`, { Authentication: global.option.encSabun }, { transports: ['websocket'], reconnectInterval: 500, reconnectDelay: 500, } ); global.stomp = window.Stomp.over(global.sockJS); global.stomp.heartbeat.outgoing = 20000; global.stomp.heartbeat.incoming = 20000; global.stomp.debug = (e) => { if (e.includes('ERROR') || e.includes('Exception') || e.includes('failed')) { // console.log(new Date().toString()) // console.error('STOMP error:', e); } }; /*stomp 연결*/ global.stomp.connect({Authentication: global.option.encSabun, location: 'TALK'}, function (frame) { resolve(); }) global.stomp.onerror = (e) => { console.log('err', e) } global.stomp.onclose = (e) => { console.log('close', e) } const maxReconnectAttempts = 3; const baseDelay = 10; global.sockJS.onclose = (e) => { if (e.wasClean) { console.log('[Chat] WS Closed', new Date()) } else { console.log('[Chat] WS Connecting...', new Date()) } console.log(e) /* 구독 해제 (남아 있는데이터 삭제) */ talkEvent.allUnsubscribe() const delay = Math.min(baseDelay * Math.pow(100, reConnectAttempts), 2000) const reConnectTimeout = setTimeout(() => { if (reConnectAttempts < maxReconnectAttempts) { reConnectAttempts++; this.reConnect(); } else { console.log('[Chat] Max Reconnect attempts reached.') clearTimeout(reConnectTimeout) /*서비스워커 종료*/ HubEvent.kill(global.talkParams({ type: 'KILL', channelSabun: global.user.sabun })) global.forceKill = true; global.shadowRoot.querySelector('.server-error').classList.remove('cus-hide') } }, delay) } }) }, reConnect: async () => { console.log('reconnect') // 소켓 설정 초기화 global.subscribeList = []; /*현재 서버 정보 호출*/ let data = {}; try { data = await getApInfo(); } catch (e) { data['server'] = ''; } /*ap 다른곳으로 이동시 서비스워커 재설치*/ if (global.currentServer !== data.server) { HubEvent.close({ type: 'TAB_CLOSE', tabId: global.tabId, url: global.apiUrl, sabun: global.user.sabun, channelSabun: global.user.sabun, site: global.currentUrl }) global.hub.install().then(() => { // 허브설정 HubEvent.start(global.talkParams({ type: 'START', tabId: global.tabId })) }); } talkEvent.connect().then(async () => { global.hub.start(); // 톡방 재갱신 getTalkList(false).then(async () => { // 톡방 메시지 재갱신 // getMessageAll(false) getMoreMessageAll() reConnectAttempts = 0; console.log('global.init = true') }); // 현재 탭 외 모든 탭 구독 해지 HubEvent.send(global.talkParams({ type: 'UNSUBSCRIBE', tabId: global.tabId })) // 개인 사번 소켓 연결 try{global.subscribeList[global.user.sabun] = talkEvent.userSubscribe();}catch(e){} // 업무별 소켓 연결 try{global.subscribeList[global.work.workId] = talkEvent.workSubscribe();}catch(e){} global.init = true; }); global.currentServer = data.server; console.log('reConnectAttempts', reConnectAttempts) }, // 오류 보수 // (추후 사용자 장애시 해당 기능 추가) // 서비스워커 제거, 소켓 제거 후 재시작 repair: () => { talkEvent.allUnsubscribe(); window.location.href = window.location.href; }, // 개인 사번 구독 userSubscribe: () => { return global.stomp.subscribe(`/exchange/user.exchange/user.${global.user.sabun}`, async function (content) { const payload = JSON.parse(content.body); if(payload.type === 'DEAD_MESSAGE') { global.shadowRoot.querySelector('.socket-error').classList.remove('cus-hide') } else { console.log('userSubscribe', payload) let sendFlag = false; while (!sendFlag) { if (global.serviceWorkerConnect) { HubEvent.send(payload) sendFlag = true; } else { global.registration.active.postMessage({"type": "WAKEUP"}) } await Common.sleep(100) } global.serviceWorkerConnect = false; } }); }, // 업무 구독 workSubscribe: () => { return global.stomp.subscribe(`/exchange/work.exchange/work.${global.work.workId}`, async function (content) { const payload = JSON.parse(content.body); let sendFlag = false; while (!sendFlag) { if (global.serviceWorkerConnect) { HubEvent.work(payload) sendFlag = true; } else { global.registration.active.postMessage({"type": "WAKEUP"}) } await Common.sleep(100) } global.serviceWorkerConnect = false; }); }, // 파일 구독 fileSubscribe: (payload) => { global.fileSubscribeList[payload.talkId] = global.stomp.subscribe(`/exchange/file.exchange/file.${payload.talkId}`, async function (content) { const payload = JSON.parse(content.body); console.log('file subscribe', payload) let sendFlag = false; while (!sendFlag) { if (global.serviceWorkerConnect) { HubEvent.fileSend(payload) sendFlag = true; } else { global.registration.active.postMessage({"type": "WAKEUP"}) } await Common.sleep(100) } global.serviceWorkerConnect = false; }) }, //톡방 구독 subscribe: (talkId) => { return global.stomp.subscribe(`/exchange/talk.exchange/room.${talkId}`, async function (content) { const payload = JSON.parse(content.body); let sendFlag = false; while (!sendFlag) { if (global.serviceWorkerConnect) { HubEvent.send(payload) sendFlag = true; } else { global.registration.active.postMessage({"type": "WAKEUP"}) } await Common.sleep(100) } global.serviceWorkerConnect = false; }); }, // 구독 해지 unsubscribe: (talkId) => { try { global.subscribeList[talkId].unsubscribe() } catch (e) { } delete global.subscribeList[talkId]; // global.subscribeList[talkId] = null; }, // 전체 구독 해지 (구독과 관련된 모든 데이터 초기화) allUnsubscribe: () => { Object.keys(global.subscribeList).forEach((key) => { try { global.subscribeList[key].unsubscribe(); } catch (e) { } }) try { global.subscribeList = []; if(global.stomp && global.stomp.connected) { global.stomp.disconnect(function() { try {global.sockJS.close();} catch(e) {} console.log('stomp 연결 해지 완료', global.stomp , global.sockJS) global.stomp = null; global.sockJS = null; }) } else if(global.sockJS && global.sockJS.readyState === 1) { try {global.sockJS.close();} catch(e) {} console.log('sockjs 연결 해지 완료', global.stomp , global.sockJS) global.sockJS = null; } } catch (e) { console.log('allUnsubscribe error ', e) } }, // 톡 시작 start: (msgObj) => { global.stomp.send(`/pub/talk.start.${msgObj.talkId}`, {}, JSON.stringify(msgObj)) }, // 톡 메시지 전송 send: async (msgObj) => { if (Common.isMaster()) { global.stomp.send(`/pub/talk.message.${msgObj.talkId}`, {}, JSON.stringify(msgObj)) } else { msgObj.type = `SEND_${msgObj.type}` let sendFlag = false; while (!sendFlag) { if (global.serviceWorkerConnect) { HubEvent.send(msgObj) sendFlag = true; } else { global.registration.active.postMessage({"type": "WAKEUP"}) } await Common.sleep(100) } global.serviceWorkerConnect = false; } }, // 톡 종료 close: (msgObj) => { global.stomp.send(`/pub/talk.close.${msgObj.talkId}`, {}, JSON.stringify(msgObj)) }, // 톡 첩부파일 전송 attach: async (msgObj) => { if (Common.isMaster()) { global.stomp.send(`/pub/talk.attach.${msgObj.talkId}`, {}, JSON.stringify(msgObj)) } else { let sendFlag = false; while (!sendFlag) { if (global.serviceWorkerConnect) { HubEvent.fileSend(msgObj) sendFlag = true; } else { global.registration.active.postMessage({"type": "WAKEUP"}) } await Common.sleep(100) } global.serviceWorkerConnect = false; } }, // 톡 생성 join: async (msgObj) => { if (Common.isMaster()) { global.stomp.send(`/pub/talk.enter.${msgObj.talkId}`, {}, JSON.stringify(msgObj)) } else { let sendFlag = false; while (!sendFlag) { if (global.serviceWorkerConnect) { HubEvent.send(msgObj) sendFlag = true; } else { global.registration.active.postMessage({"type": "WAKEUP"}) } await Common.sleep(100) } global.serviceWorkerConnect = false; } }, // 톡 떠나기 leave: async (msgObj) => { if (Common.isMaster()) { global.stomp.send(`/pub/talk.leave.${msgObj.talkId}`, {}, JSON.stringify(msgObj)) } else { let sendFlag = false; while (!sendFlag) { if (global.serviceWorkerConnect) { HubEvent.send(msgObj) sendFlag = true; } else { global.registration.active.postMessage({"type": "WAKEUP"}) } await Common.sleep(100) } global.serviceWorkerConnect = false; } }, // 시스템 변경 change: (msgObj) => { /*유지보수 여러탭 구현 필요없음*/ global.stomp.send(`/pub/talk.change.${msgObj.talkId}`, {}, JSON.stringify(msgObj)) }, // 톡 아이디 생성 generateUUID: () => { let d = new Date(); if (typeof performance !== 'undefined' && typeof performance.now === 'function') { d + performance.now(); } const uuid = 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxxxxxxyxxxxxxxxxy'.replace(/[xy]/g, function (c) { const r = (d + Math.random() * 16) % 16 | 0; d = Math.floor(d / 16); return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); }) return uuid; }, // 탭 아이디 생성 generateTabUUID: () => { let d = new Date(); if (typeof performance !== 'undefined' && typeof performance.now === 'function') { d + performance.now(); } const uuid = 'xxxxxxxxxxxx4xxxyxxxxxxxxxx'.replace(/[xy]/g, function (c) { const r = (d + Math.random() * 16) % 16 | 0; d = Math.floor(d / 16); return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); }) return uuid; }, /* 톡방 안에 담당자 및 업무명 변경*/ talkHeaderChange: (workNm, name) => { const chatHeader = global.shadowRoot.querySelector('.chat-content-header'); if (workNm) { chatHeader.querySelector('#work-nm').innerText = workNm } if (name) { const names = name.split(',') if (names.length === 1) { chatHeader.querySelector('#work-sm-nm').classList.remove('cus-tooltip') chatHeader.querySelector('#work-sm-nm').classList.remove('chat-inner-tooltip') chatHeader.querySelector('#work-sm-nm').setAttribute("data-tooltip", '') chatHeader.querySelector('#work-sm-nm').innerText = name; } else { chatHeader.querySelector('#work-sm-nm').classList.add('cus-tooltip') chatHeader.querySelector('#work-sm-nm').classList.add('chat-inner-tooltip') chatHeader.querySelector('#work-sm-nm').setAttribute("data-tooltip", name) chatHeader.querySelector('#work-sm-nm').innerText = `${names[0]}외 ${names.length - 1}명`; } } }, // 톡 내용 활성화 messageShow: () => { talkEvent.messageClean(); global.shadowRoot.querySelector('#chat-tab').click() global.shadowRoot.querySelector('#chat-0').classList.add('active') global.element.chatSidebar.style.left = '-100%' }, // 톡방 삭제 talkRemove: (talkId) => { //톡방 삭제후 배지 카운트 재정의 global.talkData.forEach((talk, idx) => { if (talk.talkId === talkId) { console.log('talkRemove :', idx, talk) // delete global.talkData[talk]; global.talkData.splice(idx, 1) } }) console.log(global.talkData) talkMapping(global.talkData) talkEvent.badgeChange() }, // 톡방 보관 talkMark: (talkId) => { let isMarkYn = false; global.talkData.forEach((chat, idx) => { if (chat.talkId === talkId) { chat.markYn = !chat.markYn; isMarkYn = chat.markYn; if (chat.markYn) { global.talkData.splice(idx, 1) global.talkData = [chat, ...global.talkData] } } }) if (!isMarkYn) { global.talkData.sort((a, b) => { if (!b.markYn) { if (a.insDate > b.insDate) { return -1 } else if (a.insDate < b.insDate) { return 1 } } }) } talkMapping(global.talkData) }, // 톡방 정렬 talkSort: () => { global.talkData.sort((a, b) => { if (a.markYn) { return -1; } else { if (a.lastMessageDate >= b.lastMessageDate) { return -1; } else if (a.lastMessageDate < b.lastMessageDate) { return 1; } } }) }, // 톡방 현재 스크롤 위치 messageScrollOriginalPositionMove: () => { const contentBody = global.shadowRoot.querySelector('.chat-content-body'); const _contentScroll = contentBody.querySelector('.simplebar-content-wrapper') _contentScroll.scrollTop = _contentScroll.scrollHeight - global.oldScrollPosition }, // 스크롤 위치 가 변경 되었을시 메시지 미리보기 preMessageShow: (text) => { const preMsg = global.shadowRoot.querySelector('.message-preview'); const msg = preMsg.querySelector('.msg'); msg.innerText = text; preMsg.classList.remove('cus-hide'); }, // 메시지 삭제 messageClean: () => { // 초기화 해야할 데이터 global.currDay = '0'; global.yourOldDate = ''; global.mineOldDate = ''; const chatContent = global.shadowRoot.querySelector('#chat-content'); const content = chatContent.querySelector('.simplebar-content'); m.render(content, []); content.innerHTML = ''; global.element.chatEditor.innerHTML = ''; }, // 메인 뱃지 카운트 초기화 badgeChange: () => { const badgeUnCount = global.talkData.reduce((a, b) => { return Number(a) + Number(b.unReadCnt) }, 0); const chatBadge = global.shadowRoot.querySelector('.unread.badge'); if (badgeUnCount !== parseInt(0)) { chatBadge.classList.remove('cus-hide'); } else { chatBadge.classList.add('cus-hide'); } chatBadge.innerText = badgeUnCount; }, /*채팅룸 읾음 표시로 변경*/ readMessage: (talkId) => { console.log('readmessage', talkId) global.shadowRoot.querySelector(`div[data-talk-id="${talkId}"]`).classList.remove('unread-message') const room = global.shadowRoot.querySelector(`div[data-talk-id="${talkId}"]`); const badge = room.querySelector('.chat-list-badge') const cacheTalk = global.talkData.filter((talk) => talk.talkId === talkId)[0]; try { cacheTalk.unReadCnt = 0; talkEvent.badgeChange(); badge.remove() } catch (e) {/*badge 텍스트가 없을시 예외 처리*/ } }, /*채팅방 타이핑에 따른 progress 호출*/ messageTyped: (msg) => { let html = '' html = `