mirror of
https://git.hmsn.ink/kospo/helptalk/api.git
synced 2026-03-20 02:52:31 +09:00
first
This commit is contained in:
BIN
sample/dev/talk/js/StressTest.png
Normal file
BIN
sample/dev/talk/js/StressTest.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
147
sample/dev/talk/js/capture.js
Normal file
147
sample/dev/talk/js/capture.js
Normal file
@@ -0,0 +1,147 @@
|
||||
const captureBtn = document.querySelector('#capture');
|
||||
const captureVideo = document.querySelector('#capture-video');
|
||||
const captureContainer = document.querySelector('#capture-video-container');
|
||||
const captureSelection = document.querySelector('#capture-selection')
|
||||
|
||||
async function captureStart() {
|
||||
try {
|
||||
captureContainer.style.display = 'inline-block';
|
||||
const displayMediaOptions = {
|
||||
audio:false,
|
||||
video:true,
|
||||
cursor: 'always',
|
||||
selfBrowserSurface: "exclude",
|
||||
surfaceSwitching: "include",
|
||||
monitorTypeSurfaces: "include",
|
||||
};
|
||||
navigator.mediaDevices.getDisplayMedia(displayMediaOptions).then((captureStream) => {
|
||||
fullscreen()
|
||||
captureVideo.srcObject = captureStream;
|
||||
selectionMount();
|
||||
|
||||
});
|
||||
|
||||
|
||||
} catch(err) {
|
||||
captureStop();
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function isFullScreen() {
|
||||
return !!document.fullscreenElement;
|
||||
}
|
||||
|
||||
function fullscreen() {
|
||||
if(isFullScreen()) {
|
||||
if(document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
} else {
|
||||
if(document.documentElement.requestFullscreen) {
|
||||
document.documentElement.requestFullscreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function captureStop() {
|
||||
if (captureVideo.srcObject) {
|
||||
const tracks = captureVideo.srcObject.getTracks();
|
||||
tracks.forEach((t) => t.stop());
|
||||
captureVideo.srcObject = null;
|
||||
}
|
||||
captureContainer.style.display = 'none';
|
||||
|
||||
fullscreen()
|
||||
selectionUnmount();
|
||||
}
|
||||
|
||||
async function takeSnapshot() {
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext("2d");
|
||||
|
||||
const rect = captureSelection.getBoundingClientRect();
|
||||
const videoRect = captureVideo.getBoundingClientRect();
|
||||
|
||||
const scaleX = captureVideo.videoWidth / videoRect.width;
|
||||
const scaleY = captureVideo.videoHeight / videoRect.height;
|
||||
|
||||
canvas.width = rect.width * scaleX
|
||||
canvas.height = rect.height * scaleY
|
||||
context.drawImage(
|
||||
captureVideo,
|
||||
(rect.left - videoRect.left) * scaleX,
|
||||
(rect.top - videoRect.top) * scaleY,
|
||||
rect.width * scaleX,
|
||||
rect.height * scaleY,
|
||||
0,
|
||||
0,
|
||||
canvas.width,
|
||||
canvas.height
|
||||
)
|
||||
const snapshot = canvas.toDataURL("image/png")
|
||||
console.log(snapshot)
|
||||
}
|
||||
|
||||
|
||||
captureVideo.addEventListener('playing', function() {
|
||||
captureVideo.pause()
|
||||
})
|
||||
|
||||
function selectionMount() {
|
||||
let isSelection = false;
|
||||
let endX = 0;
|
||||
let endY = 0;
|
||||
let startX = 0;
|
||||
let startY = 0;
|
||||
|
||||
|
||||
const mouseDownListener = (e) => {
|
||||
startX = e.offsetX;
|
||||
startY = e.offsetY;
|
||||
isSelection = true;
|
||||
captureSelection.style.left = `${startX}px`;
|
||||
captureSelection.style.top = `${startY}px`;
|
||||
captureSelection.style.width = '0px';
|
||||
captureSelection.style.height = '0px';
|
||||
captureSelection.style.display = 'block';
|
||||
};
|
||||
|
||||
const mouseMoveListener = (e) => {
|
||||
if (isSelection) {
|
||||
endX = e.offsetX;
|
||||
endY = e.offsetY;
|
||||
|
||||
captureSelection.style.left = `${Math.min(startX, endX)}px`;
|
||||
captureSelection.style.top = `${Math.min(startY, endY)}px`;
|
||||
captureSelection.style.width = `${Math.abs(endX - startX)}px`;
|
||||
captureSelection.style.height = `${Math.abs(endY - startY)}px`;
|
||||
}
|
||||
};
|
||||
|
||||
const mouseUpListener = (e) => {
|
||||
isSelection = false;
|
||||
takeSnapshot();
|
||||
captureStop();
|
||||
};
|
||||
|
||||
captureVideo.removeEventListener('mousedown', mouseDownListener);
|
||||
captureVideo.removeEventListener('mousemove', mouseMoveListener);
|
||||
captureVideo.removeEventListener('mouseup', mouseUpListener);
|
||||
|
||||
captureVideo.addEventListener('mousedown', mouseDownListener);
|
||||
captureVideo.addEventListener('mousemove', mouseMoveListener);
|
||||
captureVideo.addEventListener('mouseup', mouseUpListener);
|
||||
}
|
||||
|
||||
function selectionUnmount() {
|
||||
captureSelection.style.left = `0px`;
|
||||
captureSelection.style.top = `0px`;
|
||||
captureSelection.style.width = '0px';
|
||||
captureSelection.style.height = '0px';
|
||||
captureSelection.style.display = 'none';
|
||||
}
|
||||
|
||||
captureBtn.addEventListener('click', captureStart)
|
||||
133
sample/dev/talk/js/k6.js
Normal file
133
sample/dev/talk/js/k6.js
Normal file
@@ -0,0 +1,133 @@
|
||||
import ws from 'k6/ws';
|
||||
import { check, sleep } from 'k6';
|
||||
import stomp from 'k6/x/stomp';
|
||||
import {SharedArray} from 'k6/data';
|
||||
|
||||
const users = new SharedArray('users', function() {
|
||||
return [{"sabun":"17131427","name":"권태현","talkId":"0a9cd322767b40069dbd7f5fe5b69401","workid":"HA0001","worknm":"SD요원 처리"}]
|
||||
})
|
||||
|
||||
|
||||
const currentDate = (idx) => {
|
||||
const date = new Date();
|
||||
|
||||
// 날짜를 원하는 형식으로 포맷팅
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0'); // 월은 0부터 시작하므로 +1
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
const milliseconds = Number(String(date.getMilliseconds()).padEnd(6, '0')) + idx;
|
||||
|
||||
const formattedDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
|
||||
return formattedDate;
|
||||
}
|
||||
|
||||
|
||||
export const options = {
|
||||
// vus: 2033,
|
||||
// Iterations: 2033,
|
||||
stages: [
|
||||
{ duration: '30s', target: 200 }, // 1 500
|
||||
{ duration: '1m', target: 400 }, // 2 1000
|
||||
{ duration: '1m', target: 600 }, // 2 1000
|
||||
{ duration: '2m', target: 1000 }, // 2 1000
|
||||
{ duration: '3m', target: 2033 }, // 2 1000
|
||||
{ duration: '1m', target: 1000 }, // 2 1000
|
||||
{ duration: '1m', target: 500 }, // 2 1000
|
||||
{ duration: '30s', target: 0 }, // 1 0
|
||||
]
|
||||
};
|
||||
|
||||
const WEBSOCKET_URL = "http://devtalk.kospo.co.kr:3000/stomp/talk"
|
||||
const ROOM_DESTINATION = "/exchange/talk.exchange/room."
|
||||
const MESSAGE_DESTINATION = "/queue/talk.queue"
|
||||
|
||||
export default function () {
|
||||
|
||||
const userIndex = __VU % users.length;
|
||||
const user = users[userIndex];
|
||||
|
||||
|
||||
// connect to broker
|
||||
// const stompClient = stomp.connect({
|
||||
// protocol: 'tcp',
|
||||
// addr: 'localhost:61613',
|
||||
// timeout: '15s',
|
||||
// tls: false,
|
||||
// user: 'talk',
|
||||
// pass: 'kospo2024!',
|
||||
// heartbeat: {
|
||||
// incoming: '20s',
|
||||
// outgoing: '20s',
|
||||
// },
|
||||
// host: '/',
|
||||
// verbose: true,
|
||||
// read_buffer_size: 4096,
|
||||
// read_channel_capacity: 20,
|
||||
// write_buffer_size: 4096,
|
||||
// write_channel_capacity: 20,
|
||||
// });
|
||||
|
||||
const stompClient = stomp.connect({
|
||||
protocol: 'tcp',
|
||||
addr: 'http://devtalk.kospo.co.kr:3000/stomp/talk',
|
||||
timeout: '15s',
|
||||
tls: false,
|
||||
heartbeat: {
|
||||
incoming: '20s',
|
||||
outgoing: '20s',
|
||||
},
|
||||
host: '/',
|
||||
verbose: true,
|
||||
read_buffer_size: 4096,
|
||||
read_channel_capacity: 20,
|
||||
write_buffer_size: 4096,
|
||||
write_channel_capacity: 20,
|
||||
})
|
||||
|
||||
try {
|
||||
const subscribeOpts = {
|
||||
ack: 'client' // client-individual or auto (default)
|
||||
}
|
||||
|
||||
|
||||
const roomSub = stompClient.subscribe(`${ROOM_DESTINATION}${user.talkId}`, subscribeOpts);
|
||||
|
||||
const insDate = currentDate(__VU).padEnd(26, '0');
|
||||
const messageParam = {
|
||||
type: "MESSAGE",
|
||||
insSabun: user.sabun,
|
||||
insName : user.name,
|
||||
insDate : insDate,
|
||||
message: '',
|
||||
talkId: user.room,
|
||||
workId: user.workId,
|
||||
workNm: user.workNm,
|
||||
introOwner: '',
|
||||
talkAttachDtos: [],
|
||||
attach: null,
|
||||
}
|
||||
|
||||
|
||||
messageParam.message = `부하 테스트 ${user.name} ${user.room}`
|
||||
stompClient.send(`${MESSAGE_DESTINATION}`, 'application/json', JSON.stringify(messageParam))
|
||||
|
||||
|
||||
// setTimeout(stompClient.unsubscribe, 240000)
|
||||
sleep(10)
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
export function setup() {
|
||||
console.log("text setup completed with users : ", users.length)
|
||||
return {userCount: users.length}
|
||||
}
|
||||
|
||||
export function teardown(data) {
|
||||
// disconnect from broker
|
||||
console.log('test completed total users tested : ', data.userCount)
|
||||
}
|
||||
762
sample/dev/talk/js/module/apis.js
Normal file
762
sample/dev/talk/js/module/apis.js
Normal file
@@ -0,0 +1,762 @@
|
||||
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 {talkEvent} from "http://devtalk.kospo.co.kr:3000/static/js/module/talkEvent.js";
|
||||
import {talkMapping, messageMapping} from "http://devtalk.kospo.co.kr:3000/static/js/module/render.js"
|
||||
import {Tab} from "http://devtalk.kospo.co.kr:3000/static/bundle/bootstrap-5.1.3/js/bootstrap.esm.js";
|
||||
import Common from "http://devtalk.kospo.co.kr:3000/static/js/utils.js";
|
||||
import {HubEvent} from "http://devtalk.kospo.co.kr:3000/static/js/module/hubEvent.js";
|
||||
import {showNotification} from "http://devtalk.kospo.co.kr:3000/static/js/notification.js";
|
||||
import receiver from "http://devtalk.kospo.co.kr:3000/static/js/module/receiver.js";
|
||||
|
||||
// api 관리
|
||||
let isQuestion = true;
|
||||
|
||||
const talkInit = () => {
|
||||
work().then(() => {
|
||||
manual().then(() => {
|
||||
question()
|
||||
})
|
||||
docManual()
|
||||
videoManual()
|
||||
})
|
||||
|
||||
}
|
||||
// 현재 사용자 정보
|
||||
const getUser = async () => {
|
||||
global.user = await global.request({
|
||||
url: `/api/user/info/client`,
|
||||
method: 'get',
|
||||
withCredentials: true
|
||||
})
|
||||
}
|
||||
|
||||
// itsm 만족도 조사 저장
|
||||
const setITSMSatis = (talkId, reqId, rating, content) => {
|
||||
global.request({
|
||||
url: `/api/ict/satis`,
|
||||
method: 'post',
|
||||
body: {talk_id : talkId, req_id : reqId, h_point: rating, opinion: content},
|
||||
withCredentials: true
|
||||
})
|
||||
}
|
||||
// scm 만족도 조사 저장
|
||||
const setSCMSatis = (talkId, reqId, rating, content) => {
|
||||
global.request({
|
||||
url: `/api/scm/satis`,
|
||||
method: 'post',
|
||||
body: {talk_id : talkId, req_id : reqId, h_point: rating, opinion: content},
|
||||
withCredentials: true
|
||||
})
|
||||
}
|
||||
|
||||
// 접속 중인 시스템 정보
|
||||
const work = async () => {
|
||||
global.work = await global.request({
|
||||
url: `/api/work`,
|
||||
method: 'get',
|
||||
params: {workId: global.currentWorkId},
|
||||
withCredentials: true
|
||||
})
|
||||
|
||||
global.shadowRoot.querySelector('#intro-work').innerText = global.work.workNm;
|
||||
global.shadowRoot.querySelector('#intro-online').classList.add(global.work.onlineYn ? 'online' : 'offline');
|
||||
global.shadowRoot.querySelector('#intro-user').innerText = global.user.name;
|
||||
|
||||
receiver.changeLoginInfo(global.work)
|
||||
}
|
||||
|
||||
// 접속 중인 시스템의 매뉴얼
|
||||
const manual = () => {
|
||||
// 현재 업무의 메뉴얼 & 비디오 메뉴얼 호출
|
||||
return new Promise((resolve, reject) => {
|
||||
global.request({
|
||||
method: 'get',
|
||||
url: `/api/manual/intro`,
|
||||
params: {workId: global.work.workId},
|
||||
withCredentials: true
|
||||
}).then((images) => {
|
||||
const manualSlide = global.shadowRoot.querySelector('#manual-play');
|
||||
const vNode = []
|
||||
if(images.length === 0) {
|
||||
const parent = Common.findParentElement(manualSlide, 'mt-4')
|
||||
parent.remove()
|
||||
isQuestion = false;
|
||||
} else {
|
||||
images.forEach((i, idx) => {
|
||||
if (i.onm.includes("mp4")) {
|
||||
vNode.push(
|
||||
m('div', {class: `carousel-item ${idx === 0 ? 'active' : ''}`}, [
|
||||
m('video', {
|
||||
controls: false,
|
||||
class: 'd-block w-100 intro-video',
|
||||
muted: true,
|
||||
preload: 'auto',
|
||||
autoplay: false
|
||||
}, [
|
||||
m('source', {
|
||||
src: `${global.apiUrl}/api/manual/view/video?id=${i.id}&type=S`,
|
||||
type: "video/mp4"
|
||||
})
|
||||
]),
|
||||
m('div', {class: 'video-control'}, [
|
||||
m('div', {class:'video-fullscreen cus-hide', 'data-ori-src': `${global.apiUrl}/api/manual/view/video?id=${i.id}&type=O`}, [
|
||||
m('i', {class: 'bi-fullscreen bi-color-white'})
|
||||
])
|
||||
])
|
||||
])
|
||||
)
|
||||
} else {
|
||||
vNode.push(
|
||||
m('div', {class: `carousel-item ${idx === 0 ? 'active' : ''}`}, [
|
||||
m('img', {
|
||||
src: `${global.apiUrl}/api/manual/view/image?workId=${global.work.workId}&vnm=${i.vnm}&sabun=${global.user.sabun}`,
|
||||
class: 'd-block w-100',
|
||||
alt: 'img1'
|
||||
})
|
||||
])
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
m.render(manualSlide.querySelector('.carousel-inner'), vNode);
|
||||
/* 숏츠 비디오 이벤트 정의 */
|
||||
global.shadowRoot.querySelectorAll('.video-control').forEach((target) => {
|
||||
target.addEventListener('mouseenter', () => {
|
||||
target.querySelector('.video-fullscreen').classList.remove('cus-hide')
|
||||
target.querySelector('.video-fullscreen').classList.add('cus-show')
|
||||
})
|
||||
|
||||
target.addEventListener('mouseleave', () => {
|
||||
target.querySelector('.video-fullscreen').classList.remove('cus-show')
|
||||
target.querySelector('.video-fullscreen').classList.add('cus-hide')
|
||||
})
|
||||
})
|
||||
|
||||
global.shadowRoot.querySelectorAll('.video-fullscreen').forEach((target) => {
|
||||
target.addEventListener('click', () => {
|
||||
const oriSrc = target.dataset.oriSrc
|
||||
console.log(oriSrc)
|
||||
const fullvideo = global.shadowRoot.querySelector('#fullscreen-container');
|
||||
fullvideo.classList.remove('cus-hide')
|
||||
fullvideo.classList.add('cus-show');
|
||||
const video = m('video', {preload: 'none', controls: 'controls', id: 'fullvideo', style: 'width:100%; height:100%;'}, [
|
||||
m('source', {src: oriSrc, type: 'video/mp4'})
|
||||
])
|
||||
m.render(fullvideo, video);
|
||||
const playVideo = global.shadowRoot.querySelector('#fullvideo');
|
||||
playVideo.requestFullscreen();
|
||||
playVideo.play();
|
||||
|
||||
window.onresize = () => {
|
||||
if(window.matchMedia('(display-mode: fullscreen)').matches || window.document.fullscreenElement) {
|
||||
console.log(true)
|
||||
} else {
|
||||
closeFullscreen();
|
||||
console.log(false);
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const closeFullscreen = () => {
|
||||
const playVideo = global.shadowRoot.querySelector('#fullvideo');
|
||||
if(playVideo) {
|
||||
const fullvideo = global.shadowRoot.querySelector('#fullscreen-container');
|
||||
m.render(fullvideo, []);
|
||||
fullvideo.classList.remove('cus-show')
|
||||
fullvideo.classList.add('cus-hide')
|
||||
}
|
||||
}
|
||||
|
||||
// 접속 중인 시스템의 자주 하는 질문
|
||||
const question = () => {
|
||||
const questionCnt = isQuestion ? 5 : 6;
|
||||
/*현재 업무의 자주하는 질문 설정 */
|
||||
global.request({
|
||||
method: 'get',
|
||||
url: `/api/question/${global.work.workId}`,
|
||||
withCredentials: true
|
||||
}).then((question) => {
|
||||
const questionContent = global.shadowRoot.querySelector('#question');
|
||||
const question_page = Math.ceil(question.length / questionCnt)
|
||||
|
||||
let question_div = null
|
||||
let paging = 0;
|
||||
question.forEach((a, idx) => {
|
||||
if(idx % questionCnt === 0) {
|
||||
paging++;
|
||||
question_div = document.createElement('div')
|
||||
question_div.setAttribute("id", `question-pg-${paging}`)
|
||||
question_div.classList.add('question-pg-container')
|
||||
if(paging !== 1) {
|
||||
question_div.classList.add('cus-hide')
|
||||
} else {
|
||||
question_div.classList.add('cus-show')
|
||||
}
|
||||
}
|
||||
|
||||
const questionP = document.createElement("p");
|
||||
questionP.classList.add('cursor-pointer', 'question')
|
||||
const questionText = document.createTextNode(`${a.content}`)
|
||||
questionP.appendChild(questionText)
|
||||
question_div.append(questionP);
|
||||
questionP.addEventListener('click', () => {
|
||||
const talkId = talkEvent.generateUUID();
|
||||
// 키보드 숨김 해제
|
||||
global.element.chatEditorArea.classList.remove('cus-hide');
|
||||
// 채팅 구독 메시지 보내기
|
||||
localStorage.setItem(global.tabId, talkId)
|
||||
// 발 생성자 주입
|
||||
global.chatCreator = global.tabId;
|
||||
talkEvent.join(global.talkParams({
|
||||
type: 'JOIN',
|
||||
message: a.content,
|
||||
talkId: talkId,
|
||||
workId: global.work.workId,
|
||||
introOwner: 'mine'
|
||||
}))
|
||||
|
||||
talkEvent.messageShow()
|
||||
})
|
||||
if(idx % questionCnt === questionCnt-1 || idx === question.length-1) {
|
||||
questionContent.append(question_div)
|
||||
}
|
||||
})
|
||||
const pagination = document.createElement('div');
|
||||
pagination.classList.add('fx', 'fx-gap')
|
||||
pagination.setAttribute("id", 'question_pg')
|
||||
for(var i=0; i< question_page; i++) {
|
||||
const pagination_btn = document.createElement('div')
|
||||
if(i === 0) {
|
||||
pagination_btn.classList.add('btn', 'active')
|
||||
pagination_btn.dataset.pg = i
|
||||
} else {
|
||||
pagination_btn.classList.add('btn')
|
||||
pagination_btn.dataset.pg = i
|
||||
}
|
||||
pagination.append(pagination_btn)
|
||||
}
|
||||
questionContent.append(pagination)
|
||||
|
||||
let current_page = 0
|
||||
|
||||
function switchToNext(evt) {
|
||||
const {target} =evt;
|
||||
if(!target.classList.contains('btn')) {
|
||||
return;
|
||||
}
|
||||
const clickedBtn = target;
|
||||
if(clickedBtn.classList.contains('active')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeBtn = pagination.getElementsByClassName('active')[0]
|
||||
activeBtn?.classList.remove("active")
|
||||
clickedBtn.classList.add('active')
|
||||
global.shadowRoot.querySelectorAll(".question-pg-container").forEach((el) => {
|
||||
el.classList.add('cus-hide')
|
||||
el.classList.remove('cus-show')
|
||||
});
|
||||
const active = global.shadowRoot.querySelector(`#question-pg-${Number(clickedBtn.dataset.pg) + 1}`)
|
||||
active.classList.remove('cus-hide')
|
||||
active.classList.add('cus-show')
|
||||
current_page = Number(clickedBtn.dataset.pg) + 1;
|
||||
}
|
||||
|
||||
pagination.addEventListener('click', switchToNext)
|
||||
|
||||
/*자주찾는 질문이 2페이지 이상일때만 실행*/
|
||||
if(question_page > 1) {
|
||||
setInterval(autoNext, 10000)
|
||||
function autoNext() {
|
||||
let movePage = Number(current_page) + 1
|
||||
if(question_page < movePage) movePage = 1;
|
||||
global.shadowRoot.querySelectorAll(".question-pg-container").forEach((el) => {
|
||||
el.classList.add('cus-hide')
|
||||
el.classList.remove('cus-show')
|
||||
});
|
||||
const active = global.shadowRoot.querySelector(`#question-pg-${movePage}`);
|
||||
active.classList.remove('cus-hide')
|
||||
active.classList.add('cus-show')
|
||||
current_page = movePage;
|
||||
}
|
||||
}
|
||||
|
||||
if(!isQuestion) {
|
||||
const question = global.shadowRoot.querySelector('#question');
|
||||
const questionPg = global.shadowRoot.querySelector('.question-pg-container');
|
||||
question.style.minHeight = '286px';
|
||||
questionPg.style.minHeight = '230px';
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 접속 중인 시스템의 문서 매뉴얼
|
||||
|
||||
const docManual = () => {
|
||||
/*본인 메뉴얼*/
|
||||
global.request({
|
||||
method: 'get',
|
||||
url: `/api/manual`,
|
||||
params: {workId: global.work.workId, type: '0000'},
|
||||
withCredentials: true
|
||||
}).then((manuals) => {
|
||||
const manualVNode = [];
|
||||
|
||||
manuals.map((ma) => {
|
||||
manualVNode.push(
|
||||
m('div', {class: "d-flex justify-content-start manual-content"}, [
|
||||
m('div', {class: "p-2 text-ellipsis", style: "width:60%;cursor: help;", title:ma.onm}, `${ma.onm}`),
|
||||
m('div', {class: "p-2", style: "width:20%;"}, `${ma.version}`),
|
||||
m('a', {
|
||||
href: `${global.apiUrl}/api/manual/download/doc?workId=${ma.workId}&vnm=${ma.vnm}`,
|
||||
target: '_download_',
|
||||
rel: 'noopener',
|
||||
style: 'padding-top:6px;'
|
||||
}, [
|
||||
m('i', {
|
||||
class: "bi bi-download text-black",
|
||||
style: "padding-right:10px; font-size:1.0rem; cursor:pointer;"
|
||||
})
|
||||
])
|
||||
])
|
||||
)
|
||||
})
|
||||
const manualBody = global.shadowRoot.querySelector('#manual-doc-body');
|
||||
const content = manualBody.querySelector('.simplebar-content');
|
||||
if(manualVNode.length > 0) m.render(content, manualVNode)
|
||||
})
|
||||
}
|
||||
|
||||
// 접속 중인 시스템의 동영상 매뉴얼
|
||||
const videoManual = () => {
|
||||
global.request({
|
||||
method: 'get',
|
||||
url: `/api/manual/video`,
|
||||
params: {workId: global.work.workId},
|
||||
withCredentials: true
|
||||
}).then((manuals) => {
|
||||
const manualVNode = [];
|
||||
manuals.map((ma) => {
|
||||
manualVNode.push(
|
||||
m('div', {class: "d-flex justify-content-evenly manual-content"}, [
|
||||
m('div', {class: "p-2 text-ellipsis", style: "width:70%;"}, `${ma.onm}`),
|
||||
// m('div', {class: "p-2", style: "font-size:13px; width:20%;"}, `${ma.version}`),
|
||||
m('button', {
|
||||
class: 'btn btn-sm btn-play manual-play-btn p-0',
|
||||
'data-id': ma.id,
|
||||
}, '재생')
|
||||
])
|
||||
)
|
||||
})
|
||||
const manualBody = global.shadowRoot.querySelector('#manual-video-body');
|
||||
const content = manualBody.querySelector('.simplebar-content');
|
||||
if(manualVNode.length > 0) m.render(content, manualVNode)
|
||||
// videoManualPlayEvent();
|
||||
})
|
||||
|
||||
console.log('work init end')
|
||||
}
|
||||
|
||||
// 생성된 톡방 목록 추가
|
||||
const setChatList = function (chat) {
|
||||
console.log('setChatList before', global.talkData, chat)
|
||||
global.talkData.unshift(chat)
|
||||
console.log('setChatList after', global.talkData)
|
||||
// 채팅 목록 생성
|
||||
talkMapping(global.talkData);
|
||||
// 채팅 목록 구독
|
||||
if (Common.isMaster()) {
|
||||
chatConnect([chat]);
|
||||
}
|
||||
// 채팅 부트스트랩 이벤트 수동 연결
|
||||
chatBootstrapEvent();
|
||||
|
||||
|
||||
talkEvent.badgeChange()
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 현사용자의 톡방 목록
|
||||
const getTalkList = function (reset) {
|
||||
return new Promise((resolve, reject) => {
|
||||
global.request({
|
||||
url: `/api/talk`,
|
||||
method: 'get',
|
||||
withCredentials: true
|
||||
}).then((lists) => {
|
||||
if (reset) {
|
||||
global.talkData = lists;
|
||||
getTalkListEvent()
|
||||
resolve(global.talkData.length === 0 ? true : false)
|
||||
} else {
|
||||
lists.forEach(list => {
|
||||
if (!global.talkData.map(talk => talk.talkId).join(',').includes(list.talkId)) {
|
||||
global.talkData.push(list)
|
||||
}
|
||||
})
|
||||
getTalkListEvent()
|
||||
resolve(global.talkData.length === 0 ? true : false)
|
||||
}
|
||||
}).catch(function (error) {
|
||||
console.log(error)
|
||||
reject()
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// 톡방 이벤트 정의 소켓 연결 및 동적으로 생성된 DOM 이벤트 재설정
|
||||
const getTalkListEvent = () => {
|
||||
// 채팅 목록 생성
|
||||
talkMapping(global.talkData);
|
||||
// 마스터만 채팅 목록 구독
|
||||
if (Common.isMaster()) {
|
||||
chatConnect(global.talkData);
|
||||
}
|
||||
// 채팅 부트스트랩 이벤트 수동 연결
|
||||
chatBootstrapEvent();
|
||||
|
||||
talkEvent.badgeChange()
|
||||
}
|
||||
|
||||
const getApInfo = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
global.request({
|
||||
url: `/api/getApInfo`,
|
||||
method: 'get',
|
||||
withCredentials: true
|
||||
}).then(data => {
|
||||
console.log(data)
|
||||
resolve(data);
|
||||
}).catch(() => {
|
||||
reject();
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// 소켓 구독
|
||||
const chatConnect = function (chats) {
|
||||
for (const ch of chats) {
|
||||
if (!ch.closeYn) {
|
||||
if (!Object.keys(global.subscribeList).includes(ch.talkId)) {
|
||||
global.subscribeList[ch.talkId] = talkEvent.subscribe(ch.talkId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 톡방 이벤트 정의
|
||||
const chatBootstrapEvent = () => {
|
||||
/*bootstrap 수동 이벤트 설정*/
|
||||
|
||||
global.shadowRoot.querySelectorAll('div[data-bs-toggle="tab"]').forEach((el) => {
|
||||
if (!el.dataset.tabListener) {
|
||||
const tabTrigger = new Tab(el);
|
||||
el.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
tabTrigger.show();
|
||||
})
|
||||
}
|
||||
|
||||
el.dataset.tabListener = true;
|
||||
})
|
||||
}
|
||||
|
||||
// 톡방 클릭시 해당 메시지
|
||||
const getMessage = function (messageParams) {
|
||||
global.request({
|
||||
url: `/api/message/line`,
|
||||
method: 'get',
|
||||
params: messageParams,
|
||||
withCredentials: true
|
||||
}).then((dto) => {
|
||||
const cacheTalk = global.talkData.filter((talk) => talk.talkId === messageParams.talkId)[0];
|
||||
cacheTalk.messageParams = messageParams;
|
||||
cacheTalk.talkMessages = dto;
|
||||
});
|
||||
}
|
||||
/*
|
||||
* 비동기 톡방 메시지 호출
|
||||
* parameter
|
||||
* reset : 초기 설정 일시 true 재연결시 false
|
||||
* */
|
||||
const getMessageAll = async function (reset) {
|
||||
const dto = await global.request({
|
||||
url: `/api/message/all`,
|
||||
method: 'get',
|
||||
withCredentials: true
|
||||
});
|
||||
|
||||
if (reset) {
|
||||
global.talkData.forEach((talk) => {
|
||||
const cacheTalk = dto.filter((talkData) => talkData.talkId === talk.talkId)[0];
|
||||
talk.talkMessages = cacheTalk.talkMessages;
|
||||
})
|
||||
} else {
|
||||
let queue = []
|
||||
global.talkData.forEach((talk) => {
|
||||
try {
|
||||
const lastMsg = talk.talkMessages.talks[talk.talkMessages.talks.length - 1]
|
||||
const cacheTalk = dto.filter((talkData) => talkData.talkId === talk.talkId)[0];
|
||||
const newMsg = cacheTalk.talkMessages.talks.filter((ct) => {
|
||||
return Common.dateDifference(new Date(ct.insDate), new Date(lastMsg.insDate))
|
||||
})
|
||||
|
||||
if (newMsg.length > 0) {
|
||||
newMsg.forEach((msg) => {
|
||||
msg.workNm = talk.work.workNm
|
||||
})
|
||||
talk.lastMessage = newMsg[newMsg.length - 1].message;
|
||||
talk.lastMessageDate = newMsg[newMsg.length - 1].insDate;
|
||||
// 톡방 접속중인 경우 메시지 맵핑
|
||||
if (localStorage.getItem(global.tabId) === talk.talkId) {
|
||||
messageMapping(talk.talkId, {talks: newMsg}, 'after', false)
|
||||
// 브라우저 상태가 hidden 인 경우 알림에 추가
|
||||
if (!document.hasFocus()) queue = [...queue, ...newMsg]
|
||||
// 그외 메시지 재정의
|
||||
} else {
|
||||
talk.talkMessages.talks = [...newMsg, ...talk.talkMessages.talks]
|
||||
talk.unReadCnt = talk.unReadCnt + newMsg.length;
|
||||
queue = [...queue, ...newMsg]
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`talk.talkId is null messages`)
|
||||
}
|
||||
})
|
||||
|
||||
console.log(queue)
|
||||
// 추가된 큐가 존재 할시 톡방 저체 갱신
|
||||
// 큐에 정의된 메시지를 알림
|
||||
if (queue.length > 0) {
|
||||
talkMapping(global.talkData);
|
||||
talkEvent.badgeChange();
|
||||
queue.forEach((que) => {
|
||||
showNotification(que)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getMoreMessageAll = async function () {
|
||||
let insDate = '1999-01-01';
|
||||
global.talkData.forEach(talk => {
|
||||
if(insDate < talk.lastMessageDate) {
|
||||
insDate = talk.lastMessageDate
|
||||
}
|
||||
})
|
||||
|
||||
const dtos = await global.request({
|
||||
url: `/api/message/more/all`,
|
||||
method: 'get',
|
||||
params: {'insDate': insDate},
|
||||
withCredentials: true
|
||||
});
|
||||
|
||||
let queue = []
|
||||
dtos.forEach((dto) => {
|
||||
try {
|
||||
const cacheTalk = global.talkData.filter((talk) => talk.talkId === dto.talkId)[0];
|
||||
|
||||
const newMsg = dto.talkMessages.talks;
|
||||
console.log(newMsg)
|
||||
if (newMsg.length > 0) {
|
||||
newMsg.forEach((msg) => {
|
||||
msg.workNm = cacheTalk.work.workNm
|
||||
})
|
||||
cacheTalk.lastMessage = newMsg[newMsg.length - 1].message;
|
||||
cacheTalk.lastMessageDate = newMsg[newMsg.length - 1].insDate;
|
||||
// 톡방 접속중인 경우 메시지 맵핑
|
||||
if (localStorage.getItem(global.tabId) === cacheTalk.talkId) {
|
||||
messageMapping(cacheTalk.talkId, {talks: newMsg}, 'after', false)
|
||||
// 브라우저 상태가 hidden 인 경우 알림에 추가
|
||||
if (!document.hasFocus()) queue = [...queue, ...newMsg]
|
||||
// 그외 메시지 재정의
|
||||
} else {
|
||||
cacheTalk.talkMessages.talks = [...newMsg, ...cacheTalk.talkMessages.talks]
|
||||
cacheTalk.unReadCnt = cacheTalk.unReadCnt + newMsg.length;
|
||||
queue = [...queue, ...newMsg]
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`talk.talkId is null messages`)
|
||||
}
|
||||
})
|
||||
|
||||
// 추가된 큐가 존재 할시 톡방 저체 갱신
|
||||
// 큐에 정의된 메시지를 알림
|
||||
if (queue.length > 0) {
|
||||
talkMapping(global.talkData);
|
||||
talkEvent.badgeChange();
|
||||
queue.forEach((que) => {
|
||||
showNotification(que)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 톡방 내용 가져오기
|
||||
const getMoreMessageLine = (talkId) => {
|
||||
const cacheTalk = global.talkData.filter((talk) => talk.talkId === talkId)[0];
|
||||
cacheTalk.messageParams['insDate'] = cacheTalk.talkMessages.talks[cacheTalk.talkMessages.talks.length - 1].insDate;
|
||||
return new Promise((resolve, reject) => {
|
||||
global.request({
|
||||
url: `/api/message/line`,
|
||||
method: 'get',
|
||||
params: cacheTalk.messageParams,
|
||||
withCredentials: true
|
||||
}).then((dto) => {
|
||||
if (dto.talks.length > 0) {
|
||||
messageMapping(talkId, dto, 'before', false);
|
||||
talkEvent.messageScrollOriginalPositionMove();
|
||||
let result = '';
|
||||
if (cacheTalk.talkMessages.talks.length >= dto.total) {
|
||||
result = 'last';
|
||||
} else {
|
||||
result = 'success';
|
||||
}
|
||||
|
||||
HubEvent.send(global.talkParams({
|
||||
type: 'MORE_MESSAGE_LINE',
|
||||
talkId: talkId,
|
||||
tabId: global.tabId,
|
||||
result: result,
|
||||
talkMessages: dto
|
||||
}))
|
||||
resolve(result)
|
||||
} else {
|
||||
resolve('last');
|
||||
}
|
||||
|
||||
}).catch(function (error) {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 톡방내 메시지 검색
|
||||
const getMessageSearch = (params) => {
|
||||
global.request({
|
||||
url: `/api/message/search`,
|
||||
method: 'get',
|
||||
params: {talkId: params.talkId, search: params.search},
|
||||
withCredentials: true
|
||||
}).then((lists) => {
|
||||
params.callback(lists)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 메시지 통합 검색
|
||||
const getAllContentSearch = (params) => {
|
||||
global.request({
|
||||
url: `/api/message/search/all`,
|
||||
method: 'get',
|
||||
params: {search: params.search},
|
||||
withCredentials: true,
|
||||
}).then((lists) => {
|
||||
params.callback(lists)
|
||||
})
|
||||
}
|
||||
|
||||
//매뉴얼 조회
|
||||
const getManual = (params) => {
|
||||
const sendParam = {type: params.type}
|
||||
if(params.search !== '') sendParam['search'] = params.search
|
||||
if(params.workId !== '') sendParam['workId'] = params.workId
|
||||
global.request({
|
||||
url: `/api/manual`,
|
||||
method: 'get',
|
||||
params: sendParam,
|
||||
withCredentials: true
|
||||
}).then((manuals) => {
|
||||
params.callback(manuals)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
//매뉴얼 조회
|
||||
const getVideoManual = (params) => {
|
||||
const sendParam = {type: params.type}
|
||||
if(params.search !== '') sendParam['search'] = params.search
|
||||
if(params.workId !== '') sendParam['workId'] = params.workId
|
||||
global.request({
|
||||
url: `/api/manual/video`,
|
||||
method: 'get',
|
||||
params: sendParam,
|
||||
withCredentials: true
|
||||
}).then((manuals) => {
|
||||
params.callback(manuals)
|
||||
})
|
||||
}
|
||||
|
||||
// 톡방 삭제 (실제 삭제 여부만 갱신)
|
||||
const deleteTalk = (params) => {
|
||||
global.request({
|
||||
url: `/api/talk/delete/${params.talkId}`,
|
||||
method: 'put',
|
||||
withCredentials: true
|
||||
}).then((data) => {
|
||||
params.callback(data)
|
||||
})
|
||||
}
|
||||
|
||||
// 톡방 읽음 표시
|
||||
const setChatRead = (talkId) => {
|
||||
console.log('read', talkId)
|
||||
return new Promise((resolve, reject) => {
|
||||
global.request({
|
||||
url: `/api/message/read`,
|
||||
method: 'post',
|
||||
body: {talkId: talkId},
|
||||
withCredentials: true
|
||||
}).then((result) => {
|
||||
resolve(result);
|
||||
|
||||
}).catch((err) => {
|
||||
reject(err);
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// 톡방 보관
|
||||
const setTalkMark = (obj) => {
|
||||
global.request({
|
||||
url: `/api/talk/mark/${obj.talkId}`,
|
||||
method: 'put',
|
||||
withCredentials: true
|
||||
}).then(() => {
|
||||
obj.callback()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
talkInit,
|
||||
getUser,
|
||||
getApInfo,
|
||||
getTalkList,
|
||||
getTalkListEvent,
|
||||
setChatList,
|
||||
getMessage,
|
||||
getMoreMessageLine,
|
||||
getMessageSearch,
|
||||
getAllContentSearch,
|
||||
getManual,
|
||||
getVideoManual,
|
||||
setChatRead,
|
||||
setTalkMark,
|
||||
setITSMSatis,
|
||||
setSCMSatis,
|
||||
deleteTalk,
|
||||
getMessageAll,
|
||||
getMoreMessageAll
|
||||
}
|
||||
201
sample/dev/talk/js/module/attach.js
Normal file
201
sample/dev/talk/js/module/attach.js
Normal file
@@ -0,0 +1,201 @@
|
||||
import {talkEvent} from "http://devtalk.kospo.co.kr:3000/static/js/module/talkEvent.js";
|
||||
import {global} from "http://devtalk.kospo.co.kr:3000/static/js/module/variable.js";
|
||||
import Common from "http://devtalk.kospo.co.kr:3000/static/js/utils.js";
|
||||
|
||||
// 첨부파일 업로드 기능
|
||||
|
||||
function readFileAsDataURL(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => resolve(e.target.result);
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
}
|
||||
|
||||
function loadImage(src) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => resolve(img);
|
||||
img.onerror = reject;
|
||||
img.src = src;
|
||||
});
|
||||
}
|
||||
|
||||
const attachUpload = async (files) => {
|
||||
checkAllowExt()
|
||||
const chunkSize = 256 * 1024;
|
||||
const key = talkEvent.generateUUID()
|
||||
|
||||
const workId = localStorage.getItem('workId');
|
||||
const workNm = localStorage.getItem('workNm');
|
||||
const talkId = localStorage.getItem(global.tabId);
|
||||
|
||||
if(talkId === null || workId === null) {
|
||||
global.notifier.alert('통신 오류가 발생 했어요.<br/>새로고침 해주세요.')
|
||||
return
|
||||
}
|
||||
|
||||
const $files = [];
|
||||
const insDate = Common.dateWithNanos(new Date());
|
||||
let allFileSize = 0;
|
||||
let isImage = true;
|
||||
|
||||
// mimtype 조회 이미지가 아닌 다른 파일이 있는지 확인
|
||||
Array.from(files).forEach((fi) => {
|
||||
if (!(fi.type.includes('image'))) {
|
||||
isImage = false;
|
||||
}
|
||||
})
|
||||
|
||||
for (const file of Array.from(files)) {
|
||||
const idx = Array.from(files).indexOf(file);
|
||||
allFileSize += file.size;
|
||||
const att = {
|
||||
onm: file.name,
|
||||
type: file.type,
|
||||
sort: idx,
|
||||
size: file.size,
|
||||
}
|
||||
|
||||
// 파일 전체가 이미지 일 경우 해당 바이너리 정보 주입
|
||||
if (isImage) {
|
||||
// 1. 파일을 읽어서 DataURL로 변환 (FileReader 비동기 처리)
|
||||
const dataURL = await readFileAsDataURL(file);
|
||||
// 2. 변환된 DataURL을 이용해 이미지 로드 (Image 비동기 처리)
|
||||
const img = await loadImage(dataURL);
|
||||
// 3. 썸네일 용 dataURL 생성
|
||||
const blob = await Common.cropImage(img, 105);
|
||||
att.data = blob;
|
||||
}
|
||||
|
||||
$files.push(att);
|
||||
|
||||
}
|
||||
/*
|
||||
첨부파일 업로드 설정 소켓 통신으로 해당 파일의 정보를 보내준다.
|
||||
정보를 받는 대상자는 첨부파일 업로드 화면을 렌더링 한다.
|
||||
이미지 같은 경우는 위 바이너리 정보로 썸네일 구성
|
||||
이미지외 파일 혼합된 경우 파일 다운로드 화면 구성
|
||||
(서버에서는 해당 내용으로 메시지를 저장한다.)
|
||||
*/
|
||||
talkEvent.attach(global.talkParams({
|
||||
type: 'INIT',
|
||||
talkId: talkId,
|
||||
workId: workId,
|
||||
workNm: workNm,
|
||||
message: isImage ? "[사진]" : '[파일]',
|
||||
insDate: insDate,
|
||||
talkAttachDtos: $files,
|
||||
attach: {
|
||||
key: key,
|
||||
totalSize: allFileSize
|
||||
}
|
||||
}))
|
||||
|
||||
|
||||
setTimeout(attachStart, 300)
|
||||
|
||||
function checkAllowExt() {
|
||||
const allow = ["jpg", "jpeg", "gif", "png", "bmp",
|
||||
"xls", "xlsx", "csv", "pdf", "hwp", "doc", "docx", "ppt", "pptx", "txt",
|
||||
"zip", "egg",
|
||||
"mp4"]
|
||||
|
||||
files = Array.from(files).filter((file) => {
|
||||
const fileExt = file.name.toLowerCase().substr(file.name.toLowerCase().lastIndexOf('.') + 1)
|
||||
return allow.join(',').includes(fileExt)
|
||||
})
|
||||
}
|
||||
|
||||
function attachStart() {
|
||||
if (files.length !== 0) {
|
||||
Array.from(files).forEach((file, idx) => {
|
||||
const vnm = Common.getAttachVnm(new Date(), global.tabId)
|
||||
console.log(idx, vnm)
|
||||
let offset = 0;
|
||||
/*
|
||||
각파일 정보를 전송
|
||||
(서버에서는 메시지 첨부파일을 저장한다.)
|
||||
*/
|
||||
talkEvent.attach(global.talkParams({
|
||||
type: 'START',
|
||||
talkId: talkId,
|
||||
workId: workId,
|
||||
workNm: workNm,
|
||||
insDate: insDate,
|
||||
attach: {
|
||||
key: key,
|
||||
onm: file.name,
|
||||
vnm: vnm,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
sort: idx,
|
||||
totalSize: allFileSize
|
||||
|
||||
}
|
||||
}))
|
||||
readAndSEndChunk(file, idx, offset, vnm)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let progressSize = 0;
|
||||
|
||||
function readAndSEndChunk(file, idx, offset, vnm) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const chunk = reader.result;
|
||||
/*
|
||||
파일의 바이너리 정보를 chunkSize 256kb 로 잘라서 전송한다.
|
||||
받는 대상자는 해당 첨부파일의 진행률을 돌려받는다.
|
||||
*/
|
||||
talkEvent.attach(global.talkParams({
|
||||
type: 'PROGRESS',
|
||||
talkId: talkId,
|
||||
workId: workId,
|
||||
workNm: workNm,
|
||||
insDate: insDate,
|
||||
attach: {
|
||||
key: key,
|
||||
onm: file.name,
|
||||
sort: idx,
|
||||
vnm: vnm,
|
||||
totalSize: allFileSize,
|
||||
size: progressSize,
|
||||
data: Array.from(new Uint8Array(chunk))
|
||||
},
|
||||
}))
|
||||
|
||||
progressSize += chunk.byteLength;
|
||||
offset += chunk.byteLength;
|
||||
if (offset < file.size) {
|
||||
Common.sleep(100).then(() => {
|
||||
readAndSEndChunk(file, idx, offset, vnm)
|
||||
})
|
||||
} else {
|
||||
/*
|
||||
파일 전송 완료 메시지
|
||||
*/
|
||||
talkEvent.attach(global.talkParams({
|
||||
type: 'END',
|
||||
talkId: talkId,
|
||||
workId: workId,
|
||||
workNm: workNm,
|
||||
insDate: insDate,
|
||||
attach: {
|
||||
onm: file.name,
|
||||
vnm: vnm,
|
||||
key: key,
|
||||
},
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
const slice = file.slice(offset, offset + chunkSize);
|
||||
reader.readAsArrayBuffer(slice)
|
||||
}
|
||||
}
|
||||
|
||||
export {attachUpload}
|
||||
229
sample/dev/talk/js/module/hub.js
Normal file
229
sample/dev/talk/js/module/hub.js
Normal file
@@ -0,0 +1,229 @@
|
||||
import {global} from "http://devtalk.kospo.co.kr:3000/static/js/module/variable.js";
|
||||
import {HubEvent} from "http://devtalk.kospo.co.kr:3000/static/js/module/hubEvent.js";
|
||||
import Common from "http://devtalk.kospo.co.kr:3000/static/js/utils.js";
|
||||
import {talkEvent} from "http://devtalk.kospo.co.kr:3000/static/js/module/talkEvent.js";
|
||||
import {getMessageAll, getTalkList, getMoreMessageAll} from "http://devtalk.kospo.co.kr:3000/static/js/module/apis.js";
|
||||
import receiver from "http://devtalk.kospo.co.kr:3000/static/js/module/receiver.js";
|
||||
|
||||
export default class Hub {
|
||||
constructor() {
|
||||
this.controller = new AbortController();
|
||||
this.signal = this.controller.signal;
|
||||
}
|
||||
|
||||
|
||||
install() {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register(global.option.serviceWorkerLocation, {updateViaCache: 'none'})
|
||||
.then(async registration => {
|
||||
global.registration = registration;
|
||||
let checker = true;
|
||||
while (checker) {
|
||||
if (global.registration.active !== undefined && global.registration.active !== null) checker = false;
|
||||
await Common.sleep(5)
|
||||
}
|
||||
// 서비스워커 업데이트
|
||||
registration.update();
|
||||
|
||||
// 서비스 워커 설치 완료후 HUB 소켓 연결 명령
|
||||
HubEvent.connect({
|
||||
type: 'CONNECT',
|
||||
tabId: global.tabId,
|
||||
url: global.apiUrl,
|
||||
sabun: global.user.sabun,
|
||||
encSabun: global.option.encSabun,
|
||||
site: global.currentUrl
|
||||
})
|
||||
|
||||
resolve();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Service Worker registration failed:', error);
|
||||
reject();
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// HUB 소켓 리시버 정의
|
||||
start() {
|
||||
console.log('hub message event start', new Date())
|
||||
// navigator.serviceWorker.removeEventListener('message', this.message, {signal: this.controller.signal})
|
||||
navigator.serviceWorker.addEventListener('message', this.message,{signal: this.signal})
|
||||
}
|
||||
|
||||
stop() {
|
||||
console.log('hub message event stop', new Date())
|
||||
this.controller.abort();
|
||||
this.controller = new AbortController();
|
||||
this.signal = this.controller.signal;
|
||||
}
|
||||
|
||||
message(event) {
|
||||
|
||||
const payload = event.data;
|
||||
|
||||
if(payload.type === 'PING') {
|
||||
// console.log('PING', new Date())
|
||||
try {
|
||||
global.registration.active.postMessage({
|
||||
type: 'PONG'
|
||||
});
|
||||
} catch(e) {}
|
||||
} else if (payload.type === 'GETUP') {
|
||||
global.serviceWorkerConnect = true;
|
||||
} else if (payload.type === 'HUB_RECONNECT') {
|
||||
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(() => {
|
||||
// global.hub.start();
|
||||
HubEvent.start(global.talkParams({
|
||||
type: 'START',
|
||||
tabId: global.tabId
|
||||
}))
|
||||
// getMessageAll(false)
|
||||
getMoreMessageAll()
|
||||
|
||||
global.registration.active.postMessage({'type': 'HUB_RECONNECT_SUCCESS'})
|
||||
});
|
||||
} else if (payload.type === 'SEND_OTHER_INIT') {
|
||||
if (Common.isMaster()) {
|
||||
HubEvent.otherInit({
|
||||
type: 'OTHER_INIT',
|
||||
talkData: global.talkData,
|
||||
insSabun: global.user.sabun,
|
||||
tabId: global.tabId
|
||||
})
|
||||
}
|
||||
} else if (payload.type === 'SEND_SET_MASTER') {
|
||||
if (!Common.isMaster()) {
|
||||
global.init = false;
|
||||
talkEvent.reConnect()
|
||||
}
|
||||
} else if (payload.type === 'SEND_TYPED') {
|
||||
if (Common.isMaster()) {
|
||||
payload.type = payload.type.replace('SEND_', '')
|
||||
talkEvent.send(payload)
|
||||
}
|
||||
} else if (payload.type === 'OTHER_INIT') {
|
||||
if (!Common.isMaster()) {
|
||||
receiver.otherInit(payload)
|
||||
}
|
||||
} else if (payload.type === 'SEND') {
|
||||
if (Common.isMaster()) {
|
||||
talkEvent.send(payload)
|
||||
}
|
||||
} else if (payload.type === 'JOIN') {
|
||||
receiver.join(payload)
|
||||
} else if (payload.type === 'CHANGE') {
|
||||
receiver.change(payload)
|
||||
} else if (payload.type === 'START') {
|
||||
receiver.start(payload)
|
||||
} else if (payload.type === 'LEAVE') {
|
||||
receiver.leave(payload)
|
||||
} else if (payload.type === 'DELETE') {
|
||||
receiver.delete(payload)
|
||||
} else if (payload.type === 'WORK_ONLINE') {
|
||||
receiver.workOnline(payload)
|
||||
} else if (payload.type === 'WORK_OFFLINE') {
|
||||
receiver.workOffline(payload)
|
||||
} else if (payload.type === 'ONLINE') {
|
||||
receiver.online(payload)
|
||||
} else if (payload.type === 'OFFLINE') {
|
||||
receiver.offline(payload)
|
||||
} else if (payload.type === 'ERROR') {
|
||||
receiver.error(payload)
|
||||
} else if (payload.type === 'STATUS') {
|
||||
receiver.message(payload)
|
||||
} else if(payload.type === 'EXTERNAL_SAVE') {
|
||||
receiver.externalSave(payload)
|
||||
} else if (payload.type === 'INTRO') {
|
||||
receiver.intro(payload)
|
||||
} else if (payload.type === 'MESSAGE') {
|
||||
global.checker.flag = false;
|
||||
global.checker.inDate = '';
|
||||
receiver.message(payload);
|
||||
const chatContent = global.shadowRoot.querySelector('#chat-content');
|
||||
const content = chatContent.querySelector('.simplebar-content');
|
||||
const typedProgress = content.querySelector('.your-dot-progress');
|
||||
if(typedProgress) {
|
||||
const clone = typedProgress.cloneNode(true)
|
||||
talkEvent.messageUnTyped()
|
||||
content.insertAdjacentElement('beforeend', clone)
|
||||
}
|
||||
} else if(payload.type === 'QUESTION') {
|
||||
receiver.message(payload);
|
||||
} else if (payload.type === 'INFO_MESSAGE') {
|
||||
receiver.infoMessage(payload)
|
||||
} else if (payload.type === 'CLOSE') {
|
||||
receiver.close(payload);
|
||||
} else if (payload.type === 'TYPED') {
|
||||
console.log('service worker message', payload)
|
||||
receiver.typed(payload);
|
||||
} else if (payload.type === 'UNTYPED') {
|
||||
console.log('service worker message', payload)
|
||||
receiver.untyped(payload);
|
||||
} else if (payload.type === 'READ') {
|
||||
receiver.read(payload);
|
||||
} else if (payload.type === 'ATTACH_INIT') {
|
||||
receiver.attachStart(payload);
|
||||
} else if (payload.type === 'ATTACH_PROGRESS') {
|
||||
receiver.attachProgress(payload);
|
||||
} else if (payload.type === 'ATTACH_END') {
|
||||
receiver.attachEnd(payload);
|
||||
} else if (payload.type === 'MORE_MESSAGE_LINE') {
|
||||
if (payload.tabId !== global.tabId) {
|
||||
receiver.moreMessageLine(payload)
|
||||
}
|
||||
} else if (payload.type === 'DELETE') {
|
||||
/*삭제처리*/
|
||||
if (localStorage.getItem('talkId') === payload.talkId) {
|
||||
const chatBack = global.shadowRoot.querySelector('.contacts-list-show');
|
||||
chatBack.click();
|
||||
}
|
||||
talkEvent.talkRemove(payload.talkId)
|
||||
} else if (payload.type === 'MARK') {
|
||||
/*보관처리*/
|
||||
talkEvent.talkMark(payload.talkId)
|
||||
} else if (payload.type === 'UNSUBSCRIBE') {
|
||||
/*새로운 탭 활성화시 현재 탭 외 모든 구독 해제 조건(탭아이디가 동일 하지 않으며 모든 로딩이 완료)*/
|
||||
if (payload.tabId !== global.tabId && global.init) {
|
||||
talkEvent.allUnsubscribe();
|
||||
}
|
||||
} else if (payload.type === 'WORK_COUNT') {
|
||||
/*현재 업무 카운트*/
|
||||
receiver.changeLoginInfo(payload)
|
||||
} else if(payload.type === 'SW_RECONNECT') {
|
||||
console.log('sw_reconnect')
|
||||
// 개인 사번 소켓 연결
|
||||
try{global.subscribeList[global.user.sabun] = talkEvent.userSubscribe();}catch(e){}
|
||||
// 업무별 소켓 연결
|
||||
try{global.subscribeList[global.work.workId] = talkEvent.workSubscribe();}catch(e){}
|
||||
// 톡방 재갱신
|
||||
// getMessageAll(false)
|
||||
getMoreMessageAll()
|
||||
if (Common.isMaster()) {
|
||||
// 허브설정
|
||||
HubEvent.start(global.talkParams({
|
||||
type: 'START',
|
||||
tabId: global.tabId
|
||||
}))
|
||||
|
||||
// 현재 탭 외 모든 탭 구독 해지
|
||||
HubEvent.send(global.talkParams({
|
||||
type: 'UNSUBSCRIBE',
|
||||
tabId: global.tabId
|
||||
}))
|
||||
}
|
||||
} else if(payload.type === 'HEARTBEAT') {
|
||||
console.log("PONG")
|
||||
}
|
||||
}
|
||||
}
|
||||
65
sample/dev/talk/js/module/hubEvent.js
Normal file
65
sample/dev/talk/js/module/hubEvent.js
Normal file
@@ -0,0 +1,65 @@
|
||||
import {global} from "http://devtalk.kospo.co.kr:3000/static/js/module/variable.js";
|
||||
|
||||
const HubEvent = {
|
||||
start: (payload) => {
|
||||
global.stomp.send(`/pub/hub.start.${global.tabId}`, {}, JSON.stringify(payload))
|
||||
},
|
||||
connect: (payload) => {
|
||||
global.registration.active.postMessage(payload)
|
||||
},
|
||||
close: (payload) => {
|
||||
if(payload.talkId === null) return false;
|
||||
global.registration.active.postMessage(payload)
|
||||
global.stomp.send(`/pub/hub.close.${global.tabId}`, {}, JSON.stringify(payload))
|
||||
},
|
||||
kill: (payload) => {
|
||||
global.registration.active.postMessage(payload)
|
||||
global.registration.unregister();
|
||||
},
|
||||
sendSetMaster: (payload) => {
|
||||
payload.channelSabun = global.user.sabun
|
||||
payload.tabId = global.tabId
|
||||
global.stomp.send(`/pub/hub.send.setMaster`, {}, JSON.stringify(payload))
|
||||
},
|
||||
otherInit: (payload) => {
|
||||
payload.channelSabun = global.user.sabun
|
||||
payload.tabId = global.tabId
|
||||
global.stomp.send(`/pub/hub.otherInit`, {}, JSON.stringify(payload))
|
||||
},
|
||||
send: (payload) => {
|
||||
// console.log('hub send is me', payload)
|
||||
if(payload.talkId === null) return false;
|
||||
payload.channelSabun = global.user.sabun
|
||||
payload.tabId = global.tabId
|
||||
try {
|
||||
if (payload.type === 'ONLINE' || payload.type === 'OFFLINE') {
|
||||
global.stomp.send(`/pub/hub.online`, {}, JSON.stringify(payload))
|
||||
} else if (payload.type === 'ATTACH_INIT') {
|
||||
global.stomp.send(`/pub/hub.file.message`, {}, JSON.stringify(payload))
|
||||
} else {
|
||||
/*톡 요청 상태에 따른 comp_yn 완료 처리*/
|
||||
if(payload.reqType === '9000') {
|
||||
const cacheTalk = global.talkData.filter((talk) => talk.talkId === payload.talkId)[0];
|
||||
cacheTalk.compYn= true
|
||||
console.log(cacheTalk)
|
||||
}
|
||||
global.stomp.send(`/pub/hub.talk.message`, {}, JSON.stringify(payload))
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
},
|
||||
fileSend: (payload) => {
|
||||
if(payload.talkId === null) return false;
|
||||
payload.channelSabun = global.user.sabun
|
||||
payload.tabId = global.tabId
|
||||
global.stomp.send(`/pub/hub.file.message`, {}, JSON.stringify(payload))
|
||||
},
|
||||
work: (payload) => {
|
||||
payload.channelSabun = global.user.sabun
|
||||
payload.tabId = global.tabId
|
||||
global.stomp.send(`/pub/hub.work`, {}, JSON.stringify(payload))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export {HubEvent};
|
||||
46
sample/dev/talk/js/module/queue.js
Normal file
46
sample/dev/talk/js/module/queue.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import {global} from 'http://devtalk.kospo.co.kr:3000/static/js/module/variable.js';
|
||||
import {talkEvent} from 'http://devtalk.kospo.co.kr:3000/static/js/module/talkEvent.js';
|
||||
import Common from "http://devtalk.kospo.co.kr:3000/static/js/utils.js";
|
||||
|
||||
class SocketQueue {
|
||||
constructor() {
|
||||
this.queue = [];
|
||||
this.isSending = false;
|
||||
}
|
||||
|
||||
enqueue(data) {
|
||||
this.queue.push(data);
|
||||
if(!this.isSending) {
|
||||
this.processQueue();
|
||||
}
|
||||
}
|
||||
|
||||
async processQueue() {
|
||||
this.isSending = true;
|
||||
while(this.queue.length > 0) {
|
||||
const data = this.queue.shift();
|
||||
try {
|
||||
await this.sendToSocket(data);
|
||||
Common.sleep(30).then(() => {
|
||||
this.isSending = false;
|
||||
});
|
||||
} catch(err) {
|
||||
this.isSending = false;
|
||||
console.error('전송실패', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async sendToSocket(data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if(global.sockJS.readyState === WebSocket.OPEN) {
|
||||
talkEvent.send(global.talkParams(data));
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error('소켓 연결 안됨'))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default SocketQueue
|
||||
657
sample/dev/talk/js/module/receiver.js
Normal file
657
sample/dev/talk/js/module/receiver.js
Normal file
@@ -0,0 +1,657 @@
|
||||
import {global} from "http://devtalk.kospo.co.kr:3000/static/js/module/variable.js";
|
||||
import {infoMessage, systemMessage, talkMapping, messageMapping} from "http://devtalk.kospo.co.kr:3000/static/js/module/render.js";
|
||||
import {getMessage, getTalkListEvent, setChatList, setChatRead} from "http://devtalk.kospo.co.kr:3000/static/js/module/apis.js";
|
||||
import {showNotification} from 'http://devtalk.kospo.co.kr:3000/static/js/notification.js';
|
||||
import {talkEvent} from "http://devtalk.kospo.co.kr:3000/static/js/module/talkEvent.js";
|
||||
import Common from "http://devtalk.kospo.co.kr:3000/static/js/utils.js";
|
||||
import {HubEvent} from "http://devtalk.kospo.co.kr:3000/static/js/module/hubEvent.js"
|
||||
|
||||
// 소켓 메시지 이벤트
|
||||
|
||||
const receiver = {
|
||||
workOnline: (payload) => {
|
||||
const introOnline = global.shadowRoot.querySelector('#intro-online');
|
||||
introOnline.classList.remove('offline');
|
||||
introOnline.classList.add('online');
|
||||
},
|
||||
workOffline: (payload) => {
|
||||
const introOnline = global.shadowRoot.querySelector('#intro-online');
|
||||
introOnline.classList.remove('online');
|
||||
introOnline.classList.add('offline');
|
||||
},
|
||||
online: (payload) => {
|
||||
const onlineList = [];
|
||||
if (payload.works.length === 0) {
|
||||
const chatList = global.shadowRoot.querySelectorAll(`div[data-target-sabun="${payload.sabun}"]`)
|
||||
|
||||
chatList.forEach((el) => {
|
||||
onlineList.push(el.dataset.talkId);
|
||||
const offline = el.querySelector('.status-offline')
|
||||
if (offline) {
|
||||
offline.classList.remove('status-offline')
|
||||
offline.classList.add('status-online')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const chatList = global.shadowRoot.querySelectorAll('.chat-wrap');
|
||||
chatList.forEach((el) => {
|
||||
// const targetSabun = el.getAttribute('data-target-sabun');
|
||||
const smSabun = el.getAttribute('data-work-sm-sabun');
|
||||
if (smSabun.includes(payload.sabun)) {
|
||||
onlineList.push(el.dataset.talkId);
|
||||
const offline = el.querySelector('.status-offline')
|
||||
|
||||
if (offline) {
|
||||
offline.classList.remove('status-offline')
|
||||
offline.classList.add('status-online')
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
global.talkData.forEach((talk) => {
|
||||
if (onlineList.includes(talk.talkId)) {
|
||||
talk.talkMembers.forEach((tm) => {
|
||||
if (tm.sabun === payload.sabun) {
|
||||
tm.onlineYn = true;
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
offline: (payload) => {
|
||||
const offlineList = [];
|
||||
let onlineYn = false;
|
||||
if (payload.works.length === 0) {
|
||||
const chatList = global.shadowRoot.querySelectorAll(`div[data-target-sabun="${payload.sabun}"]`)
|
||||
chatList.forEach((el) => {
|
||||
offlineList.push(el.dataset.talkId);
|
||||
const offline = el.querySelector('.status-online')
|
||||
if (offline) {
|
||||
offline.classList.remove('status-online')
|
||||
offline.classList.add('status-offline')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
let oldtalkId = "";
|
||||
const chatList = payload.talks.filter((ch) => {
|
||||
if (ch.sabun !== global.user.sabun && ch.onlineYn) oldtalkId = ch.talkId;
|
||||
if (ch.sabun !== global.user.sabun) return ch;
|
||||
})
|
||||
|
||||
chatList.forEach((cl) => {
|
||||
if (oldtalkId !== cl.talkId) {
|
||||
const chatEl = global.shadowRoot.querySelector(`div[data-talk-id="${cl.talkId}"]`);
|
||||
if (chatEl) {
|
||||
const online = chatEl.querySelector('.status-online')
|
||||
if (online) {
|
||||
online.classList.remove('status-online')
|
||||
online.classList.add('status-offline')
|
||||
}
|
||||
const smSabun = chatEl.getAttribute('data-work-sm-sabun');
|
||||
if (smSabun.includes(payload.sabun)) {
|
||||
offlineList.push(chatEl.dataset.talkId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
global.talkData.forEach((talk) => {
|
||||
if (offlineList.includes(talk.talkId)) {
|
||||
talk.talkMembers.forEach((tm) => {
|
||||
if (tm.sabun === payload.sabun) {
|
||||
tm.onlineYn = false;
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
error: (payload) => {
|
||||
global.notifier.alert(payload.message)
|
||||
},
|
||||
join: (payload) => {
|
||||
setChatList(payload)
|
||||
if(localStorage.getItem(global.tab) === '' || localStorage.getItem(global.tab) === null ) {
|
||||
if (!(payload.work.smUsers.map((sm) => sm.sabun).toString().indexOf(global.user.sabun) > -1)) {
|
||||
// 방 생성자 주입
|
||||
if (payload.tabId === global.tabId) {
|
||||
/* 방 생성자 초기화 */
|
||||
console.log("*(*******************A")
|
||||
global.chatCreator = null;
|
||||
talkEvent.messageShow();
|
||||
localStorage.setItem(global.tabId, payload.talkId)
|
||||
talkEvent.talkHeaderChange(payload.work.workNm, payload.work.smUsers.map((sm) => sm.name).join(','));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
change: (payload) => {
|
||||
setChatList(payload)
|
||||
const cacheTalk = global.talkData.filter((talk) => talk.talkId === payload.talkId)[0];
|
||||
cacheTalk.messageParams = {};
|
||||
cacheTalk.messageParams.talkId = payload.talkId;
|
||||
cacheTalk.messageParams.start = 0;
|
||||
cacheTalk.messageParams.row = 30;
|
||||
cacheTalk.lastMessage = payload.lastMessage;
|
||||
cacheTalk.lastMessageDate = payload.lastMessageDate;
|
||||
cacheTalk.talkMessages = payload.talkMessages;
|
||||
cacheTalk.talkMembers = payload.talkMembers
|
||||
console.log('user change', cacheTalk)
|
||||
|
||||
|
||||
if (!(payload.work.smUsers.map((sm) => sm.sabun).includes(global.user.sabun))) {
|
||||
// 방 생성자 주입
|
||||
if (payload.insSabun === global.user.sabun) {
|
||||
|
||||
const _this = global.shadowRoot.querySelector(`div[data-talk-id="${payload.talkId}"]`)
|
||||
const talkId = _this.dataset.talkId;
|
||||
const workId = _this.dataset.workId;
|
||||
const workNm = _this.dataset.workNm;
|
||||
const workSmNm = _this.dataset.workSmNm;
|
||||
|
||||
const currentTalkId = localStorage.getItem(global.tabId)
|
||||
if (currentTalkId === payload.prevTalkId) {
|
||||
|
||||
talkEvent.talkHeaderChange(workNm, workSmNm)
|
||||
|
||||
global.talkClean();
|
||||
global.scrollMoreFlag = false;
|
||||
localStorage.setItem(global.tabId, talkId);
|
||||
localStorage.setItem('workId', workId);
|
||||
localStorage.setItem('workNm', workNm);
|
||||
}
|
||||
|
||||
talkMapping(global.talkData)
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
start: (payload) => {
|
||||
if(localStorage.getItem(global.tabId) === payload.talkId) {
|
||||
if (global.user.sabun !== payload.insSabun) {
|
||||
const cacheTalk = global.talkData.filter((talk) => talk.talkId === payload.talkId)[0];
|
||||
cacheTalk.talkMembers = cacheTalk.talkMembers.filter((member) => (member.sabun === 'info' || member.sabun === global.user.sabun || member.sabun === payload.insSabun) )
|
||||
// 앤드 유저 이벤트 설정
|
||||
const room = global.shadowRoot.querySelector(`div[data-talk-id="${payload.talkId}"]`);
|
||||
room.dataset.workSmNm = payload.insName
|
||||
room.dataset.workSmSabun = payload.insSabun
|
||||
talkEvent.talkTitleChange(payload.talkId, payload.insSabun)
|
||||
talkEvent.talkHeaderChange(null, payload.insName)
|
||||
|
||||
} else {
|
||||
// 유지보수 담당자 이벤트 설정
|
||||
const room = global.shadowRoot.querySelector(`div[data-talk-id="${payload.talkId}"]`);
|
||||
room.setAttribute("data-work-sm-nm", payload.insName)
|
||||
room.setAttribute("data-work-sm-sabun", payload.insSabun)
|
||||
talkEvent.talkHeaderChange(null, payload.insName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
leave: (payload) => {
|
||||
/*구독 취소*/
|
||||
talkEvent.talkRemove(payload.talkId);
|
||||
if (Common.isMaster()) {
|
||||
console.log('unsubscribe')
|
||||
talkEvent.unsubscribe(payload.talkId);
|
||||
}
|
||||
|
||||
if(localStorage.getItem(global.tabId) === payload.talkId) {
|
||||
talkEvent.talkHeaderChange('', '')
|
||||
global.reset()
|
||||
}
|
||||
|
||||
},
|
||||
delete: (payload) => {
|
||||
/*구독 취소*/
|
||||
talkEvent.talkRemove(payload.talkId);
|
||||
if (Common.isMaster()) {
|
||||
console.log('unsubscribe')
|
||||
talkEvent.unsubscribe(payload.talkId);
|
||||
}
|
||||
},
|
||||
close: (payload) => {
|
||||
if (Common.isMaster()) {
|
||||
talkEvent.unsubscribe(payload.talkId);
|
||||
}
|
||||
if (localStorage.getItem(global.tabId) === payload.talkId && !payload.force) {
|
||||
global.element.chatEditorArea.classList.add('cus-hide');
|
||||
const chatRatingConfirmWrapper = global.shadowRoot.querySelector('.chat-rating-confirm-wrapper')
|
||||
chatRatingConfirmWrapper.classList.remove('cus-hide')
|
||||
chatRatingConfirmWrapper.classList.add('cus-show')
|
||||
}
|
||||
|
||||
const room = global.shadowRoot.querySelector(`div[data-talk-id="${payload.talkId}"]`);
|
||||
|
||||
/*상담 강제 종료시 만족사 조사 제외 완료 처리*/
|
||||
if(payload.force) {
|
||||
room.dataset.compYn = true;
|
||||
room.dataset.ratingYn = true;
|
||||
}
|
||||
|
||||
|
||||
room.classList.add('chat-close');
|
||||
const avatar = room.querySelector('.avatar')
|
||||
avatar.classList.remove('status-online')
|
||||
avatar.classList.remove('status-offline')
|
||||
avatar.classList.add('status-do-not-disturb')
|
||||
|
||||
const roomData = global.talkData.filter((talk) => talk.talkId === payload.talkId)[0];
|
||||
roomData.closeYn = true;
|
||||
talkMapping(global.talkData)
|
||||
if(localStorage.getItem(global.tabId) === payload.talkId) {
|
||||
global.element.chatEditorArea.classList.add('cus-hide');
|
||||
}
|
||||
|
||||
},
|
||||
intro: (payload) => {
|
||||
if(localStorage.getItem(global.tabId) === payload.talkId) {
|
||||
infoMessage(payload.message)
|
||||
talkEvent.messageShow()
|
||||
}
|
||||
},
|
||||
message: async (payload) => {
|
||||
|
||||
//기초 데이터 초기화
|
||||
global.yourOldDate = '';
|
||||
global.mineOldDate = '';
|
||||
global.currDay = '';
|
||||
let incrementIdxFlag = true;
|
||||
let listening = 1;
|
||||
// Common.setListening(1);
|
||||
const master = Common.isMaster();
|
||||
|
||||
/*현재 톡방이 첨부파일 메시지 와 동일한 방이면 메시지 맵핑*/
|
||||
if(master) {
|
||||
|
||||
if ((localStorage.getItem(global.tabId) === payload.talkId && document.visibilityState === 'hidden') || (localStorage.getItem(global.tabId) === payload.talkId && global.shadowRoot.querySelector('.chat-floating').classList.contains('cus-hide'))) {
|
||||
listening = 3
|
||||
} else if (localStorage.getItem(global.tabId) === payload.talkId) {
|
||||
listening = 2
|
||||
}
|
||||
|
||||
// 우선순위가 되는 상태 간격 조절
|
||||
// Common.setListening(payload.talkId, listening);
|
||||
} else {
|
||||
listening = 99
|
||||
// await Common.sleep(400)
|
||||
}
|
||||
|
||||
|
||||
/*메시지 호출이 안된 상태이면 제외 첫 접근도 안된 상태로 메시지 맵핑 필요없음*/
|
||||
const cacheTalk = global.talkData.filter((talk) => talk.talkId === payload.talkId)[0];
|
||||
/* 메시지 존재 여부에 따른 분기 처리 */
|
||||
try {
|
||||
if (cacheTalk.talkMessages !== undefined) {
|
||||
messageMapping(payload.talkId, {talks: [payload]}, 'after', false)
|
||||
} else {
|
||||
messageMapping(payload.talkId, {talks: [payload]}, 'after', true)
|
||||
}
|
||||
} catch(e) {
|
||||
global.notifier.alert('통신 오류가 발생 했어요.<br/>새로고침 해주세요.')
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
톡방에 입장중이면 썸네일 메시지 전송
|
||||
입장중이 아니면 톡방 목록에 배지 카운트 추가및 메시지 메시지 변경
|
||||
* */
|
||||
|
||||
const msg = payload.message.includes('[매뉴얼]') ? '[매뉴얼]' : payload.message
|
||||
// await Common.sleep(200)
|
||||
// listening = Common.getListening(payload.talkId);
|
||||
if (listening === 1) {
|
||||
if(master) {
|
||||
if (payload.type !== 'QUESTION') {
|
||||
showNotification({
|
||||
workNm: payload.workNm,
|
||||
talkId: payload.talkId,
|
||||
message: msg,
|
||||
insName: payload.insName,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if(listening === 2 || listening === 3) {
|
||||
// 메인탭만 작동하도록 조치
|
||||
// 현재 톡방 에 입장중이며 해당 탭이 비활성화 인상태 일시 쿠키값 변경
|
||||
|
||||
// if (localStorage.getItem(global.tabId) === payload.talkId && !document.hasFocus()) {
|
||||
// Common.setListening(payload.talkId, 1);
|
||||
// }
|
||||
|
||||
if (master) {
|
||||
talkEvent.readMessage(payload.talkId)
|
||||
}
|
||||
|
||||
if (payload.insSabun !== global.user.sabun) {
|
||||
if (master) {
|
||||
setChatRead(payload.talkId)
|
||||
}
|
||||
// 기타 톡방으로 메시지 읽음 전송
|
||||
console.log('read')
|
||||
HubEvent.send(global.talkParams({
|
||||
type: "READ",
|
||||
talkId: payload.talkId,
|
||||
insSabun: global.user.sabun
|
||||
}))
|
||||
|
||||
}
|
||||
|
||||
// 스크롤 옵저버가 꺼져 있을시 메시지 팝업으로 활성화
|
||||
if (!global.scrollObserveFlag && payload.talkId === localStorage.getItem(global.tabId) && payload.insSabun !== global.user.sabun) {
|
||||
talkEvent.preMessageShow(msg)
|
||||
}
|
||||
if (listening === 3) {
|
||||
if (master) {
|
||||
if (payload.type !== 'QUESTION') {
|
||||
showNotification({
|
||||
workNm: payload.workNm,
|
||||
talkId: payload.talkId,
|
||||
message: msg ,
|
||||
insName: payload.insName,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
incrementIdxFlag = false;
|
||||
} else {
|
||||
incrementIdxFlag = false;
|
||||
}
|
||||
|
||||
|
||||
// 톡방 데이터 주입
|
||||
global.talkData.forEach((talk, idx) => {
|
||||
if (talk.talkId === payload.talkId) {
|
||||
talk.unReadCnt = incrementIdxFlag ? talk.unReadCnt + 1 : 0;
|
||||
if (payload.type !== 'QUESTION') {
|
||||
talk.lastMessage = msg;
|
||||
talk.lastMessageDate = payload.insDate;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 톡방 재정의
|
||||
talkMapping(global.talkData)
|
||||
|
||||
// 메인 배지 재정의
|
||||
if (payload.type !== 'QUESTION') {
|
||||
talkEvent.badgeChange()
|
||||
}
|
||||
},
|
||||
infoMessage: (payload) => {
|
||||
console.log('infoMessage')
|
||||
if(localStorage.getItem(global.tabId) === payload.talkId) {
|
||||
if (payload.insSabun === global.user.sabun) {
|
||||
systemMessage(payload.message);
|
||||
// showNotification(payload.workNm, payload.talkId, message)
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
externalSave: (payload) => {
|
||||
const cacheTalk = global.talkData.filter((talk) => talk.talkId === payload.talkId)[0];
|
||||
cacheTalk.reqId = payload.reqId;
|
||||
},
|
||||
typed: (payload) => {
|
||||
const currentTalkId = localStorage.getItem(global.tabId)
|
||||
if (currentTalkId === payload.talkId) {
|
||||
if (payload.insSabun !== global.user.sabun) {
|
||||
talkEvent.messageTyped(payload);
|
||||
}
|
||||
}
|
||||
},
|
||||
untyped: (payload) => {
|
||||
const currentTalkId = localStorage.getItem(global.tabId)
|
||||
if (currentTalkId === payload.talkId) {
|
||||
if (payload.insSabun !== global.user.sabun) {
|
||||
talkEvent.messageUnTyped(payload);
|
||||
}
|
||||
}
|
||||
},
|
||||
read: (payload) => {
|
||||
if (payload.insSabun === global.user.sabun) {
|
||||
talkEvent.readMessage(payload.talkId)
|
||||
}
|
||||
},
|
||||
attachStart: async (payload) => {
|
||||
// 마스터 와 그외 탭간의 간격 조절
|
||||
// 파일소켓 구독
|
||||
payload.talkAttachDtos.forEach((att) => {
|
||||
att.uploading = payload.uploading;
|
||||
})
|
||||
talkEvent.fileSubscribe(payload);
|
||||
await Common.sleep(200)
|
||||
global.yourOldDate = '';
|
||||
global.mineOldDate = '';
|
||||
global.currDay = '';
|
||||
let incrementIdxFlag = true;
|
||||
let listening = 1;
|
||||
let sleepSecond = 10
|
||||
const master = Common.isMaster();
|
||||
|
||||
/*현재 톡방이 첨부파일 메시지 와 동일한 방이면 메시지 맵핑*/
|
||||
if(master) {
|
||||
if ((localStorage.getItem(global.tabId) === payload.talkId && document.visibilityState === 'hidden') || (localStorage.getItem(global.tabId) === payload.talkId && global.shadowRoot.querySelector('.chat-floating').classList.contains('cus-hide'))) {
|
||||
listening = 3
|
||||
} else if (localStorage.getItem(global.tabId) === payload.talkId) {
|
||||
listening = 2
|
||||
}
|
||||
|
||||
// 우선순위가 되는 상태 간격 조절
|
||||
Common.setListening(payload.talkId, listening);
|
||||
} else {
|
||||
await Common.sleep(100)
|
||||
}
|
||||
|
||||
/*메시지 호출이 안된 상태이면 제외 첫 접근도 안된 상태로 메시지 맵핑 필요없음*/
|
||||
const cacheTalk = global.talkData.filter((talk) => talk.talkId === payload.talkId)[0];
|
||||
if (cacheTalk.talkMessages !== undefined) {
|
||||
messageMapping(payload.talkId, {talks: [payload]}, 'after', false)
|
||||
// if (localStorage.getItem(global.tabId) === payload.talkId) {
|
||||
// const uploadWrap = global.shadowRoot.querySelector(`div[data-key="${payload.attach.key}"]`);
|
||||
// const messageWrap = uploadWrap.querySelector('.chat-message');
|
||||
// messageWrap.style.whiteSpace = 'nowrap';
|
||||
//
|
||||
// /* 다운로드 시작시 배경 페이드 활성화 */
|
||||
// const imageFade = uploadWrap.querySelector('.file-fade')
|
||||
// imageFade.classList.remove('cus-hide');
|
||||
// /*다운로드 버튼 비활성화*/
|
||||
// const downloadBtn = uploadWrap.querySelector('.chat-gallery-option')
|
||||
// downloadBtn.classList.remove('cus-show')
|
||||
// downloadBtn.classList.add('cus-hide')
|
||||
// const chatFile = uploadWrap.querySelector('.chat-file')
|
||||
// if (chatFile) {
|
||||
// /*프로그레스 설정*/
|
||||
// chatFile.insertAdjacentHTML('beforeend', `<div class="progress" style="margin-top:5px; margin-bottom:5px;">
|
||||
// <div class="progress-bar bg-low-purple" role="progressbar" style="width:0%;" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100">0%</div>
|
||||
// </div>
|
||||
// `)
|
||||
// } else {
|
||||
// messageWrap.insertAdjacentHTML('beforeend', `<div class="progress" style="margin-top:5px; margin-bottom:5px;">
|
||||
// <div class="progress-bar bg-low-purple" role="progressbar" style="width:0%;" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100">0%</div>
|
||||
// </div>
|
||||
// `)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
톡방에 입장중이면 썸네일 메시지 전송
|
||||
입장중이 아니면 톡방 목록에 배지 카운트 추가및 메시지 메시지 변경
|
||||
* */
|
||||
listening = Common.getListening(payload.talkId);
|
||||
if (listening === 1 || global.shadowRoot.querySelector('.chat-floating').classList.contains('cus-hide')) {
|
||||
if (master) {
|
||||
showNotification({
|
||||
workNm: payload.workNm,
|
||||
talkId: payload.talkId,
|
||||
message: payload.message,
|
||||
insName: payload.insName,
|
||||
});
|
||||
}
|
||||
// if (global.user.sabun !== payload.insSabun) {
|
||||
// const chatBadge = global.shadowRoot.querySelector('.unread.badge');
|
||||
// // let badgeCount = chatBadge.innerText;
|
||||
// // badgeCnt = parseInt(badgeCount) + 1;
|
||||
// }
|
||||
} else {
|
||||
// 메인탭에서만 작동 하도록 조치
|
||||
if (master) {
|
||||
talkEvent.readMessage(payload.talkId)
|
||||
}
|
||||
|
||||
// 기타 탭으로 메시지 읽음 전송
|
||||
if (payload.insSabun !== global.user.sabun) {
|
||||
if (master) {
|
||||
setChatRead(payload.talkId)
|
||||
}
|
||||
HubEvent.send(global.talkParams({
|
||||
type: "READ",
|
||||
talkId: payload.talkId,
|
||||
insSabun: global.user.sabun
|
||||
}))
|
||||
}
|
||||
|
||||
if (master && !document.hasFocus()) {
|
||||
showNotification({
|
||||
workNm: payload.workNm,
|
||||
talkId: payload.talkId,
|
||||
message: payload.message,
|
||||
insName: payload.insName,
|
||||
});
|
||||
}
|
||||
|
||||
// 스크롤 옵저버가 꺼져 있을시 메시지 팝업으로 활성화
|
||||
if (!global.scrollObserveFlag && payload.talkId === localStorage.getItem(global.tabId) && payload.insSabun !== global.user.sabun) {
|
||||
talkEvent.preMessageShow(payload.message)
|
||||
}
|
||||
|
||||
incrementIdxFlag = false;
|
||||
}
|
||||
|
||||
|
||||
// 톡방 정보에 정보 주입
|
||||
global.talkData.forEach((talk, idx) => {
|
||||
if (talk.talkId === payload.talkId) {
|
||||
talk.unReadCnt = incrementIdxFlag ? talk.unReadCnt + 1 : 0;
|
||||
talk.lastMessage = payload.message;
|
||||
talk.lastMessageDate = payload.insDate;
|
||||
global.talkData.splice(idx, 1)
|
||||
global.talkData = [talk, ...global.talkData]
|
||||
}
|
||||
})
|
||||
|
||||
// 톡방 재정의
|
||||
talkMapping(global.talkData)
|
||||
|
||||
// 메인 배지 재정의
|
||||
talkEvent.badgeChange()
|
||||
},
|
||||
attachProgress: (payload) => {
|
||||
if (localStorage.getItem(global.tabId) === payload.talkId) {
|
||||
const uploadWrap = global.shadowRoot.querySelector(`div[data-ins-date="${payload.insDate}"]`);
|
||||
const progressBar = uploadWrap.querySelector('.progress-bar')
|
||||
if (progressBar) {
|
||||
if(Number(progressBar.style.width.replace('%', '')) < Number(payload.attach.percent.replace('%', ''))) {
|
||||
progressBar.style.width = payload.attach.percent;
|
||||
progressBar.innerText = payload.attach.percent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
attachEnd: (payload) => {
|
||||
/*파일 구독 삭제*/
|
||||
if (Common.isMaster()) {
|
||||
console.log('attachEnd : ' + JSON.stringify(global.fileSubscribeList[payload.talkId]))
|
||||
global.fileSubscribeList[payload.talkId].unsubscribe()
|
||||
delete global.fileSubscribeList[payload.talkId];
|
||||
}
|
||||
|
||||
|
||||
const cacheTalk = global.talkData.filter((talk) => talk.talkId === payload.talkId)[0];
|
||||
if (cacheTalk.talkMessages !== undefined) {
|
||||
|
||||
cacheTalk.talkMessages.talks.forEach((talks) => {
|
||||
if(talks.insDate === payload.insDate) {
|
||||
talks.talkAttachDtos.forEach((att) => {
|
||||
att.uploading = false;
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
console.log(cacheTalk)
|
||||
if (localStorage.getItem(global.tabId) === payload.talkId) {
|
||||
messageMapping(payload.talkId, cacheTalk.talkMessages, 'before', true)
|
||||
}
|
||||
},
|
||||
otherInit: (payload) => {
|
||||
global.talkData = payload.talkData
|
||||
console.log(global)
|
||||
getTalkListEvent();
|
||||
},
|
||||
messageLine: (payload) => {
|
||||
const cacheTalk = global.talkData.filter((talk) => talk.talkId === payload.talkId)[0];
|
||||
cacheTalk.talkMessages = JSON.parse(payload.talkMessages);
|
||||
cacheTalk.messageParams = JSON.parse(payload.messageParams)
|
||||
console.log(`receiver MESSAGE_LINE : `, cacheTalk)
|
||||
if (localStorage.getItem(global.tabId) === payload.talkId) {
|
||||
console.log('messageMapping')
|
||||
messageMapping(payload.talkId, cacheTalk.talkMessages, 'before', true)
|
||||
if (cacheTalk.talkMessages.length !== cacheTalk.talkMessages.total) {
|
||||
setTimeout(() => {
|
||||
global.scrollMoreFlag = true;
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
moreMessageLine: (payload) => {
|
||||
const cacheTalk = global.talkData.filter((talk) => talk.talkId === payload.talkId)[0];
|
||||
cacheTalk.talkMessages.talks = [...payload.talkMessages.talks, ...cacheTalk.talkMessages.talks];
|
||||
console.log(`receiver MORE_MESSAGE_LINE : `, cacheTalk)
|
||||
if (localStorage.getItem(global.tabId) === payload.talkId) {
|
||||
console.log('messageMapping', global.oldScrollPosition)
|
||||
messageMapping(payload.talkId, cacheTalk.talkMessages, 'before', true)
|
||||
talkEvent.messageScrollOriginalPositionMove();
|
||||
|
||||
if (payload.result === 'success') {
|
||||
global.scrollMoreFlag = true;
|
||||
} else if (payload.result === 'last') {
|
||||
global.scrollMoreFlag = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
talkRefresh: (payload) => {
|
||||
global.talkData = JSON.parse(payload.talkData)
|
||||
console.log('talkRefresh', global.talkData)
|
||||
},
|
||||
changeLoginInfo: (payload) => {
|
||||
console.log('work_count', payload.notStart)
|
||||
const introOnline = global.shadowRoot.querySelector('#intro-online')
|
||||
if (payload.notStart < 5) {
|
||||
introOnline.style.setProperty('--after-content', `'지금은 바로 답변 받으실 수 있어요'`);
|
||||
introOnline.style.setProperty('--before-background', '#00d27a');
|
||||
} else if (payload.notStart < 10) {
|
||||
introOnline.style.setProperty('--after-content', `'현재 문의량이 많아 답변이 늦어져요. (대기시간 10분)'`);
|
||||
introOnline.style.setProperty('--before-background', '#fd7e14');
|
||||
} else if (payload.notStart >= 10) {
|
||||
introOnline.style.setProperty('--after-content', `'현재 문의량이 많아 답변이 늦어져요. (대기시간 30분)'`);
|
||||
introOnline.style.setProperty('--before-background', '#e63757');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default receiver;
|
||||
951
sample/dev/talk/js/module/render.js
Normal file
951
sample/dev/talk/js/module/render.js
Normal file
@@ -0,0 +1,951 @@
|
||||
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 {talkEvent} from "http://devtalk.kospo.co.kr:3000/static/js/module/talkEvent.js"
|
||||
import Common from "http://devtalk.kospo.co.kr:3000/static/js/utils.js";
|
||||
|
||||
// 화면 랜더링 관련 기능
|
||||
|
||||
const talkMapping = (chats) => {
|
||||
talkEvent.talkSort()
|
||||
const vnode = [];
|
||||
|
||||
const chatList = global.shadowRoot.querySelector('#chat-list');
|
||||
|
||||
if(chats.length === 0) {
|
||||
vnode.push(
|
||||
m('img', {class: 'chat-empty', src: `http://devtalk.kospo.co.kr:3000/static/image/empty.svg?color=${global.option.button.color.replace("#", '')}`})
|
||||
)
|
||||
} else {
|
||||
for (const ch of chats) {
|
||||
let smName = []
|
||||
let smSabun = []
|
||||
let unRead = '';
|
||||
let avatar = null;
|
||||
let onlineYn = false;
|
||||
/*문의자가 방은 만들었지만 메시지 작성이 없을시 제외*/
|
||||
if((ch.lastMessage !== null && ch.lastMessage !== '') || !ch.work.smUsers.map((sm) => sm.sabun).join(',').includes(global.user.sabun)) {
|
||||
ch.work.smUsers.forEach((sm) => {
|
||||
ch.talkMembers.forEach((cm) => {
|
||||
if(sm.sabun === cm.sabun) {
|
||||
smName.push(sm.name);
|
||||
smSabun.push(sm.sabun);
|
||||
if(cm.onlineYn && global.user.sabun !== sm.sabun) {
|
||||
onlineYn = true;
|
||||
|
||||
}
|
||||
} else {
|
||||
if(global.user.sabun === sm.sabun) {
|
||||
if(cm.onlineYn && cm.sabun !== global.user.sabun) {
|
||||
if(!onlineYn) {
|
||||
onlineYn = cm.onlineYn
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if (smName.length === 0 && smSabun.length === 0) {
|
||||
ch.talkMembers.forEach((cm) => {
|
||||
if (cm.sabun !== global.user.sabun) {
|
||||
smName.push(cm.name);
|
||||
smSabun.push(cm.sabun);
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if(ch.unReadCnt !== 0) {
|
||||
unRead = m('div', {class:'chat-list-badge bg-danger'}, ch.unReadCnt);
|
||||
}
|
||||
|
||||
if(ch.work.workLogo === null) {
|
||||
avatar = m('div', {class:`avatar avatar-xl ${ch.closeYn ? 'status-do-not-disturb' : (onlineYn ? 'status-online' : 'status-offline')}`}, [
|
||||
m('div', {class:'rounded-square chat-room-avatar'}, ch.insSabun === global.user.sabun ? `${ch.work.workNm.substring(0, 1)}` : ch.insName.substring(0, 1))
|
||||
])
|
||||
} else {
|
||||
avatar = m('div', {class:`avatar avatar-xl ${ch.closeYn ? 'status-do-not-disturb' : (onlineYn ? 'status-online' : 'status-offline')}`}, [
|
||||
m('img', {src: `${global.apiUrl}/logo/${ch.work.workLogo.vnm}`, class:`avatar avatar-xl ${ch.closeYn ? 'status-do-not-disturb' : (onlineYn ? 'status-online' : 'status-offline')}`})
|
||||
])
|
||||
}
|
||||
|
||||
vnode.push(m('div', {class:`cus-show hover-actions-trigger chat-wrap nav-item ${ch.unReadCnt === 0 ? '' : 'unread-message'} ${ch.closeYn ? 'chat-close' : ''}`, role:"tab", 'data-bs-toggle':"tab", 'data-bs-target':"#chat-0", 'aria-controls':"chat-0",
|
||||
'aria-selected':"true", 'data-work-id':`${ch.work.workId}`, 'data-work-nm':`${ch.work.workNm}`, 'data-target-sabun': `${ch.insSabun}`, 'data-work-sm-nm':`${smName.join(',')}`, 'data-work-sm-sabun' : `${smSabun.join(',')}`, 'data-talk-id':`${ch.talkId}`, 'data-comp-yn':`${ch.compYn}`, 'data-close-yn':`${ch.closeYn}`, 'data-rating-Yn':`${ch.ratingYn}`}, [
|
||||
m('div', {class:"d-lg-block"}, [
|
||||
m('div', {class:"dropdown dropdown-active-trigger dropdown-chat"}, [
|
||||
m('button', {class:"bg-transparent chat-options hover-actions btn btn-link btn-sm text-400 dropdown-caret-none dropdown-toggle end-0 fs-9 mt-3 me-1 z-1 pb-2 mb-n2", type:"button", 'data-boundary':"viewport",'data-bs-auto-close': 'true', 'data-bs-toggle':"dropdown", 'aria-haspopup':'true', 'aria-expanded':'false'}, [
|
||||
m('i', {class: 'bi bi-gear-fill bi-color-grey'})
|
||||
]),
|
||||
m('div', {class:'dropdown-menu dropdown-menu-end border py-2 rounded-3'}, [
|
||||
m('button', {class:'dropdown-item chat-mark bg-transparent'}, '보관'),
|
||||
m('div', {class:'dropdown-divider'}),
|
||||
m('button', {class:'dropdown-item chat-delete text-danger bg-transparent'}, '삭제')
|
||||
])
|
||||
])
|
||||
]),
|
||||
m('div', {class:'d-flex p-3', role:'button'}, [
|
||||
avatar,
|
||||
m('div', {class:'flex-1 chat-wrap-body ms-3 d-lg-block'}, [
|
||||
m('div', {class: 'd-flex justify-content-between', style:'padding-bottom:3px;'}, [
|
||||
m('h6', {class:`mb-0 chat-wrap-title ${smName.length === 1 ? '': 'cus-tooltip'} ${ch.markYn ? 'pin': ''}`, 'data-tooltip':`${smName.join(',')}`}, global.user.sabun === ch.insSabun ? `${ch.work.workNm} (${smName.length === 1 ? smName[0] : smName[0]+'외 '+ (smName.length -1)+'명'})` : `${ch.insName}(${ch.insSabun})`),
|
||||
m('span', {class:'message-time'}, Common.talkListDateFormat(ch.lastMessageDate))
|
||||
]),
|
||||
m('div', {class:'min-w-0 max-h-15'}, [
|
||||
m('div', {class:'chat-wrap-content pe-3'}, Common.nullCheck(ch.lastMessage)),
|
||||
m('div', {class:'position-absolute bottom-0 end-0 hover-hide'}),
|
||||
unRead
|
||||
])
|
||||
])
|
||||
]),
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m.render(chatList, []);
|
||||
m.render(chatList, vnode);
|
||||
}
|
||||
|
||||
/*정보메시지 전송*/
|
||||
const infoMessage = (msg) => {
|
||||
return m('div', {class: 'd-flex p-2 pb-3 chat-info'}, [
|
||||
m('div', {class:'flex-1 px-3'}, [
|
||||
m('div', {class:'v-100'}, [
|
||||
m('div', {class: 'hover-actions-trigger'}, [
|
||||
m('div', {class:'bg-info text-white p-2 rounded-3 w-100 text-center text-pre-wrap', 'data-bs-theme': 'light'}, msg)
|
||||
])
|
||||
])
|
||||
])
|
||||
])
|
||||
}
|
||||
|
||||
const systemMessage = (msg) => {
|
||||
const html = [];
|
||||
html.push(`<div class="d-flex p-2 pb-3 system-message">`);
|
||||
html.push(`<div class="flex-1 px-3">`)
|
||||
html.push(`<div class="v-100">`)
|
||||
html.push(`<div class="hover-actions-trigger">`)
|
||||
html.push(`<div class="bg-info text-white p-2 rounded-2 w-100 text-center text-pre-wrap" data-bs-theme="light">`)
|
||||
|
||||
html.push(msg);
|
||||
html.push(`</div>`);
|
||||
html.push(`</div>`);
|
||||
html.push(`</div>`);
|
||||
html.push(`</div>`);
|
||||
html.push(`</div>`);
|
||||
const chatContent = global.shadowRoot.querySelector('#chat-content');
|
||||
const content = chatContent.querySelector('.simplebar-content');
|
||||
content.insertAdjacentHTML('beforeend', html.join(''))
|
||||
}
|
||||
|
||||
|
||||
/*채팅 날짜 엘리먼트 생성*/
|
||||
const dateMessage = (date) => {
|
||||
return m('div', {class: 'text-center fs-10 text-500 chat-content-date p-2'}, [
|
||||
m('span', date)
|
||||
])
|
||||
}
|
||||
|
||||
/*이미지 미리보기 */
|
||||
const setImageModal = (param) => {
|
||||
console.log(1)
|
||||
const cca = JSON.parse(param);
|
||||
// global.shadowRoot.querySelector('.modal-title').innerText = decodeURI(cca.onm)
|
||||
const modalBody = global.shadowRoot.querySelector('.modal-body');
|
||||
|
||||
const img = m('img', {'src': `${global.apiUrl}/api/message/image?${new URLSearchParams(cca)}`, class: 'w-100'})
|
||||
m.render(modalBody, img)
|
||||
}
|
||||
|
||||
const messageMapping = (talkId, dto, position, reset) => {
|
||||
let prevTalkId = '';
|
||||
let vNode = []
|
||||
global.currDay = '';
|
||||
const chatContent = global.shadowRoot.querySelector('#chat-content');
|
||||
const content = chatContent.querySelector('.simplebar-content');
|
||||
const cacheTalk = global.talkData.filter((talk) => talk.talkId === talkId)[0];
|
||||
|
||||
// 톡방 메시지가 존재 하지 않을시 강제 주입
|
||||
if(cacheTalk.talkMessages.talks === undefined || cacheTalk.talkMessages.talks === null) {
|
||||
cacheTalk.talkMessages = dto;
|
||||
} else {
|
||||
if(reset) {
|
||||
cacheTalk.talkMessages = dto;
|
||||
} else {
|
||||
if(position === 'after') cacheTalk.talkMessages.talks = [...cacheTalk.talkMessages.talks, ...dto.talks];
|
||||
else if(position === 'before') cacheTalk.talkMessages.talks = [...dto.talks, ...cacheTalk.talkMessages.talks];
|
||||
cacheTalk.talkMessages.total = cacheTalk.talkMessages.total + dto.talks.length;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(localStorage.getItem(global.tabId) === talkId) {
|
||||
for (const idx in cacheTalk.talkMessages.talks) {
|
||||
try {
|
||||
const msg = cacheTalk.talkMessages.talks[idx];
|
||||
const nextMsg = cacheTalk.talkMessages.talks[parseInt(idx) +1];
|
||||
const newDate = Common.dayOfWeek(msg.insDate.substring(0, 10));
|
||||
|
||||
if (global.currDay !== newDate) {
|
||||
vNode.push(...[dateMessage(newDate)])
|
||||
global.currDay = newDate
|
||||
}
|
||||
|
||||
const chatDate = Common.talkDateFormat(msg.insDate);
|
||||
let nextDate = nextMsg === undefined ? '' : (nextMsg.insSabun === 'info' ? "" : Common.talkDateFormat(nextMsg.insDate));
|
||||
|
||||
if(msg.insSabun === 'info') {
|
||||
vNode.push(...[infoMessage(msg.message)])
|
||||
} else if (global.user.sabun === msg.insSabun) {
|
||||
/*이전 대화내용이 본인이 아니고 채팅 날짜 일때는 강제 입력 날짜 주입*/
|
||||
let force = false;
|
||||
try {
|
||||
force = (vNode[vNode.length - 1].attrs.className.includes('your') ||
|
||||
vNode[vNode.length - 1].attrs.className.includes('chat-content-date') ||
|
||||
vNode[vNode.length - 1].attrs.className.includes('chat-info')) && !nextMsg;
|
||||
if(!force) {
|
||||
force = global.user.sabun !== (nextMsg === undefined ? '' : nextMsg.insSabun);
|
||||
}
|
||||
} catch(e) {
|
||||
force = true;
|
||||
}
|
||||
vNode.push(...mineMessage(msg, chatDate, nextDate, force))
|
||||
} else {
|
||||
/*이전 대화내용이 본인이 아니고 채팅 날짜 일때는 강제 입력 날짜 주입*/
|
||||
let force = false;
|
||||
try {
|
||||
/*다음 메시지가 존재 하지 않으면 이전 글이 상대발 글일시 날짜 주입*/
|
||||
force = (vNode[vNode.length - 1].attrs.className.includes('mine') ||
|
||||
vNode[vNode.length - 1].attrs.className.includes('chat-content-date') ||
|
||||
vNode[vNode.length - 1].attrs.className.includes('chat-info')) && !nextMsg;
|
||||
if(!force) {
|
||||
force = global.user.sabun === (nextMsg === undefined ? '' : nextMsg.insSabun);
|
||||
}
|
||||
} catch(e) {
|
||||
force = true;
|
||||
}
|
||||
vNode.push(...yourMessage(msg, vNode[vNode.length - 1], chatDate, nextDate, force))
|
||||
}
|
||||
prevTalkId = msg.talkId;
|
||||
} catch(e) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
m.render(content, vNode)
|
||||
}
|
||||
}
|
||||
|
||||
const mineFileMessage = (msg, date, nextDate, force) => {
|
||||
const talkAttachDtos = msg.talkAttachDtos;
|
||||
const key = msg.attach == null ? '' : msg.attach.key;
|
||||
let downloadFlag = true;
|
||||
const downloadParam = {
|
||||
talkId : msg.talkId,
|
||||
sabun : msg.insSabun,
|
||||
insDate : msg.insDate,
|
||||
}
|
||||
|
||||
let delYn = false;
|
||||
let onm = '';
|
||||
let size = 0;
|
||||
let fileIcon = '';
|
||||
talkAttachDtos.forEach((cc) => {
|
||||
size = size + cc.size
|
||||
if(cc.delYn) delYn = true;
|
||||
if(cc.uploading) downloadFlag = false;
|
||||
});
|
||||
|
||||
if(talkAttachDtos.length === 1) {
|
||||
fileIcon = 'bi-file-earmark-text'
|
||||
onm = talkAttachDtos[0].onm;
|
||||
} else {
|
||||
fileIcon = 'bi-file-earmark-zip'
|
||||
onm = `${talkAttachDtos[0].onm} 외 ${talkAttachDtos.length - 1}.zip`;
|
||||
}
|
||||
size = Math.round(size / 1024);
|
||||
|
||||
let tailFlag = false;
|
||||
let insDate;
|
||||
|
||||
if(force) {
|
||||
insDate = m('div', {class: 'text-400 fs-11 chat-date text-end mr-10'}, [
|
||||
m('span', `${date}`)
|
||||
])
|
||||
} else {
|
||||
if(date !== nextDate) {
|
||||
insDate = m('div', {class: 'text-400 fs-11 chat-date text-end mr-10'}, [
|
||||
m('span', `${date}`)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
if(global.mineOldDate !== date) {
|
||||
tailFlag = true;
|
||||
global.mineOldDate = date
|
||||
}
|
||||
|
||||
return m('div', {class: 'd-flex p-1 chat-text-wrapper mine cus-show', 'data-key': key, 'data-ins-date' : msg.insDate}, [
|
||||
m('div', {class: 'flex-1 d-flex justify-content-end'}, [
|
||||
m('div', {class: 'w-max-85'}, [
|
||||
m('div', {class: 'hover-actions-trigger d-flex flex-end-center'}, [
|
||||
m('ul', {class: 'position-relative list-inline mb-0 text-400 me-2 chat-gallery-option mr-10'}, [
|
||||
delYn ? '' :
|
||||
m('li', {class: 'list-inline-item'}, [
|
||||
downloadFlag ?
|
||||
m('a', {
|
||||
class: 'chat-option',
|
||||
href: `${global.apiUrl}/api/message/download?${new URLSearchParams(downloadParam).toString()}`,
|
||||
target: '_download_',
|
||||
rel: 'noopener',
|
||||
'ata-bs-toggle': "tooltip",
|
||||
'data-bs-placement': "top",
|
||||
'aria-label': "Forward",
|
||||
'data-bs-original-title': "Forward"
|
||||
}, [
|
||||
m('i', {class: 'bi bi-download text-black', style: 'font-size: 12px;'})
|
||||
]) : ''
|
||||
])
|
||||
]),
|
||||
m('div', {'search-yn': `${msg.searchYn}`, class: `chat-content-custom-font-size chat-message bg-light p-2 rounded-3 mr-10 chat-file ${tailFlag ? 'tail': ''}`, style: 'max-width:250px;'}, [
|
||||
delYn ? m('div', {class: 'attach-del'}) : '',
|
||||
m('div', {class: 'file-fade cus-hide'}),
|
||||
m('div', {class: 'd-flex justify-content-between'}, [
|
||||
m('div', {}, [
|
||||
m('i', {class: `bi ${fileIcon}`, style: 'color:#505050;font-size:30px;padding-right:10px;'}),
|
||||
]),
|
||||
m('div', {class: 'attach-file'}, [
|
||||
m('div', {class: 'fs-xxl-10 pb-1 attach-file'}, onm),
|
||||
m('div', {class: 'fs-xxl-11'},`용량 : ${size}kb`),
|
||||
])
|
||||
]),
|
||||
downloadFlag ? '': m('div', {class: 'progress', style: 'margin-top:5px; margin-bottom:5px;'}, [
|
||||
m('div', {class:'progress-bar bg-low-purple', role: 'progressbar', style: 'width:0%;', 'aria-valuenow': "100",'aria-valuemin':"0", 'aria-valuemax':"100" }, '0%')
|
||||
])
|
||||
]),
|
||||
]),
|
||||
insDate
|
||||
])
|
||||
])
|
||||
])
|
||||
}
|
||||
|
||||
const mineManualMessage = (msg, date, manualParam, nextDate, force) => {
|
||||
const downloadParam = {
|
||||
vnm : manualParam.vnm,
|
||||
workId : manualParam.workId,
|
||||
}
|
||||
|
||||
const fileIcon = 'bi-file-earmark-text'
|
||||
const size = Math.round(parseInt(manualParam.size) / 1024);
|
||||
let tailFlag = false;
|
||||
let insDate;
|
||||
|
||||
if(force) {
|
||||
insDate = m('div', {class: 'text-400 fs-11 chat-date text-end mr-10'}, [
|
||||
m('span', `${date}`)
|
||||
])
|
||||
} else {
|
||||
if(date !== nextDate) {
|
||||
insDate = m('div', {class: 'text-400 fs-11 chat-date text-end mr-10'}, [
|
||||
m('span', `${date}`)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
if(global.mineOldDate !== date) {
|
||||
tailFlag = true;
|
||||
global.mineOldDate = date
|
||||
}
|
||||
|
||||
return m('div', {class: 'd-flex p-1 chat-text-wrapper mine cus-show'}, [
|
||||
m('div', {class: 'flex-1 d-flex justify-content-end'}, [
|
||||
m('div', {class: 'w-max-85'}, [
|
||||
m('div', {class: 'hover-actions-trigger d-flex flex-end-center'}, [
|
||||
m('ul', {class: 'position-relative list-inline mb-0 text-400 me-2 chat-gallery-option mr-10'}, [
|
||||
m('li', {class: 'list-inline-item'}, [
|
||||
m('a', {
|
||||
class: 'chat-option',
|
||||
href: `${global.apiUrl}/api/manual/download/doc?${new URLSearchParams(downloadParam).toString()}`,
|
||||
target: '_download_',
|
||||
rel: 'noopener',
|
||||
'ata-bs-toggle': "tooltip",
|
||||
'data-bs-placement': "top",
|
||||
'aria-label': "Forward",
|
||||
'data-bs-original-title': "Forward"
|
||||
}, [
|
||||
m('i', {class: 'bi bi-download text-black', style: 'font-size: 12px;'})
|
||||
])
|
||||
])
|
||||
]),
|
||||
m('div', {'search-yn': `${msg.searchYn}`, class: `chat-content-custom-font-size chat-message bg-light p-2 rounded-3 mr-10 chat-file ${tailFlag ? 'tail': ''}`, style: 'max-width:250px;'}, [
|
||||
m('div', {class: 'file-fade cus-hide'}),
|
||||
m('div', {class: 'd-flex justify-content-between'}, [
|
||||
m('div', {}, [
|
||||
m('i', {class: `bi ${fileIcon}`, style: 'color:#505050;font-size:30px;padding-right:10px;'}),
|
||||
]),
|
||||
m('div', {class: 'attach-file'}, [
|
||||
m('div', {class: 'fs-xxl-10 pb-1 attach-file'}, manualParam.onm),
|
||||
m('div', {class: 'fs-xxl-11'},`용량 : ${size}kb`),
|
||||
])
|
||||
])
|
||||
]),
|
||||
]),
|
||||
insDate
|
||||
])
|
||||
])
|
||||
])
|
||||
}
|
||||
|
||||
/*자신에게 보이는 첨부파일 메시지 정의*/
|
||||
const mineImageMessage = (msg, date, nextDate, force) => {
|
||||
const talkAttachDtos = msg.talkAttachDtos;
|
||||
console.log(talkAttachDtos)
|
||||
const key = msg.attach == null ? '' : msg.attach.key;
|
||||
let downloadFlag = true;
|
||||
let attachList = []
|
||||
let delYn = false
|
||||
talkAttachDtos.forEach((cca, idx) => {
|
||||
if(cca.uploading) downloadFlag = false;
|
||||
if(cca.delYn) delYn = true;
|
||||
const imageParam = {
|
||||
talkId : msg.talkId,
|
||||
sabun : msg.insSabun,
|
||||
insDate : msg.insDate,
|
||||
onm : encodeURI(cca.onm),
|
||||
sort : cca.sort,
|
||||
size : 100
|
||||
}
|
||||
|
||||
const jsonStr = JSON.stringify(imageParam)
|
||||
const remain = talkAttachDtos.length % 3
|
||||
attachList.push(m('div', {onclick:function() {setImageModal(jsonStr)}, class:`col-${talkAttachDtos.length - remain > idx ? '4' : remain === 1 ? '12' : '6'} p-1`}, [
|
||||
m('div', {'data-bs-toggle':"modal", 'data-bs-target':"#imageModal", 'data-url' :new URLSearchParams(imageParam).toString(), class:'attach-a'}, [
|
||||
delYn ?
|
||||
m('img', {src: `http://devtalk.kospo.co.kr:3000/static/image/empty_img.png`, alt: '', class: 'img-fluid rounded attach-img mb-2'}) :
|
||||
m('img', {src: cca.data === null ? `${global.apiUrl}/api/message/thumb?${new URLSearchParams(imageParam).toString()}` : cca.data, alt: '', class: 'img-fluid rounded attach-img mb-2'})
|
||||
])
|
||||
]
|
||||
))
|
||||
})
|
||||
|
||||
const downloadParam = {
|
||||
talkId : msg.talkId,
|
||||
sabun : msg.insSabun,
|
||||
insDate : msg.insDate,
|
||||
}
|
||||
|
||||
/*
|
||||
tail bg-primary text-white p-2 rounded-3 chat-message mr-10 mine
|
||||
* */
|
||||
let tailFlag = false;
|
||||
let insDate;
|
||||
|
||||
if(force) {
|
||||
insDate = m('div', {class: 'text-400 fs-11 chat-date text-end mr-10'}, [
|
||||
m('span', `${date}`)
|
||||
])
|
||||
} else {
|
||||
if(date !== nextDate) {
|
||||
insDate = m('div', {class: 'text-400 fs-11 chat-date text-end mr-10'}, [
|
||||
m('span', `${date}`)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
if(global.mineOldDate !== date) {
|
||||
tailFlag = true;
|
||||
global.mineOldDate = date
|
||||
}
|
||||
|
||||
return m('div', {class: 'd-flex p-1 chat-text-wrapper mine cus-show', 'data-key': key, 'data-ins-date' : msg.insDate}, [
|
||||
m('div', {class: 'flex-1 d-flex justify-content-end'}, [
|
||||
m('div', {class: 'w-xxl-85'}, [
|
||||
m('div', {class: 'hover-actions-trigger d-flex flex-end-center'}, [
|
||||
delYn ? '' :
|
||||
m('ul', {class: 'position-relative list-inline mb-0 text-400 me-2 chat-gallery-option mr-10'}, [
|
||||
m('li', {class: 'list-inline-item'}, [
|
||||
downloadFlag ?
|
||||
m('a', {
|
||||
class: 'chat-option',
|
||||
href: `${global.apiUrl}/api/message/download?${new URLSearchParams(downloadParam).toString()}`,
|
||||
target: '_download_',
|
||||
rel: 'noopener',
|
||||
'ata-bs-toggle': "tooltip",
|
||||
'data-bs-placement': "top",
|
||||
'aria-label': "Forward",
|
||||
'data-bs-original-title': "Forward"
|
||||
}, [
|
||||
m('i', {class: 'bi bi-download text-black', style: 'font-size: 14px;'})
|
||||
]) : ''
|
||||
])
|
||||
]),
|
||||
m('div', {'search-yn': `${msg.searchYn}`, class: `chat-content-custom-font-size bg-primary text-white p-2 rounded-3 chat-message mr-10 mine chat-gallery ${tailFlag ? 'tail': ''}`}, [
|
||||
delYn ? m('div', {class: 'attach-del'}) : '',
|
||||
m('div', {class: 'file-fade cus-hide'}),
|
||||
m('div', {class: 'row mx-n1'}, attachList),
|
||||
downloadFlag ? '': m('div', {class: 'progress', style: 'margin-top:5px; margin-bottom:5px;'}, [
|
||||
m('div', {class:'progress-bar bg-low-purple', role: 'progressbar', style: 'width:0%;', 'aria-valuenow': "100",'aria-valuemin':"0", 'aria-valuemax':"100" }, '0%')
|
||||
])
|
||||
])
|
||||
])
|
||||
,insDate
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
/*자신에게 보이는 메시지 정의*/
|
||||
const mineMessage = (msg, date, nextDate, force) => {
|
||||
const wrapper = global.shadowRoot.querySelectorAll('.chat-text-wrapper');
|
||||
let merge = [];
|
||||
let attaches;
|
||||
|
||||
let insDate;
|
||||
let tailFlag = false;
|
||||
/*첨부파일 리스트가 없을시 강제 리스트 주입*/
|
||||
if(msg.talkAttachDtos === null || msg.talkAttachDtos === undefined) msg.talkAttachDtos = [];
|
||||
|
||||
/**/
|
||||
if(msg.message.includes('[매뉴얼]')) {
|
||||
const manualParam = JSON.parse(msg.message);
|
||||
return mineManualMessage(msg, date, manualParam, nextDate, force)
|
||||
} else if(msg.talkAttachDtos.length > 0) {
|
||||
let isImage = true;
|
||||
msg.talkAttachDtos.forEach((cca) => {
|
||||
if(!(cca.type.includes('image'))) {
|
||||
isImage = false;
|
||||
}
|
||||
})
|
||||
if(isImage) {
|
||||
attaches = mineImageMessage(msg, date, nextDate, force)
|
||||
} else {
|
||||
attaches = mineFileMessage(msg, date, nextDate, force)
|
||||
}
|
||||
merge = [
|
||||
attaches,
|
||||
];
|
||||
} else {
|
||||
|
||||
if(force) {
|
||||
insDate = m('div', {class: 'text-400 fs-11 chat-date text-end mr-10'}, [
|
||||
m('span', `${date}`)
|
||||
])
|
||||
} else {
|
||||
if(date !== nextDate) {
|
||||
insDate = m('div', {class: 'text-400 fs-11 chat-date text-end mr-10'}, [
|
||||
m('span', `${date}`)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(global.mineOldDate !== date) {
|
||||
tailFlag = true;
|
||||
global.mineOldDate = date;
|
||||
}
|
||||
|
||||
let message = msg.message
|
||||
let linkYn = false;
|
||||
const urlRegex = /(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/g
|
||||
const matches = message.match(urlRegex)
|
||||
if(matches !== null) linkYn = true;
|
||||
|
||||
if(linkYn) {
|
||||
if(message.includes('[ITSM]') || message.includes('[SCM]')) {
|
||||
message = m.trust(message.replace(matches[matches.length -1], `<div class="detail-wrapper"><div class="detail-btn btn-sm btn-info btn-color" data-href="${matches[matches.length -1]}">자세히보기</div></div>`));
|
||||
} else {
|
||||
message = m.trust(message.replace(matches[matches.length -1], `<a title="${matches[matches.length -1]}" class="text-ellipsis" target="_blank" href="${matches[matches.length -1]}">${matches[matches.length -1]}</a>`));
|
||||
}
|
||||
}
|
||||
|
||||
merge = [
|
||||
m('div', {class:'d-flex p-1 chat-text-wrapper mine cus-show'}, [
|
||||
m('div', {class: 'flex-1 d-flex justify-content-end'}, [
|
||||
m('div', {class: 'w-100'}, [
|
||||
m('div', {class: 'hover-actions-trigger d-flex flex-end-center'}, [
|
||||
m('div', {"data-line": `${msg.line}`, 'search-yn': `${msg.searchYn}`,class:`${tailFlag ? 'tail': '' } chat-content-custom-font-size bg-primary text-white p-2 rounded-3 chat-message mr-10 mine`, 'data-bs-theme':"light"}, message)
|
||||
])
|
||||
,insDate
|
||||
])
|
||||
])
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
return merge
|
||||
}
|
||||
|
||||
const yourFileMessage = (msg, prevMsg, avatar, date, nextDate, force) => {
|
||||
const talkAttachDtos = msg.talkAttachDtos;
|
||||
const key = msg.attach == null ? '' : msg.attach.key;
|
||||
let downloadFlag = true;
|
||||
const downloadParam = {
|
||||
talkId : msg.talkId,
|
||||
sabun : msg.insSabun,
|
||||
insDate : msg.insDate
|
||||
}
|
||||
|
||||
let onm = '';
|
||||
let size = 0;
|
||||
let fileIcon = '';
|
||||
let delYn = false;
|
||||
talkAttachDtos.forEach((cc) => {
|
||||
size = size + cc.size
|
||||
if(cc.delYn) delYn = true;
|
||||
if(cc.uploading) downloadFlag = false;
|
||||
});
|
||||
|
||||
if(talkAttachDtos.length === 1) {
|
||||
fileIcon = 'bi-file-earmark-text'
|
||||
onm = talkAttachDtos[0].onm;
|
||||
} else {
|
||||
fileIcon = 'bi-file-earmark-zip'
|
||||
onm = `${talkAttachDtos[0].onm} 외 ${talkAttachDtos.length - 1}.zip`;
|
||||
talkAttachDtos.forEach((cc) => size = size + cc.size);
|
||||
}
|
||||
size = Math.round(size / 1024);
|
||||
|
||||
let insAvatar;
|
||||
let insDate;
|
||||
let tailFlag = false;
|
||||
|
||||
if(force) {
|
||||
insDate = m('div', {class:'text-400 fs-11 chat-date ml-10'}, date);
|
||||
insAvatar = m('div', {class:"rounded-circle chat-avatar"}, avatar)
|
||||
} else {
|
||||
if(date !== nextDate) {
|
||||
insDate = m('div', {class:'text-400 fs-11 chat-date ml-10'}, date);
|
||||
}
|
||||
}
|
||||
|
||||
if(global.yourOldDate !== date || prevMsg.attrs.className.includes('mine')) {
|
||||
insAvatar = m('div', {class:"rounded-circle chat-avatar"}, avatar)
|
||||
tailFlag = true;
|
||||
global.yourOldDate = date
|
||||
}
|
||||
|
||||
return m('div', {class: 'd-flex p-1 chat-text-wrapper your cus-show', 'data-key': key, 'data-ins-date' : msg.insDate}, [
|
||||
m('div', {class: 'avatar avatar-l me-2'}, [
|
||||
insAvatar
|
||||
]),
|
||||
m('div', {class: 'flex-1 d-flex justify-content-start'}, [
|
||||
m('div', {class: 'w-max-85'}, [
|
||||
m('div', {class: 'hover-actions-trigger d-flex flex-between-normal'}, [
|
||||
m('div', {'search-yn': `${msg.searchYn}`, class: `chat-content-custom-font-size chat-message bg-light p-2 rounded-3 ml-10 your chat-file ${tailFlag ? 'tail': ''}`, style: 'max-width:220px;'}, [
|
||||
delYn ? m('div', {class: 'attach-del'}) : '',
|
||||
m('div', {class: 'file-fade cus-hide'}),
|
||||
m('div', {class: 'd-flex justify-content-start'}, [
|
||||
m('i', {class: `bi ${fileIcon}`, style: 'color:#505050;font-size:30px;padding-right:10px;'}),
|
||||
m('div', {class: ' attach-file'}, [
|
||||
m('div', {class: 'fs-xxl-10 pb-1 attach-file'}, onm),
|
||||
m('div', {class: 'fs-xxl-11'},`용량 : ${size}kb`),
|
||||
])
|
||||
]),
|
||||
downloadFlag ? '': m('div', {class: 'progress', style: 'margin-top:5px; margin-bottom:5px;'}, [
|
||||
m('div', {class:'progress-bar bg-low-purple', role: 'progressbar', style: 'width:0%;', 'aria-valuenow': "100",'aria-valuemin':"0", 'aria-valuemax':"100" }, '0%')
|
||||
])
|
||||
]),
|
||||
delYn ? '' :
|
||||
m('ul', {class: 'position-relative list-inline mb-0 text-400 me-2 chat-gallery-option ml-10'}, [
|
||||
m('li', {class: 'list-inline-item'}, [
|
||||
downloadFlag ?
|
||||
m('a', {
|
||||
class: 'chat-option',
|
||||
href: `${global.apiUrl}/api/message/download?${new URLSearchParams(downloadParam).toString()}`,
|
||||
target: '_download_',
|
||||
rel: 'noopener',
|
||||
'ata-bs-toggle': "tooltip",
|
||||
'data-bs-placement': "top",
|
||||
'aria-label': "Forward",
|
||||
'data-bs-original-title': "Forward"
|
||||
}, [
|
||||
m('i', {class: 'bi bi-download text-black', style: 'font-size: 14px;'})
|
||||
]): ''
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
insDate
|
||||
])
|
||||
])
|
||||
])
|
||||
}
|
||||
|
||||
const yourManualMessage = (msg, prevMsg, manualParam, avatar, date, nextDate, force) => {
|
||||
const downloadParam = {
|
||||
vnm : manualParam.vnm,
|
||||
workId : manualParam.workId,
|
||||
}
|
||||
|
||||
const fileIcon = 'bi-file-earmark-text'
|
||||
const size = Math.round(parseInt(manualParam.size) / 1024);
|
||||
|
||||
let insAvatar;
|
||||
let insDate;
|
||||
let tailFlag = false;
|
||||
|
||||
if(force) {
|
||||
insDate = m('div', {class:'text-400 fs-11 chat-date ml-10'}, date);
|
||||
insAvatar = m('div', {class:"rounded-circle chat-avatar"}, avatar)
|
||||
} else {
|
||||
if(date !== nextDate) {
|
||||
insDate = m('div', {class:'text-400 fs-11 chat-date ml-10'}, date);
|
||||
}
|
||||
}
|
||||
|
||||
if(global.yourOldDate !== date || prevMsg.attrs.className.includes('mine')) {
|
||||
insAvatar = m('div', {class:"rounded-circle chat-avatar"}, avatar)
|
||||
tailFlag = true;
|
||||
global.yourOldDate = date
|
||||
}
|
||||
|
||||
return m('div', {class: 'd-flex p-1 chat-text-wrapper your cus-show'}, [
|
||||
m('div', {class: 'avatar avatar-l me-2'}, [
|
||||
insAvatar
|
||||
]),
|
||||
m('div', {class: 'flex-1 d-flex justify-content-start'}, [
|
||||
m('div', {class: 'w-max-85'}, [
|
||||
m('div', {class: 'hover-actions-trigger d-flex flex-between-normal'}, [
|
||||
m('div', {'search-yn': `${msg.searchYn}`, class: `chat-content-custom-font-size chat-message bg-light p-2 rounded-3 ml-10 your chat-file ${tailFlag ? 'tail': ''}`, style: 'max-width:220px;'}, [
|
||||
m('div', {class: 'file-fade cus-hide'}),
|
||||
m('div', {class: 'd-flex justify-content-start'}, [
|
||||
m('i', {class: `bi ${fileIcon}`, style: 'color:#505050;font-size:30px;padding-right:10px;'}),
|
||||
m('div', {class: ' attach-file'}, [
|
||||
m('div', {class: 'fs-xxl-10 pb-1 attach-file'}, manualParam.onm),
|
||||
m('div', {class: 'fs-xxl-11'},`용량 : ${size}kb`),
|
||||
])
|
||||
])
|
||||
]),
|
||||
m('ul', {class: 'position-relative list-inline mb-0 text-400 me-2 chat-gallery-option ml-10'}, [
|
||||
m('li', {class: 'list-inline-item'}, [
|
||||
m('a', {
|
||||
class: 'chat-option',
|
||||
href: `${global.apiUrl}/api/manual/download/doc?${new URLSearchParams(downloadParam).toString()}`,
|
||||
target: '_download_',
|
||||
rel: 'noopener',
|
||||
'ata-bs-toggle': "tooltip",
|
||||
'data-bs-placement': "top",
|
||||
'aria-label': "Forward",
|
||||
'data-bs-original-title': "Forward"
|
||||
}, [
|
||||
m('i', {class: 'bi bi-download text-black', style: 'font-size: 14px;'})
|
||||
])
|
||||
])
|
||||
]),
|
||||
]),
|
||||
insDate
|
||||
])
|
||||
])
|
||||
])
|
||||
}
|
||||
|
||||
/*상대방에게 보이는 첨부파일 메시지 정의*/
|
||||
const yourImageMessage = (msg, prevMsg, avatar, date, nextDate, force) => {
|
||||
let attachList = []
|
||||
const talkAttachDtos = msg.talkAttachDtos;
|
||||
let downloadFlag = true;
|
||||
let delYn = false;
|
||||
const key = msg.attach == null ? '' : msg.attach.key;
|
||||
talkAttachDtos.forEach((cca, idx) => {
|
||||
if(cca.uploading) downloadFlag = false;
|
||||
if(cca.delYn) delYn = true;
|
||||
const imageParam = {
|
||||
talkId : msg.talkId,
|
||||
sabun : msg.insSabun,
|
||||
insDate : msg.insDate,
|
||||
onm : encodeURI(cca.onm),
|
||||
sort : cca.sort,
|
||||
size : 100
|
||||
}
|
||||
|
||||
const jsonStr = JSON.stringify(imageParam)
|
||||
const remain = talkAttachDtos.length % 3
|
||||
attachList.push(m('div', {onclick:function() {setImageModal(jsonStr)}, class:`col-${talkAttachDtos.length - remain > idx ? '4' : remain === 1 ? '12' : '6'} p-1`}, [
|
||||
m('div', {'data-bs-toggle':"modal", 'data-bs-target':"#imageModal", 'data-url' :new URLSearchParams(imageParam).toString(), class:'attach-a'}, [
|
||||
delYn ?
|
||||
m('img', {src: `http://devtalk.kospo.co.kr:3000/static/image/empty_img.png`, alt: '', class: 'img-fluid rounded attach-img mb-2'}) :
|
||||
m('img', {src: cca.data === null ? `${global.apiUrl}/api/message/thumb?${new URLSearchParams(imageParam).toString()}` : cca.data, alt: '', class: 'img-fluid attach-img rounded mb-2'})
|
||||
])
|
||||
]
|
||||
))
|
||||
|
||||
return attachList;
|
||||
})
|
||||
|
||||
const downloadParam = {
|
||||
talkId : talkAttachDtos[0].talkId,
|
||||
sabun : talkAttachDtos[0].sabun,
|
||||
insDate : talkAttachDtos[0].insDate,
|
||||
}
|
||||
|
||||
let insAvatar;
|
||||
let insDate;
|
||||
let tailFlag = false;
|
||||
|
||||
if(force) {
|
||||
insDate = m('div', {class:'text-400 fs-11 chat-date ml-10'}, date);
|
||||
insAvatar = m('div', {class:"rounded-circle chat-avatar"}, avatar)
|
||||
} else {
|
||||
if(date !== nextDate) {
|
||||
insDate = m('div', {class:'text-400 fs-11 chat-date ml-10'}, date);
|
||||
}
|
||||
}
|
||||
|
||||
if(global.yourOldDate !== date || prevMsg.attrs.className.includes('mine')) {
|
||||
insAvatar = m('div', {class:"rounded-circle chat-avatar"}, avatar)
|
||||
tailFlag = true;
|
||||
global.yourOldDate = date
|
||||
}
|
||||
|
||||
|
||||
return m('div', {class: 'd-flex p-1 chat-text-wrapper your cus-show', 'data-key': key, 'data-ins-date' : msg.insDate}, [
|
||||
m('div', {class: 'avatar avatar-l me-2'}, [
|
||||
insAvatar
|
||||
]),
|
||||
m('div', {class: 'flex-1 d-flex justify-content-start'}, [
|
||||
m('div', {class: 'w-xxl-85'}, [
|
||||
m('div', {class: 'hover-actions-trigger d-flex flex-between-normal'}, [
|
||||
m('div', { 'search-yn': `${msg.searchYn}`,class: `chat-content-custom-font-size chat-message bg-200 p-2 rounded-3 ml-10 your chat-gallery ${tailFlag ? 'tail' : ''}`}, [
|
||||
delYn ? m('div', {class: 'attach-del'}) : '',
|
||||
m('div', {class: 'file-fade cus-hide'}),
|
||||
m('div', {class: 'row mx-n1'}, attachList),
|
||||
downloadFlag ? '': m('div', {class: 'progress', style: 'margin-top:5px; margin-bottom:5px;'}, [
|
||||
m('div', {class:'progress-bar bg-low-purple', role: 'progressbar', style: 'width:0%;', 'aria-valuenow': "100",'aria-valuemin':"0", 'aria-valuemax':"100" }, '0%')
|
||||
])
|
||||
]),
|
||||
delYn ? '' :
|
||||
m('ul', {class: 'position-relative list-inline mb-0 text-400 me-2 chat-gallery-option ml-10'}, [
|
||||
m('li', {class: 'list-inline-item'}, [
|
||||
downloadFlag ?
|
||||
m('a', {
|
||||
class: 'chat-option',
|
||||
href: `${global.apiUrl}/api/message/download?${new URLSearchParams(downloadParam).toString()}`,
|
||||
'ata-bs-toggle': "tooltip",
|
||||
'data-bs-placement': "top",
|
||||
'aria-label': "Forward",
|
||||
'data-bs-original-title': "Forward"
|
||||
}, [
|
||||
m('i', {class: 'bi bi-download text-black', style: 'font-size: 14px;'})
|
||||
]) : ''
|
||||
])
|
||||
]),
|
||||
]),
|
||||
insDate
|
||||
])
|
||||
])
|
||||
])
|
||||
}
|
||||
|
||||
/*상대방에게 보이는 메시지 정의*/
|
||||
const yourMessage = (msg, pervMsg, date, nextDate, force) => {
|
||||
const avatar = msg.insName
|
||||
let attaches = [];
|
||||
|
||||
let insAvatar;
|
||||
let insDate;
|
||||
let tailFlag = false;
|
||||
|
||||
/*첨부파일 리스트가 없을시 강제 리스트 주입*/
|
||||
if(msg.talkAttachDtos === null || msg.talkAttachDtos === undefined) msg.talkAttachDtos = [];
|
||||
if(msg.message.includes('[매뉴얼]')) {
|
||||
const manualParam = JSON.parse(msg.message)
|
||||
return [yourManualMessage(msg, pervMsg, manualParam, avatar, date, nextDate, force)]
|
||||
} else if(msg.talkAttachDtos.length > 0) {
|
||||
let isImage = true;
|
||||
msg.talkAttachDtos.forEach((cca) => {
|
||||
if(!(cca.type.includes('image'))) {
|
||||
isImage = false;
|
||||
}
|
||||
})
|
||||
if(isImage) {
|
||||
attaches = yourImageMessage(msg, pervMsg, avatar, date, nextDate, force)
|
||||
} else {
|
||||
attaches = yourFileMessage(msg, pervMsg, avatar, date, nextDate, force);
|
||||
}
|
||||
|
||||
return [
|
||||
attaches
|
||||
]
|
||||
} else {
|
||||
|
||||
if(force) {
|
||||
insDate = m('div', {class:'text-400 fs-11 chat-date ml-10'}, date);
|
||||
insAvatar = m('div', {class:"rounded-circle chat-avatar"}, avatar)
|
||||
} else {
|
||||
if(date !== nextDate) {
|
||||
insDate = m('div', {class:'text-400 fs-11 chat-date ml-10'}, date);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if(global.yourOldDate !== date || pervMsg.attrs.className.includes('mine')) {
|
||||
insAvatar = m('div', {class:"rounded-circle chat-avatar"}, avatar)
|
||||
tailFlag = true;
|
||||
global.yourOldDate = date
|
||||
}
|
||||
} catch(e) {}
|
||||
|
||||
let message = msg.message
|
||||
let linkYn = false;
|
||||
const urlRegex = /(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/
|
||||
const matches = message.match(urlRegex)
|
||||
if(matches !== null) linkYn = true;
|
||||
|
||||
if(linkYn) {
|
||||
if(message.includes('[ITSM]') || message.includes('[SCM]')) {
|
||||
message = m.trust(message.replace(matches[0], `<div class="detail-wrapper"><div class="detail-btn btn-sm btn-info btn-color" data-href="${matches[0]}">자세히보기</div></div>`));
|
||||
} else {
|
||||
message = m.trust(message.replace(matches[0], `<a title="${matches[0]}" class="text-ellipsis" target="_blank" href="${matches[0]}">${matches[0]}</a>`));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return [
|
||||
m('div', {class:'d-flex p-1 chat-text-wrapper your cus-show'}, [
|
||||
m('div', {class: 'avatar avatar-l me-2'}, [
|
||||
insAvatar
|
||||
]),
|
||||
m('div', {class:'flex-1'}, [
|
||||
m('div', {class:'w-100'}, [
|
||||
m('div', {class:'hover-actions-trigger d-flex align-items-center'}, [
|
||||
m('div', {"data-line": `${msg.line}`, 'search-yn': `${msg.searchYn}`, class:`${tailFlag ? 'tail' : ''} chat-content-custom-font-size chat-message bg-200 p-2 rounded-3 ml-10 your`}, message)
|
||||
]),
|
||||
insDate
|
||||
])
|
||||
])
|
||||
])
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
return [
|
||||
attaches,
|
||||
m('div', {class:'d-flex p-1 chat-text-wrapper your'}, [
|
||||
m('div', {class: 'avatar avatar-l me-2'}, [
|
||||
insAvatar
|
||||
]),
|
||||
m('div', {class:'flex-1'}, [
|
||||
m('div', {class:'w-100'}, [
|
||||
m('div', {class:'hover-actions-trigger d-flex align-items-center'}, [
|
||||
m('div', {"data-line": `${msg.line}`, 'search-yn': `${msg.searchYn}`, class:`${tailFlag ? 'tail' : ''} chat-content-custom-font-size chat-message bg-200 p-2 rounded-3 ml-10 your`}, msg.message)
|
||||
]),
|
||||
insDate
|
||||
])
|
||||
])
|
||||
])
|
||||
]
|
||||
}
|
||||
|
||||
const findParentElement = function(node, parentClass) {
|
||||
let pa = node.parentElement;
|
||||
if(pa.classList.contains(parentClass)) {
|
||||
return pa;
|
||||
} else {
|
||||
return findParentElement(pa, parentClass)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export {infoMessage, systemMessage, dateMessage, talkMapping, messageMapping}
|
||||
5232
sample/dev/talk/js/module/sockjs.js
Normal file
5232
sample/dev/talk/js/module/sockjs.js
Normal file
File diff suppressed because it is too large
Load Diff
492
sample/dev/talk/js/module/stomp.js
Normal file
492
sample/dev/talk/js/module/stomp.js
Normal file
@@ -0,0 +1,492 @@
|
||||
// Generated by CoffeeScript 1.7.1
|
||||
|
||||
/*
|
||||
Stomp Over WebSocket http://www.jmesnil.net/stomp-websocket/doc/ | Apache License V2.0
|
||||
|
||||
Copyright (C) 2010-2013 [Jeff Mesnil](http://jmesnil.net/)
|
||||
Copyright (C) 2012 [FuseSource, Inc.](http://fusesource.com)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
var Byte, Client, Frame, Stomp,
|
||||
__hasProp = {}.hasOwnProperty,
|
||||
__slice = [].slice;
|
||||
|
||||
Byte = {
|
||||
LF: '\x0A',
|
||||
NULL: '\x00'
|
||||
};
|
||||
|
||||
Frame = (function () {
|
||||
var unmarshallSingle;
|
||||
|
||||
function Frame(command, headers, body) {
|
||||
this.command = command;
|
||||
this.headers = headers != null ? headers : {};
|
||||
this.body = body != null ? body : '';
|
||||
}
|
||||
|
||||
Frame.prototype.toString = function () {
|
||||
var lines, name, skipContentLength, value, _ref;
|
||||
lines = [this.command];
|
||||
skipContentLength = this.headers['content-length'] === false ? true : false;
|
||||
if (skipContentLength) {
|
||||
delete this.headers['content-length'];
|
||||
}
|
||||
_ref = this.headers;
|
||||
for (name in _ref) {
|
||||
if (!__hasProp.call(_ref, name)) continue;
|
||||
value = _ref[name];
|
||||
lines.push("" + name + ":" + value);
|
||||
}
|
||||
if (this.body && !skipContentLength) {
|
||||
lines.push("content-length:" + (Frame.sizeOfUTF8(this.body)));
|
||||
}
|
||||
lines.push(Byte.LF + this.body);
|
||||
return lines.join(Byte.LF);
|
||||
};
|
||||
|
||||
Frame.sizeOfUTF8 = function (s) {
|
||||
if (s) {
|
||||
return encodeURI(s).match(/%..|./g).length;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
unmarshallSingle = function (data) {
|
||||
var body, chr, command, divider, headerLines, headers, i, idx, len, line, start, trim, _i, _j, _len, _ref,
|
||||
_ref1;
|
||||
divider = data.search(RegExp("" + Byte.LF + Byte.LF));
|
||||
headerLines = data.substring(0, divider).split(Byte.LF);
|
||||
command = headerLines.shift();
|
||||
headers = {};
|
||||
trim = function (str) {
|
||||
return str.replace(/^\s+|\s+$/g, '');
|
||||
};
|
||||
_ref = headerLines.reverse();
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
line = _ref[_i];
|
||||
idx = line.indexOf(':');
|
||||
headers[trim(line.substring(0, idx))] = trim(line.substring(idx + 1));
|
||||
}
|
||||
body = '';
|
||||
start = divider + 2;
|
||||
if (headers['content-length']) {
|
||||
len = parseInt(headers['content-length']);
|
||||
body = ('' + data).substring(start, start + len);
|
||||
} else {
|
||||
chr = null;
|
||||
for (i = _j = start, _ref1 = data.length; start <= _ref1 ? _j < _ref1 : _j > _ref1; i = start <= _ref1 ? ++_j : --_j) {
|
||||
chr = data.charAt(i);
|
||||
if (chr === Byte.NULL) {
|
||||
break;
|
||||
}
|
||||
body += chr;
|
||||
}
|
||||
}
|
||||
return new Frame(command, headers, body);
|
||||
};
|
||||
|
||||
Frame.unmarshall = function (datas) {
|
||||
var data;
|
||||
return (function () {
|
||||
var _i, _len, _ref, _results;
|
||||
_ref = datas.split(RegExp("" + Byte.NULL + Byte.LF + "*"));
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
data = _ref[_i];
|
||||
if ((data != null ? data.length : void 0) > 0) {
|
||||
_results.push(unmarshallSingle(data));
|
||||
}
|
||||
}
|
||||
return _results;
|
||||
})();
|
||||
};
|
||||
|
||||
Frame.marshall = function (command, headers, body) {
|
||||
var frame;
|
||||
frame = new Frame(command, headers, body);
|
||||
return frame.toString() + Byte.NULL;
|
||||
};
|
||||
|
||||
return Frame;
|
||||
|
||||
})();
|
||||
|
||||
Client = (function () {
|
||||
var now;
|
||||
|
||||
function Client(ws) {
|
||||
this.ws = ws;
|
||||
this.ws.binaryType = "arraybuffer";
|
||||
this.counter = 0;
|
||||
this.connected = false;
|
||||
this.heartbeat = {
|
||||
outgoing: 10000,
|
||||
incoming: 10000
|
||||
};
|
||||
this.maxWebSocketFrameSize = 16 * 1024;
|
||||
this.subscriptions = {};
|
||||
}
|
||||
|
||||
Client.prototype.debug = function (message) {
|
||||
var _ref;
|
||||
return typeof window !== "undefined" && window !== null ? (_ref = window.console) != null ? _ref.log(message) : void 0 : void 0;
|
||||
};
|
||||
|
||||
now = function () {
|
||||
if (Date.now) {
|
||||
return Date.now();
|
||||
} else {
|
||||
return new Date().valueOf;
|
||||
}
|
||||
};
|
||||
|
||||
Client.prototype._transmit = function (command, headers, body) {
|
||||
var out;
|
||||
out = Frame.marshall(command, headers, body);
|
||||
if (typeof this.debug === "function") {
|
||||
this.debug(">>> " + out);
|
||||
}
|
||||
while (true) {
|
||||
if (out.length > this.maxWebSocketFrameSize) {
|
||||
this.ws.send(out.substring(0, this.maxWebSocketFrameSize));
|
||||
out = out.substring(this.maxWebSocketFrameSize);
|
||||
if (typeof this.debug === "function") {
|
||||
this.debug("remaining = " + out.length);
|
||||
}
|
||||
} else {
|
||||
return this.ws.send(out);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Client.prototype._setupHeartbeat = function (headers) {
|
||||
var serverIncoming, serverOutgoing, ttl, v, _ref, _ref1;
|
||||
if ((_ref = headers.version) !== Stomp.VERSIONS.V1_1 && _ref !== Stomp.VERSIONS.V1_2) {
|
||||
return;
|
||||
}
|
||||
_ref1 = (function () {
|
||||
var _i, _len, _ref1, _results;
|
||||
_ref1 = headers['heart-beat'].split(",");
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||||
v = _ref1[_i];
|
||||
_results.push(parseInt(v));
|
||||
}
|
||||
return _results;
|
||||
})(), serverOutgoing = _ref1[0], serverIncoming = _ref1[1];
|
||||
if (!(this.heartbeat.outgoing === 0 || serverIncoming === 0)) {
|
||||
ttl = Math.max(this.heartbeat.outgoing, serverIncoming);
|
||||
if (typeof this.debug === "function") {
|
||||
this.debug("send PING every " + ttl + "ms");
|
||||
}
|
||||
this.pinger = Stomp.setInterval(ttl, (function (_this) {
|
||||
return function () {
|
||||
_this.ws.send(Byte.LF);
|
||||
return typeof _this.debug === "function" ? _this.debug(">>> PING") : void 0;
|
||||
};
|
||||
})(this));
|
||||
}
|
||||
if (!(this.heartbeat.incoming === 0 || serverOutgoing === 0)) {
|
||||
ttl = Math.max(this.heartbeat.incoming, serverOutgoing);
|
||||
if (typeof this.debug === "function") {
|
||||
this.debug("check PONG every " + ttl + "ms");
|
||||
}
|
||||
return this.ponger = Stomp.setInterval(ttl, (function (_this) {
|
||||
return function () {
|
||||
var delta;
|
||||
delta = now() - _this.serverActivity;
|
||||
if (delta > ttl * 2) {
|
||||
if (typeof _this.debug === "function") {
|
||||
_this.debug("did not receive server activity for the last " + delta + "ms");
|
||||
}
|
||||
return _this.ws.close();
|
||||
}
|
||||
};
|
||||
})(this));
|
||||
}
|
||||
};
|
||||
|
||||
Client.prototype._parseConnect = function () {
|
||||
var args, connectCallback, errorCallback, headers;
|
||||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
||||
headers = {};
|
||||
switch (args.length) {
|
||||
case 2:
|
||||
headers = args[0], connectCallback = args[1];
|
||||
break;
|
||||
case 3:
|
||||
if (args[1] instanceof Function) {
|
||||
headers = args[0], connectCallback = args[1], errorCallback = args[2];
|
||||
} else {
|
||||
headers.login = args[0], headers.passcode = args[1], connectCallback = args[2];
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
headers.login = args[0], headers.passcode = args[1], connectCallback = args[2], errorCallback = args[3];
|
||||
break;
|
||||
default:
|
||||
headers.login = args[0], headers.passcode = args[1], connectCallback = args[2], errorCallback = args[3], headers.host = args[4];
|
||||
}
|
||||
return [headers, connectCallback, errorCallback];
|
||||
};
|
||||
|
||||
Client.prototype.connect = function () {
|
||||
var args, errorCallback, headers, out;
|
||||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
||||
out = this._parseConnect.apply(this, args);
|
||||
headers = out[0], this.connectCallback = out[1], errorCallback = out[2];
|
||||
if (typeof this.debug === "function") {
|
||||
this.debug("Opening Web Socket...");
|
||||
}
|
||||
this.ws.onmessage = (function (_this) {
|
||||
return function (evt) {
|
||||
var arr, c, client, data, frame, messageID, onreceive, subscription, _i, _len, _ref, _results;
|
||||
data = typeof ArrayBuffer !== 'undefined' && evt.data instanceof ArrayBuffer ? (arr = new Uint8Array(evt.data), typeof _this.debug === "function" ? _this.debug("--- got data length: " + arr.length) : void 0, ((function () {
|
||||
var _i, _len, _results;
|
||||
_results = [];
|
||||
for (_i = 0, _len = arr.length; _i < _len; _i++) {
|
||||
c = arr[_i];
|
||||
_results.push(String.fromCharCode(c));
|
||||
}
|
||||
return _results;
|
||||
})()).join('')) : evt.data;
|
||||
_this.serverActivity = now();
|
||||
if (data === Byte.LF) {
|
||||
if (typeof _this.debug === "function") {
|
||||
_this.debug("<<< PONG");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (typeof _this.debug === "function") {
|
||||
_this.debug("<<< " + data);
|
||||
}
|
||||
_ref = Frame.unmarshall(data);
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
frame = _ref[_i];
|
||||
switch (frame.command) {
|
||||
case "CONNECTED":
|
||||
if (typeof _this.debug === "function") {
|
||||
_this.debug("connected to server " + frame.headers.server);
|
||||
}
|
||||
_this.connected = true;
|
||||
_this._setupHeartbeat(frame.headers);
|
||||
_results.push(typeof _this.connectCallback === "function" ? _this.connectCallback(frame) : void 0);
|
||||
break;
|
||||
case "MESSAGE":
|
||||
subscription = frame.headers.subscription;
|
||||
onreceive = _this.subscriptions[subscription] || _this.onreceive;
|
||||
if (onreceive) {
|
||||
client = _this;
|
||||
messageID = frame.headers["message-id"];
|
||||
frame.ack = function (headers) {
|
||||
if (headers == null) {
|
||||
headers = {};
|
||||
}
|
||||
return client.ack(messageID, subscription, headers);
|
||||
};
|
||||
frame.nack = function (headers) {
|
||||
if (headers == null) {
|
||||
headers = {};
|
||||
}
|
||||
return client.nack(messageID, subscription, headers);
|
||||
};
|
||||
_results.push(onreceive(frame));
|
||||
} else {
|
||||
_results.push(typeof _this.debug === "function" ? _this.debug("Unhandled received MESSAGE: " + frame) : void 0);
|
||||
}
|
||||
break;
|
||||
case "RECEIPT":
|
||||
_results.push(typeof _this.onreceipt === "function" ? _this.onreceipt(frame) : void 0);
|
||||
break;
|
||||
case "ERROR":
|
||||
_results.push(typeof errorCallback === "function" ? errorCallback(frame) : void 0);
|
||||
break;
|
||||
default:
|
||||
_results.push(typeof _this.debug === "function" ? _this.debug("Unhandled frame: " + frame) : void 0);
|
||||
}
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
})(this);
|
||||
this.ws.onclose = (function (_this) {
|
||||
return function () {
|
||||
var msg;
|
||||
msg = "Whoops! Lost connection to " + _this.ws.url;
|
||||
if (typeof _this.debug === "function") {
|
||||
_this.debug(msg);
|
||||
}
|
||||
_this._cleanUp();
|
||||
return typeof errorCallback === "function" ? errorCallback(msg) : void 0;
|
||||
};
|
||||
})(this);
|
||||
return this.ws.onopen = (function (_this) {
|
||||
return function () {
|
||||
if (typeof _this.debug === "function") {
|
||||
_this.debug('Web Socket Opened...');
|
||||
}
|
||||
headers["accept-version"] = Stomp.VERSIONS.supportedVersions();
|
||||
headers["heart-beat"] = [_this.heartbeat.outgoing, _this.heartbeat.incoming].join(',');
|
||||
return _this._transmit("CONNECT", headers);
|
||||
};
|
||||
})(this);
|
||||
};
|
||||
|
||||
Client.prototype.disconnect = function (disconnectCallback, headers) {
|
||||
if (headers == null) {
|
||||
headers = {};
|
||||
}
|
||||
this._transmit("DISCONNECT", headers);
|
||||
this.ws.onclose = null;
|
||||
this.ws.close();
|
||||
this._cleanUp();
|
||||
return typeof disconnectCallback === "function" ? disconnectCallback() : void 0;
|
||||
};
|
||||
|
||||
Client.prototype._cleanUp = function () {
|
||||
this.connected = false;
|
||||
if (this.pinger) {
|
||||
Stomp.clearInterval(this.pinger);
|
||||
}
|
||||
if (this.ponger) {
|
||||
return Stomp.clearInterval(this.ponger);
|
||||
}
|
||||
};
|
||||
|
||||
Client.prototype.send = function (destination, headers, body) {
|
||||
if (headers == null) {
|
||||
headers = {};
|
||||
}
|
||||
if (body == null) {
|
||||
body = '';
|
||||
}
|
||||
headers.destination = destination;
|
||||
return this._transmit("SEND", headers, body);
|
||||
};
|
||||
|
||||
Client.prototype.subscribe = function (destination, callback, headers) {
|
||||
var client;
|
||||
if (headers == null) {
|
||||
headers = {};
|
||||
}
|
||||
if (!headers.id) {
|
||||
headers.id = "sub-" + this.counter++;
|
||||
}
|
||||
headers.destination = destination;
|
||||
this.subscriptions[headers.id] = callback;
|
||||
this._transmit("SUBSCRIBE", headers);
|
||||
client = this;
|
||||
return {
|
||||
id: headers.id,
|
||||
unsubscribe: function () {
|
||||
return client.unsubscribe(headers.id);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Client.prototype.unsubscribe = function (id) {
|
||||
delete this.subscriptions[id];
|
||||
return this._transmit("UNSUBSCRIBE", {
|
||||
id: id
|
||||
});
|
||||
};
|
||||
|
||||
Client.prototype.begin = function (transaction) {
|
||||
var client, txid;
|
||||
txid = transaction || "tx-" + this.counter++;
|
||||
this._transmit("BEGIN", {
|
||||
transaction: txid
|
||||
});
|
||||
client = this;
|
||||
return {
|
||||
id: txid,
|
||||
commit: function () {
|
||||
return client.commit(txid);
|
||||
},
|
||||
abort: function () {
|
||||
return client.abort(txid);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Client.prototype.commit = function (transaction) {
|
||||
return this._transmit("COMMIT", {
|
||||
transaction: transaction
|
||||
});
|
||||
};
|
||||
|
||||
Client.prototype.abort = function (transaction) {
|
||||
return this._transmit("ABORT", {
|
||||
transaction: transaction
|
||||
});
|
||||
};
|
||||
|
||||
Client.prototype.ack = function (messageID, subscription, headers) {
|
||||
if (headers == null) {
|
||||
headers = {};
|
||||
}
|
||||
headers["message-id"] = messageID;
|
||||
headers.subscription = subscription;
|
||||
return this._transmit("ACK", headers);
|
||||
};
|
||||
|
||||
Client.prototype.nack = function (messageID, subscription, headers) {
|
||||
if (headers == null) {
|
||||
headers = {};
|
||||
}
|
||||
headers["message-id"] = messageID;
|
||||
headers.subscription = subscription;
|
||||
return this._transmit("NACK", headers);
|
||||
};
|
||||
|
||||
return Client;
|
||||
|
||||
})();
|
||||
|
||||
Stomp = {
|
||||
VERSIONS: {
|
||||
V1_0: '1.0',
|
||||
V1_1: '1.1',
|
||||
V1_2: '1.2',
|
||||
supportedVersions: function () {
|
||||
return '1.1,1.0';
|
||||
}
|
||||
},
|
||||
client: function (url, protocols) {
|
||||
var klass, ws;
|
||||
if (protocols == null) {
|
||||
protocols = ['v10.stomp', 'v11.stomp'];
|
||||
}
|
||||
klass = Stomp.WebSocketClass || WebSocket;
|
||||
ws = new klass(url, protocols);
|
||||
return new Client(ws);
|
||||
},
|
||||
over: function (ws) {
|
||||
return new Client(ws);
|
||||
},
|
||||
Frame: Frame
|
||||
};
|
||||
|
||||
if (typeof window !== "undefined" && window !== null) {
|
||||
Stomp.setInterval = function (interval, f) {
|
||||
return window.setInterval(f, interval);
|
||||
};
|
||||
|
||||
Stomp.clearInterval = function (id) {
|
||||
return window.clearInterval(id);
|
||||
};
|
||||
window.Stomp = Stomp;
|
||||
} else {
|
||||
Stomp.setInterval = function (interval, f) {
|
||||
return setInterval(f, interval);
|
||||
}
|
||||
Stomp.clearInterval = function (id) {
|
||||
return clearInterval(id);
|
||||
};
|
||||
self.Stomp = Stomp;
|
||||
}
|
||||
|
||||
}).call(this);
|
||||
571
sample/dev/talk/js/module/talkEvent.js
Normal file
571
sample/dev/talk/js/module/talkEvent.js
Normal file
@@ -0,0 +1,571 @@
|
||||
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}
|
||||
154
sample/dev/talk/js/module/variable.js
Normal file
154
sample/dev/talk/js/module/variable.js
Normal file
@@ -0,0 +1,154 @@
|
||||
// 전역 변수
|
||||
|
||||
import {talkEvent} from "./talkEvent.js";
|
||||
|
||||
const global = {
|
||||
/*스톰프*/
|
||||
stomp: null,
|
||||
/*사용자 정보 */
|
||||
user: null,
|
||||
/*현페이지 업무정보*/
|
||||
work: null,
|
||||
/*api url정의*/
|
||||
apiUrl: '',
|
||||
/*헬프톡 생성 옵션*/
|
||||
option: {},
|
||||
/*헬프톡 content 루트 */
|
||||
shadowRoot: null,
|
||||
/*헬프톨 주요 엘리먼트 */
|
||||
element: {},
|
||||
/*채팅 날짜 정의*/
|
||||
currDay: '',
|
||||
/*채팅 상태글 이전 날짜*/
|
||||
yourOldDate: '',
|
||||
/*채팅 본인글 이전 날짜*/
|
||||
mineOldDate: '',
|
||||
/*톡방 메시지 스크롤 위치*/
|
||||
oldScrollPosition: 0,
|
||||
/*스크롤 top < 300 페이징 데이터 호출 여부*/
|
||||
scrollMoreFlag: true,
|
||||
/*채팅방 엘리먼트 변화 감지 하여 자동 스크롤 사용여부*/
|
||||
scrollObserveFlag:true,
|
||||
/* 불필요 변수 */
|
||||
allVNode: [],
|
||||
/* 채팅방 데이터 */
|
||||
talkData: [],
|
||||
/* 소켓 커넥션 및 브로드 캐스드 관리*/
|
||||
channel: null,
|
||||
/* 현재 접속 서버 */
|
||||
currentServer: '',
|
||||
/*시스템 강제 종료 여부*/
|
||||
forceKill: false,
|
||||
/* 채팅글 호출 파라미터 */
|
||||
messageParams: {
|
||||
'talkId': '',
|
||||
'week': 1,
|
||||
'row': 50,
|
||||
'start': 0,
|
||||
},
|
||||
/* 현재 접속 url 파리미터 정보 */
|
||||
requestParams: {},
|
||||
/* 구독 목록 */
|
||||
subscribeList : {},
|
||||
// 파일 구독 목록
|
||||
fileSubscribeList: {},
|
||||
/* 현재 사이트 URL*/
|
||||
currentUrl: '',
|
||||
/* 에러 메시지 함수 */
|
||||
notifier: null,
|
||||
/* 브라우저 탭 아이디 */
|
||||
tabId: '',
|
||||
/* 방 생성자 */
|
||||
chatCreator: null,
|
||||
hub: null,
|
||||
/*클립보드 정보*/
|
||||
clipboardData: null,
|
||||
clipboardObjectUrl: null,
|
||||
// 서비스워커 연결 여부
|
||||
serviceWorkerConnect: false,
|
||||
// 소켓연결 제어
|
||||
reConnect: false,
|
||||
/* 로딩 여부 (새로고침시 reconnect 방지용)*/
|
||||
init: false,
|
||||
checker : {flag: false, inDate: ''},
|
||||
talkParams : (obj) => {
|
||||
return {
|
||||
type: obj.type,
|
||||
insSabun: global.user.sabun,
|
||||
insName : global.user.name,
|
||||
insDate : obj.insDate || '',
|
||||
message: obj.message || '',
|
||||
talkId: obj.talkId,
|
||||
workId: obj.workId,
|
||||
workNm: obj.workNm || '',
|
||||
introOwner: obj.introOwner || '',
|
||||
talkAttachDtos: obj.talkAttachDtos === undefined ? [] : obj.talkAttachDtos,
|
||||
talkMessages: obj.talkMessages,
|
||||
attach: obj.attach,
|
||||
tabId: global.tabId
|
||||
}
|
||||
},
|
||||
/*특정 파라미터 초기화(채팅방 나가기시)*/
|
||||
reset: () => {
|
||||
// 인포 메시지 삭제
|
||||
const systemMessages = global.shadowRoot.querySelectorAll('.system-message');
|
||||
Array.from(systemMessages).forEach((sm) => sm.remove());
|
||||
// 사이드바 원위치
|
||||
global.element.chatSidebar.style.left = 0;
|
||||
// // 채팅 내용 호출 파라미터 초기화
|
||||
global.currDay = ''
|
||||
global.yourOldDate = '';
|
||||
global.mineOldDate = '';
|
||||
// global.messageParams.talkId = '';
|
||||
// global.messageParams.week = 1;
|
||||
global.scrollMoreFlag = true;
|
||||
global.scrollObserveFlag = true;
|
||||
// 검색 비활성화
|
||||
global.searchClose();
|
||||
|
||||
/*작성된 메시지 제거*/
|
||||
const msg = global.element.chatEditor.innerText.trim();
|
||||
if(msg !== '') {
|
||||
const talkId = localStorage.getItem(global.tabId)
|
||||
const _this = global.shadowRoot.querySelector(`div[data-talk-id="${talkId}"]`)
|
||||
const workId = _this.dataset.workId;
|
||||
const workNm = _this.dataset.workNm;
|
||||
talkEvent.send(global.talkParams({
|
||||
type: "UNTYPED",
|
||||
talkId: talkId,
|
||||
workId: workId,
|
||||
workNm: workNm
|
||||
}))
|
||||
|
||||
global.element.chatEditor.innerHTML = '';
|
||||
}
|
||||
|
||||
talkEvent.messageClean();
|
||||
// localstorage 정보 제거
|
||||
localStorage.removeItem(global.tabId)
|
||||
localStorage.removeItem('workId')
|
||||
localStorage.removeItem('workNm')
|
||||
|
||||
},
|
||||
talkClean: () => {
|
||||
//
|
||||
console.log('clean')
|
||||
global.currDay = ''
|
||||
global.yourOldDate = '';
|
||||
global.mineOldDate = '';
|
||||
global.messageParams.talkId = '';
|
||||
global.messageParams.week = 1;
|
||||
global.scrollMoreFlag = true;
|
||||
global.scrollObserveFlag = true;
|
||||
},
|
||||
searchClose: () => {
|
||||
const searchCloseBtn = global.shadowRoot.querySelector('#chat-option.bi-x-lg');
|
||||
if(searchCloseBtn) {
|
||||
searchCloseBtn.click()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export {global}
|
||||
102
sample/dev/talk/js/notification.js
Normal file
102
sample/dev/talk/js/notification.js
Normal file
@@ -0,0 +1,102 @@
|
||||
import {global} from 'http://devtalk.kospo.co.kr:3000/static/js/module/variable.js'
|
||||
import {talkEvent} from 'http://devtalk.kospo.co.kr:3000/static/js/module/talkEvent.js'
|
||||
// 브라우저 푸시 설정
|
||||
let notificationCount = 0;
|
||||
// 서비스 워커 등록 및 상태 확인
|
||||
async function registerServiceWorker() {
|
||||
if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
|
||||
throw new Error('서비스 워커와 푸시 알림이 지원되지 않는 브라우저입니다.');
|
||||
}
|
||||
|
||||
try {
|
||||
// 기존 서비스 워커 확인
|
||||
const existingReg = await navigator.serviceWorker.getRegistration();
|
||||
if (existingReg) {
|
||||
console.log('기존 서비스 워커 상태:', existingReg.active ? 'active' : 'inactive');
|
||||
return existingReg;
|
||||
}
|
||||
|
||||
// 새로운 서비스 워커 등록
|
||||
const registration = await navigator.serviceWorker.register(global.option.serviceWorkerLocation);
|
||||
console.log('서비스 워커 등록 상태:', registration.active ? 'active' : 'pending');
|
||||
|
||||
// 서비스 워커가 활성화될 때까지 대기
|
||||
if (!registration.active) {
|
||||
await new Promise((resolve) => {
|
||||
registration.addEventListener('activate', () => {
|
||||
console.log('서비스 워커가 활성화되었습니다.');
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return registration;
|
||||
} catch (error) {
|
||||
console.error('서비스 워커 등록 실패:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 알림 권한 요청
|
||||
async function requestNotificationPermission() {
|
||||
const permission = await Notification.requestPermission();
|
||||
console.log('알림 권한 상태:', permission);
|
||||
if (permission !== 'granted') {
|
||||
throw new Error('알림 권한이 거부되었습니다.');
|
||||
}
|
||||
return permission;
|
||||
}
|
||||
|
||||
async function showNotification(obj) {
|
||||
console.log('show Notification', obj)
|
||||
// const registration = await registerServiceWorker();
|
||||
const title = `${obj.workNm}${obj.insName !== null ? '['+obj.insName+']' : ''}`;
|
||||
const options = {
|
||||
body: obj.message,
|
||||
icon: '/talk/image/noti-icon.png',
|
||||
tag: `notification`, // 고유한 tag 할당
|
||||
renotify: true, // 같은 tag여도 다시 알림
|
||||
}
|
||||
|
||||
const notification = new Notification(title, options);
|
||||
|
||||
notification.onclick = (event) => {
|
||||
console.log(event)
|
||||
const chatFloatingBtn = global.shadowRoot.querySelector('.chat-floating-btn')
|
||||
if (!chatFloatingBtn.getAttribute("class").includes('active')) {
|
||||
chatFloatingBtn.click()
|
||||
}
|
||||
talkEvent.messageShow()
|
||||
const chatFloating = global.shadowRoot.querySelector('.chat-floating')
|
||||
chatFloating.classList.remove('hide')
|
||||
global.shadowRoot.querySelector(`div[data-talk-id="${obj.talkId}"]`).click()
|
||||
window.focus();
|
||||
|
||||
notification.close()
|
||||
};
|
||||
|
||||
notification.onclose = () => {
|
||||
// const $chatFloatingBtn = global.shadowRoot.querySelector('.chat-floating-btn')
|
||||
// const $chatFloating = global.shadowRoot.querySelector('.chat-floating')
|
||||
//
|
||||
// $chatFloatingBtn.style.bottom = '30px'
|
||||
// $chatFloating.style.bottom = '85px'
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// if ('serviceWorker' in navigator) {
|
||||
// navigator.serviceWorker.register('/talk/js/module/service-worker.js')
|
||||
// .then(registration => {
|
||||
// console.log('Service Worker registered with scope:', registration.scope);
|
||||
// })
|
||||
// .catch(error => {
|
||||
// console.error('Service Worker registration failed:', error);
|
||||
// });
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export {showNotification, requestNotificationPermission}
|
||||
2397
sample/dev/talk/js/talk.js
Normal file
2397
sample/dev/talk/js/talk.js
Normal file
File diff suppressed because it is too large
Load Diff
383
sample/dev/talk/js/utils.js
Normal file
383
sample/dev/talk/js/utils.js
Normal file
@@ -0,0 +1,383 @@
|
||||
// 공통 유틸
|
||||
|
||||
import {global} from "http://devtalk.kospo.co.kr:3000/static/js/module/variable.js";
|
||||
|
||||
let masterVar = false;
|
||||
const Common = {
|
||||
isMasterVar : () => {
|
||||
return masterVar;
|
||||
},
|
||||
sleep: (delay) => {
|
||||
return new Promise(resolve => setTimeout(resolve, delay));
|
||||
},
|
||||
nowDateDifference: (date1) => {
|
||||
// date1과 date2 문자열을 Date 객체로 변환합니다.
|
||||
const now = new Date();
|
||||
const midNow = new Date(`${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate() + 1}`);
|
||||
// 두 날짜 간의 시간 차이를 밀리초 단위로 계산합니다.
|
||||
let diffTime = midNow.getTime() - date1.getTime();
|
||||
|
||||
// 밀리초를 일 단위로 변환합니다.
|
||||
let diffDays = diffTime / (1000 * 60 * 60 * 24);
|
||||
|
||||
// 일수 차이를 반환합니다.
|
||||
return diffDays;
|
||||
},
|
||||
dateDifference: (date1, date2) => {
|
||||
// date1과 date2 문자열을 Date 객체로 변환합니다.
|
||||
// 두 날짜 간의 시간 차이를 밀리초 단위로 계산합니다.
|
||||
let diffTime = date1.getTime() - date2.getTime();
|
||||
|
||||
// 일수 차이를 반환합니다.
|
||||
return diffTime > 0;
|
||||
},
|
||||
dateWithNanos: (date) => {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hour = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
|
||||
const millisNanos = String(date.getMilliseconds()).padStart(3, '0')
|
||||
const additionalNanos = String(Math.floor(Math.random() * 999)).padStart(3, '0');
|
||||
|
||||
return `${year}-${month}-${day} ${hour}:${minutes}:${seconds}.${millisNanos}${additionalNanos}`
|
||||
|
||||
},
|
||||
getAttachVnm: (date, tabId) => {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hour = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
|
||||
const millisNanos = String(date.getMilliseconds()).padStart(3, '0')
|
||||
const nanoSecond = Math.round(Math.random() * 999).toString().padEnd(3, '0')
|
||||
|
||||
|
||||
return `${year}${month}${day}${hour}${minutes}${seconds}${millisNanos}${nanoSecond}_${tabId}`
|
||||
|
||||
},
|
||||
nowDate: () => {
|
||||
// 날짜 포맷터 생성
|
||||
const dateFormatter = new Intl.DateTimeFormat('ko-KR', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false
|
||||
});
|
||||
|
||||
|
||||
// 날짜와 시간 포맷팅
|
||||
const parts = dateFormatter.formatToParts(now);
|
||||
|
||||
// 원하는 형식으로 조합
|
||||
let formattedDate = '';
|
||||
let datePart = '';
|
||||
let timePart = '';
|
||||
|
||||
parts.forEach(({type, value}) => {
|
||||
switch (type) {
|
||||
case 'year':
|
||||
case 'month':
|
||||
case 'day':
|
||||
datePart += (type === 'year' ? value : '-' + value.padStart(2, '0'));
|
||||
break;
|
||||
case 'hour':
|
||||
case 'minute':
|
||||
case 'second':
|
||||
timePart += (timePart ? ':' : '') + value.padStart(2, '0');
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
formattedDate = `${datePart} ${timePart}`;
|
||||
|
||||
return formattedDate;
|
||||
},
|
||||
//대화방 리스트에서 사용
|
||||
talkListDateFormat: (str) => {
|
||||
const convertDate = new Date(str);
|
||||
let formatOptions;
|
||||
let prefix = '';
|
||||
const diffDay = Common.nowDateDifference(convertDate);
|
||||
if (diffDay > 0 && diffDay <= 1) {
|
||||
formatOptions = {
|
||||
// year: 'numeric',
|
||||
// month: '2-digit',
|
||||
// day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
// second: '2-digit',
|
||||
hour12: false
|
||||
}
|
||||
prefix = ''
|
||||
} else if (diffDay > 1 && diffDay <= 2) {
|
||||
formatOptions = {
|
||||
// year: 'numeric',
|
||||
// month: '2-digit',
|
||||
// day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
// second: '2-digit',
|
||||
hour12: false
|
||||
}
|
||||
|
||||
prefix = '어제 '
|
||||
} else {
|
||||
formatOptions = {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
// hour: '2-digit',
|
||||
// minute: '2-digit',
|
||||
// second: '2-digit',
|
||||
hour12: false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 날짜 포맷터 생성
|
||||
const dateFormatter = new Intl.DateTimeFormat('ko-KR', formatOptions);
|
||||
|
||||
|
||||
// 날짜와 시간 포맷팅
|
||||
const parts = dateFormatter.formatToParts(convertDate);
|
||||
|
||||
// 원하는 형식으로 조합
|
||||
let formattedDate = '';
|
||||
let datePart = '';
|
||||
let timePart = '';
|
||||
|
||||
parts.forEach(({type, value}) => {
|
||||
switch (type) {
|
||||
case 'year':
|
||||
case 'month':
|
||||
case 'day':
|
||||
datePart += (type === 'year' ? value : '-' + value.padStart(2, '0'));
|
||||
break;
|
||||
case 'hour':
|
||||
timePart += (parseInt(value) > 12 ? '오후 ' : '오전 ') + (timePart ? ':' : '') + (parseInt(value) > 12 ? parseInt(value) % 12 : parseInt(value));
|
||||
break;
|
||||
case 'minute':
|
||||
case 'second':
|
||||
timePart += (timePart ? ':' : '') + value.padStart(2, '0');
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
formattedDate = `${prefix}${prefix !== '' ? '' : datePart} ${prefix !== '' ? '' : timePart}`;
|
||||
|
||||
return formattedDate;
|
||||
},
|
||||
//대화방 글작성시 사용
|
||||
talkDateFormat: (str) => {
|
||||
// 문자열을 Date 객체로 변환
|
||||
const date = new Date(str);
|
||||
|
||||
// 시간을 가져옴
|
||||
let hours = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
|
||||
// 오전/오후 결정
|
||||
const ampm = hours >= 12 ? '오후' : '오전';
|
||||
|
||||
// 12시간 형식으로 변환
|
||||
hours = hours % 12;
|
||||
hours = hours ? hours : 12; // 0시를 12시로 표시
|
||||
|
||||
// 분이 한 자리 수일 경우 앞에 0을 붙임
|
||||
const minutesStr = minutes < 10 ? '0' + minutes : minutes;
|
||||
|
||||
// 최종 형식으로 조합
|
||||
return `${ampm} ${hours}:${minutesStr}`;
|
||||
},
|
||||
dayOfWeek: (yyyymmdd) => {
|
||||
const dow = new Date(yyyymmdd).getDay();
|
||||
const week = {
|
||||
'0': '일',
|
||||
'1': '월',
|
||||
'2': '화',
|
||||
'3': '수',
|
||||
'4': '목',
|
||||
'5': '금',
|
||||
'6': '토',
|
||||
}
|
||||
return `${yyyymmdd} (${week[dow]})`;
|
||||
},
|
||||
nullCheck: (value) => {
|
||||
let result = value;
|
||||
if (value === undefined || value === null) {
|
||||
result = ''
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
fileToBase64: async (file) => {
|
||||
const reader = new FileReader();
|
||||
let result;
|
||||
reader.readAsDataURL(file)
|
||||
reader.onload = async () => {
|
||||
result = await reader.result
|
||||
}
|
||||
|
||||
return result
|
||||
},
|
||||
cropImage: (image, size) => {
|
||||
const widthRatio = size / image.width;
|
||||
const heightRatio = size / image.height;
|
||||
const ratio = Math.max(widthRatio, heightRatio);
|
||||
|
||||
const newWidth = image.width * ratio;
|
||||
const newHeight = image.height * ratio;
|
||||
return new Promise((resolve, reject) => {
|
||||
// 썸네일 생성
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = newWidth;
|
||||
canvas.height = newHeight;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// 이미지 크기 조정 및 그리기 (Bicubic과 같은 고품질 스케일링)
|
||||
ctx.imageSmoothingEnabled = true;
|
||||
ctx.imageSmoothingQuality = 'high';
|
||||
|
||||
ctx.drawImage(image, 0, 0, newWidth, newHeight);
|
||||
|
||||
resolve(canvas.toDataURL());
|
||||
});
|
||||
},
|
||||
mergeObject: (target, source) => {
|
||||
for(let key in source) {
|
||||
if(source[key] instanceof Object) {
|
||||
target[key] = Object.assign({}, Common.mergeObject(source[key], target[key]))
|
||||
}
|
||||
|
||||
target = Object.assign({}, source, target);
|
||||
}
|
||||
return target;
|
||||
},
|
||||
addDelegatedEventListener: (parentElement, eventType, selector, handler) => {
|
||||
function elementMatches(el, selector) {
|
||||
const matches = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;
|
||||
|
||||
if (matches) {
|
||||
return matches.call(el, selector);
|
||||
} else {
|
||||
// matches 메서드를 지원하지 않는 브라우저를 위한 폴백
|
||||
const allElements = el.parentNode.querySelectorAll(selector);
|
||||
return Array.prototype.indexOf.call(allElements, el) !== -1;
|
||||
}
|
||||
}
|
||||
|
||||
parentElement.addEventListener(eventType, function (e) {
|
||||
const path = e.composedPath && e.composedPath() || e.path || (function (target) {
|
||||
const path = [];
|
||||
while (target.parentElement) {
|
||||
path.push(target);
|
||||
target = target.parentElement;
|
||||
}
|
||||
path.push(document, window);
|
||||
return path;
|
||||
})(e.target);
|
||||
|
||||
|
||||
for (let element of path) {
|
||||
if (element === parentElement) break;
|
||||
if (elementMatches(element, selector)) {
|
||||
const wrappedEvent = new Proxy(e, {
|
||||
get: function (target, prop) {
|
||||
if (prop === 'target' || prop === 'currentTarget') {
|
||||
return element;
|
||||
}
|
||||
if (prop === 'preventDefault') {
|
||||
return () => e.preventDefault();
|
||||
}
|
||||
if (prop === 'stopPropagation') {
|
||||
return () => {
|
||||
e.stopImmediatePropagation();
|
||||
e.stopPropagation();
|
||||
};
|
||||
}
|
||||
if (typeof target[prop] === 'function') {
|
||||
return target[prop].bind(target);
|
||||
}
|
||||
return target[prop];
|
||||
}
|
||||
});
|
||||
|
||||
const result = handler.call(element, wrappedEvent);
|
||||
|
||||
if (result === false) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
if (wrappedEvent.defaultPrevented) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
},
|
||||
findParentElement: (node, parentClass) => {
|
||||
let pa = node.parentElement;
|
||||
if(pa === null) {
|
||||
return null;
|
||||
} else {
|
||||
if (pa.classList.contains(parentClass)) {
|
||||
return pa;
|
||||
} else {
|
||||
return Common.findParentElement(pa, parentClass)
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
getListening: (talkId) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', `${global.apiUrl}/api/user/listening?talkId=${talkId}`, false);
|
||||
xhr.withCredentials = true;
|
||||
xhr.send();
|
||||
|
||||
if(xhr.status === 200) {
|
||||
console.log(xhr.responseText)
|
||||
return Number(xhr.responseText);
|
||||
}
|
||||
},
|
||||
setListening: (talkId, status) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', `${global.apiUrl}/api/user/listening`, false);
|
||||
xhr.withCredentials = true;
|
||||
xhr.setRequestHeader("content-type", "application/json")
|
||||
xhr.send(JSON.stringify({"talkId":talkId, "status":status}));
|
||||
|
||||
if(xhr.status === 200) {
|
||||
console.log(xhr.responseText)
|
||||
}
|
||||
},
|
||||
isMaster() {
|
||||
try {
|
||||
return global.stomp.connected;
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
setCookie: (key, value) => {
|
||||
document.cookie = `${key}=${value};Path=/;domain=.kospo.co.kr`
|
||||
},
|
||||
getCookie: (key) => {
|
||||
const value = document.cookie.match('(^|;) ?' + key + '=([^;]*)(;|$)');
|
||||
return value ? value[2] : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default Common;
|
||||
Reference in New Issue
Block a user