diff --git a/ling_chair_http/chat-message.css b/ling_chair_http/chat-message.css index 33b435b..52b5cdf 100644 --- a/ling_chair_http/chat-message.css +++ b/ling_chair_http/chat-message.css @@ -23,14 +23,14 @@ display: flex; justify-content: flex-end; align-items: flex-start; - margin: 13px; + margin: 13px 13px 13px 10%; } .chat-message-left { display: flex; justify-content: flex-start; align-items: flex-start; - margin: 13px; + margin: 13px 10% 13px 13px; } .message-content { @@ -39,9 +39,10 @@ margin-left: 5px; margin-right: 5px; max-width: 100%; + min-width: 0%; white-space: normal; word-break: break-all; - font-size: medium; + /* font-size: medium; */ /* 使用了 CardView 就不需要边框了 */ /* border: 1.3px solid; */ padding: 15px; diff --git a/ling_chair_http/handler.js b/ling_chair_http/handler.js index d283c49..73e17e5 100644 --- a/ling_chair_http/handler.js +++ b/ling_chair_http/handler.js @@ -188,14 +188,14 @@ class NickCache { * @returns {String} nick */ static async getNick(name) { - return await new Promise((res, rej) => { + return await new Promise((res, _rej) => { // 这个this别摆着不放啊 不然两下就会去世 - let nick = this.data[name] + let nick = NickCache.data[name] if (nick == null) - client.emit("user.getNick", { name: localStorage.userName }, (re) => { + client.emit("user.getNick", { name: name }, (re) => { let nk = re.data != null ? re.data.nick : name if (nk == null) nk = name - this.data[name] = nk + NickCache.data[name] = nk res(nk) }) else @@ -204,6 +204,10 @@ class NickCache { } } +// ================================ +// 联系人 +// ================================ + class ContactsList { /** * 重载联系人列表 @@ -221,7 +225,7 @@ class ContactsList { for (let index in ls) { let name = ls[index] let dick = await NickCache.getNick(name) - $($.parseHTML(`
  • ` + dick + `
  • `)).appendTo(viewBinding.contactsList).click(() => { + $($.parseHTML(`
  • ` + dick + `
  • `)).appendTo(viewBinding.contactsList).click(() => { ChatMsgAdapter.switchTo(name, "single") }) } @@ -232,9 +236,16 @@ class ContactsList { * 添加联系人/群峦 * @param {String} nameOrId */ - static add(name, type) { + static async add(name, type) { if (type == "single") { - + client.emit("user.addFriend", { + name: localStorage.userName, + target: name, + accessToken: await CurrentUser.getAccessToken(), + }, async (re) => { + // if (re.code !== 0) + return mdui.snackbar(re.msg) + }) } } /** @@ -245,21 +256,256 @@ class ContactsList { } } -// 消息核心 +// ================================ +// 消息核心 +// ================================ + +class ChatTabManager { + static tabs = {} + /** + * 添加Tab + * @param { String } title + * @param { String } target + */ + static add(title, target) { + $($.parseHTML(`${title}`)).appendTo(viewBinding.chatTab) + } + /** + * 寻找Tab + * @param { String } target + * @returns { jQuery } element + */ + static find(target) { + return $("#chatTab_" + target) + } + /** + * 点击Tab + * @param { String } target + */ + static click(target) { + this.find(this.target).click() + } + /** + * 删除Tab + * @param { String } target + */ + static remove(target) { + this.find(target).remove() + } +} class ChatPage { static cached = {} - constructor(name, type) { - + constructor(name, title, type) { + this.chatTarget = name + this.chatType = type + ChatTabManager.add(title, this.chatTarget) + this.chatPageElement = $($.parseHTML(`
    `)) + this.chatPageElement.appendTo(viewBinding.pageChatSeesion) } /** - * 切换到某一个聊天对象 - * @param {String} name - * @param {String} type + * 获取当前的聊天栏 + * @returns { jQuery } */ - static switchTo(name, type) { - if (!this.cached[name]) - this.cached[name] = new ChatPage(name, type) + static getCurrentChatSeesion() { + return $(".chat-seesion[actived=true]") + } + /** + * 获取当前聊天页面 + * @returns { ChatPage } + */ + static getCurrentChatPage(name) { + return ChatPage.cached[$(".chat-seesion[actived=true]").attr("target")] + } + /** + * 切换选择的聊天对象 + */ + async show() { + ChatTabManager.click(this.chatTarget) + + this.minMsgId = null + + for (let k of Object.keys(ChatPage.cached)) { + let cpe = ChatPage.cached[k].chatPageElement + cpe.attr("actived", null) + cpe.hide() + let tbe = ChatTabManager.find(k) + tbe.removeClass("mdui-tab-active") + } + + $(this.chatPageElement).empty() + $(this.chatPageElement).attr("actived", "true") + $(this.chatPageElement).show() + ChatTabManager.find(this.chatTarget).addClass("mdui-tab-active") + + await this.loadMore() + ChatMsgAdapter.scrollToBottom() + } + /** + * 连带Tab一起销毁 + */ + remove() { + ChatTabManager.remove(this.chatTarget) + ChatPage.cached[this.chatTarget].chatPageElement.remove() + ChatPage.cached[this.chatTarget] = null + } + /** + * 加载更多聊天记录 + * @param {int} 加载数量 + */ + async loadMore(limit) { + let histroy = await this.getHistroy(this.minMsgId, limit == null ? 13 : limit) + let chatPager = viewBinding.chatPager.get(0) + + if (histroy.length == 0) + return mdui.snackbar("已经加载完了~") + + let re = this.minMsgId != null + this.minMsgId = histroy[0].msgid - 1 + let sc = 0 + if (re) histroy = histroy.reverse() + for (let index in histroy) { + let i = histroy[index] + let e = await this.addMsg(i.name, i.msg, i.time, re, i.msgid) + // 因为某些因素直接DEBUG到吐血 断点继续都不报错 原因不明 + sc = sc + (e == null ? 35 : getOffsetTop(chatPager, e.get(0))) + } + chatPager.scrollBy({ + top: sc, + behavior: 'smooth' + }) + } + /** + * 获取聊天消息记录 + * @param {int} 起始点 + * @param {int} 获取数量 + */ + async getHistroy(start, limit) { + if (this.chatType == "single") + return new Promise(async (res, _rej) => { + client.emit("user.getSingleChatHistroy", { + name: localStorage.userName, + target: this.chatTarget, + limit: limit, + accessToken: await CurrentUser.getAccessToken(), + startId: start, + }, (re) => { + if (re.code !== 0) + return mdui.snackbar(re.msg) + res(re.data.histroy) + }) + }) + throw new TypeError("Unsupported chat type!") + } + /** + * 发送消息 + * @param { String } msg + */ + async send(msg) { + if (this.chatType == "single") + client.emit("user.sendSingleMsg", { + name: localStorage.userName, + target: this.chatTarget, + msg: msg, + accessToken: await CurrentUser.getAccessToken(), + }, async (re) => { + if (re.code !== 0) + return mdui.snackbar(re.msg) + + viewBinding.inputMsg.val("") + + // 微机课闲的没事干玩玩 发现私聊会多发一个(一个是本地的, 另一个是发送成功的) 选择一个关掉就好了 + // 这里我选择服务端不发送回调, 不然多设备同步会吵死 + // 错了 应该是客户端少发条才对 不然不能多设备同步 + if (ChatMsgAdapter.target !== localStorage.userName) { + let i = ChatMsgAdapter.isAtBottom() + await ChatMsgAdapter.addMsg(localStorage.userName, msg, re.data.time, re.data.msgid) + if (i) ChatMsgAdapter.scrollToBottom() + } + }) + throw new TypeError("Unsupported chat type!") + } + /** + * 添加系统消息 + * @param { String } 消息 + * @param { Boolean } 是否加到顶部 + * @returns { jQuery } 消息元素 + */ + addSystemMsg(msg, addToTop) { + let element + if (addToTop) + // 加到头部 + element = $($.parseHTML(msg)).prependTo(this.chatPageElement) + else + // 加到尾部 + element = $($.parseHTML(msg)).appendTo(this.chatPageElement) + return element + } + /** + * 添加聊天记录 + * @param { String } name + * @param { String } msg + * @param { String } type + * @param { Boolean } 是否加到头部 + * @param { String || int } 消息id + * @returns { jQuery } 消息元素 + */ + async addMsg(name, preMsg, time, addToTop, msgid) { + + let nick = await NickCache.getNick(name) // re.data == null ? name : re.data.nick + + let msg + + try { + msg = await marked.parse(preMsg) + } catch (error) { + console.log("解析消息失败: " + error) + msg = escapeHTML(preMsg) + } + + let temp + if (name === localStorage.userName) + temp = `
    +
    + ${nick} +
    + ${msg} +
    ${preMsg}
    +
    +
    + +
    ` + else + temp = `
    + +
    + ${nick} +
    + ${msg} +
    ${preMsg}
    +
    +
    +
    ` + + let nowMinutes = new Date(time).getMinutes() + let msgElement + if (addToTop) { + this.addSystemMsg(temp, addToTop) + if (this.minutesCache != nowMinutes) { + msgElement = this.addSystemMsg(`
    ${new Date().format(time == null ? Date.parse("1000-1-1 00:00:00") : time, "yyyy年MM月dd日 hh:mm:ss")}
    `, addToTop) + this.time = nowMinutes + } + } else { + if (this.minutesCache != nowMinutes) { + msgElement = this.addSystemMsg(`
    ${new Date().format(time == null ? Date.parse("1000-1-1 00:00:00") : time, "yyyy年MM月dd日 hh:mm:ss")}
    `, addToTop) + this.time = nowMinutes + } + this.addSystemMsg(temp, addToTop) + } + + this.minutesCache = new Date(time).getMinutes() + + return msgElement } } @@ -276,7 +522,11 @@ class ChatMsgAdapter { * @param {String} type */ static async switchTo(name, type) { - viewBinding.tabChatSeesion.show() + if (!ChatPage.cached[name]) + ChatPage.cached[name] = new ChatPage(name, await NickCache.getNick(name), type) + + ChatPage.cached[name].show() + /* viewBinding.tabChatSeesion.show() viewBinding.tabChatSeesion.text(await NickCache.getNick(name)) viewBinding.tabChatSeesion.get(0).click() @@ -287,14 +537,15 @@ class ChatMsgAdapter { viewBinding.pageChatSeesion.empty() await this.loadMore() - this.scrollToBottom() + this.scrollToBottom() */ } /** * 发送消息 * @param {String} msg */ static async send(msg) { - client.emit("user.sendSingleMsg", { + ChatPage.getCurrentChatPage().send(msg) + /* client.emit("user.sendSingleMsg", { name: localStorage.userName, target: this.target, msg: msg, @@ -313,7 +564,7 @@ class ChatMsgAdapter { await ChatMsgAdapter.addMsg(localStorage.userName, msg, re.data.time, re.data.msgid) if (i) ChatMsgAdapter.scrollToBottom() } - }) + }) */ } /** * 获取聊天消息记录 @@ -337,10 +588,12 @@ class ChatMsgAdapter { } /** * 加载更多聊天记录 - * @param {int}} 加载数量 + * @param {int} 加载数量 */ static async loadMore(limit) { - let histroy = await this.getHistroy(this.minMsgId, limit == null ? 13 : limit) + ChatPage.getCurrentChatPage().loadMore(limit) + /* let histroy = await this.getHistroy(this.minMsgId, limit == null ? 13 : limit) + let chatPager = viewBinding.chatPager.get(0) if (histroy.length == 0) return mdui.snackbar("已经加载完了~") @@ -353,12 +606,21 @@ class ChatMsgAdapter { let i = histroy[index] let e = await this.addMsg(i.name, i.msg, i.time, re, i.msgid) // 因为某些因素直接DEBUG到吐血 断点继续都不报错 原因不明 - sc = sc + (e == null ? 25 : e.get(0).offsetTop) + sc = sc + (e == null ? 35 : getOffsetTop(chatPager, e.get(0))) } - window.scrollBy({ + chatPager.scrollBy({ top: sc, behavior: 'smooth' - }) + }) */ + } + + /** + * 是否在底部 + * @returns {Boolean} 是否在底部 + */ + static isAtBottom() { + let elementRect = viewBinding.pageChatSeesion.get(0).getBoundingClientRect() + return (elementRect.bottom <= window.innerHeight) } /** * 添加系统消息 @@ -376,14 +638,6 @@ class ChatMsgAdapter { e = $($.parseHTML(m)).appendTo(viewBinding.pageChatSeesion) return e } - /** - * 是否在底部 - * @returns {Boolean} 是否在底部 - */ - static isAtBottom() { - let elementRect = viewBinding.pageChatSeesion.get(0).getBoundingClientRect() - return (elementRect.bottom <= window.innerHeight) - } // 添加消息 返回消息的JQ对象 // name: 用户id m: 消息 t: 时间戳 re: 默认加到尾部 msgid: 消息id /** @@ -400,10 +654,10 @@ class ChatMsgAdapter { let nick = await NickCache.getNick(name) // re.data == null ? name : re.data.nick let msg - + try { msg = await marked.parse(preMsg) - } catch(error) { + } catch (error) { console.log("解析消息失败: " + error) msg = escapeHTML(preMsg) } @@ -412,22 +666,22 @@ class ChatMsgAdapter { if (name === localStorage.userName) temp = `
    - ${ nick } -
    - ${ msg } -
    ${ preMsg }
    + ${nick} +
    + ${msg} +
    ${preMsg}
    - +
    ` else temp = `
    - +
    - ${ nick } -
    - ${ msg } -
    ${ preMsg }
    + ${nick} +
    + ${msg} +
    ${preMsg}
    ` @@ -437,12 +691,12 @@ class ChatMsgAdapter { if (addToTop) { this.addSystemMsg(temp, addToTop) if (this.minutesCache != nowMinutes) { - msgElement = this.addSystemMsg(`
    ` + new Date().format(time == null ? Date.parse("1000-1-1 00:00:00") : time, "yyyy年MM月dd日 hh:mm:ss") + `
    `, addToTop) + msgElement = this.addSystemMsg(`
    ${new Date().format(time == null ? Date.parse("1000-1-1 00:00:00") : time, "yyyy年MM月dd日 hh:mm:ss")}
    `, addToTop) this.time = nowMinutes } } else { if (this.minutesCache != nowMinutes) { - msgElement = this.addSystemMsg(`
    ` + new Date().format(time == null ? Date.parse("1000-1-1 00:00:00") : time, "yyyy年MM月dd日 hh:mm:ss") + `
    `, addToTop) + msgElement = this.addSystemMsg(`
    ${new Date().format(time == null ? Date.parse("1000-1-1 00:00:00") : time, "yyyy年MM月dd日 hh:mm:ss")}
    `, addToTop) this.time = nowMinutes } this.addSystemMsg(temp, addToTop) @@ -478,7 +732,7 @@ class ChatMsgAdapter { // 可以利用这个特性来实现自动滚动文本 let resize = () => { // CSS 牵一发而动全身 因此这个减少的数值是每天都要更改的 - viewBinding.pageChatSeesion.height(window.innerHeight - viewBinding.inputToolbar.height() - $("header.mdui-appbar").height() - viewBinding.chatTab.height() - 65) + viewBinding.chatPager.height(window.innerHeight - viewBinding.inputToolbar.height() - $("header.mdui-appbar").height() - viewBinding.chatTab.height() - 17) let ledi = this.resizeDick - window.innerHeight if (isMobile()) viewBinding.chatPager.get(0).scrollBy({ // 5.19晚10:56分调配出来的秘方 @@ -509,10 +763,10 @@ class ChatMsgAdapter { e = $(ele) let menuHtml = $.parseHTML(`