Files
api/sample/dev/talk/js/module/talkEvent.js
2025-07-02 21:55:07 +09:00

571 lines
21 KiB
JavaScript

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);
console.log('workSubscribe', payload)
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 = `
<div class="d-flex p-1 your-dot-progress cus-show">
<div class="avatar avatar-l me-2">
<!--<img class="rounded-circle" src="../assets/img/team/5.jpg" alt="">-->
<div class="rounded-circle chat-avatar">
${msg.insName}
</div>
</div>
<div class="flex-1">
<div class="w-100">
<div class="stage your">
<div class="dot-typing"/>
</div>
</div>
</div>
</div>
`
const chatContent = global.shadowRoot.querySelector('#chat-content');
const content = chatContent.querySelector('.simplebar-content');
if(!content.querySelector('.your-dot-progress')) {
content.insertAdjacentHTML('beforeend', html)
}
},
/*채팅방 타이핑을 하지 않을시 progress 제거*/
messageUnTyped: (msg) => {
const chatContent = global.shadowRoot.querySelector('#chat-content');
const content = chatContent.querySelector('.simplebar-content');
const dotProgress = content.querySelector('.your-dot-progress')
if (dotProgress !== null) dotProgress.remove();
},
/*채팅리스트 메인 글 변경 */
talkTitleChange: (talkId, sabun) => {
const cacheTalk = global.talkData.filter((talk) => talk.talkId === talkId)[0];
cacheTalk.talkMembers = cacheTalk.talkMembers.filter((member) => (member.sabun === 'info' || member.sabun === global.user.sabun || member.sabun === sabun))
talkMapping(global.talkData)
}
}
export {talkEvent}