import { get, set, del, update, createStore, values } from 'idb-keyval' import { Span, Button, List, ListItem, Input, createElement } from './weigets.js' // 先不划分频道, 只有一个公共聊天室 export default class Chat { constructor({ name, EventListeners = {}, onsend, onexit }) { this.event = { onsend, onexit } this.store = createStore(`db-chat-${name}`, `store-chat-${name}`) this.ul = List({ classList: ['chat-list'] }) this.EventListeners = EventListeners document.body.appendChild(createElement({ children: [ this.ul, createElement({ type: 'text', placeholder: '输入聊天内容', style: { height: '5rem', margin: '1rem 2rem', padding: '1rem', boxSizing: 'border-box', boxShadow: '0 0 1rem #eee', }, onkeydown: event => { const text = event.target.value.trim() if (text && event.ctrlKey && event.key === 'Enter') { this.发送消息(text) event.target.value = '' } } }, 'textarea'), Button({ textContent: '发送(ctrl+Enter)', onclick: event => { const text = event.target.previousSibling.value.trim() if (text) { this.发送消息(text) event.target.previousSibling.value = '' } }, style: { margin: '1rem 2rem', padding: '.5rem 1rem', boxSizing: 'border-box', boxShadow: '0 0 1rem #eee', borderRadius: '1rem', } }), ] })) // 写入 css 样式到 head const style = document.createElement('style') style.innerText = ` ul.chat-list { max-height: 70vh; overflow-y: auto; } ul.chat-list > li > span { cursor: pointer; } ul.chat-list > li.play > span { color: #02be08; } ul.chat-list > li.cache::marker { color: #02be08; font-size: 1em; contentx: '⚡'; } ul.chat-list > li.disable { color: #888; } ` document.head.appendChild(style) this.载入消息() } // 收到应答(对方确认消息已被接收) answer(data) { const { name, text, time, type } = data const item = this.add({ name, text, time, type }) item.classList.add('disable') } // 添加一条消息 add({ name, text, time, type }) { const item = ListItem({ classList: [type], children: [ Span({ innerText: `${name} ${time} ${text}` }) ] }) this.ul.appendChild(item) this.ul.scrollTop = this.ul.scrollHeight return item } send(text) { if (this.event.onsend) { this.event.onsend(text) } } 添加元素(data) { this.ul.appendChild(ListItem({ children: [ Span({ innerText: `${data.name} ${data.time} ${data.text}` }) ] })) } 存储消息(data) { const { name, text, time, type } = data const id = window.crypto.randomUUID() const item = { id, name, text, time, type } set(id, item, this.store) } 载入消息() { values(this.store).then(items => { items.map(item => { item.timestamp = new Date(item.time).getTime() return item }).sort((a, b) => a.timestamp - b.timestamp).forEach(item => { this.添加元素(item) }) }) } 发送消息(text) { const name = '我' const time = new Date().toLocaleString() console.log('发送消息', { name, text, time }) const type = 'text' this.添加元素({ name, text, time, type }) this.存储消息({ name, text, time, type }) this.send({ name, text, time, type }) } 收到消息(data) { const { name, text, time, type } = data this.add({ name, text, time, type }) } // 退出 exit() { if (this.event.onexit) { this.event.onexit() } } }