mirror of
https://github.com/LingChair/LingChair-V0.git
synced 2025-12-09 02:25:50 +08:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d7d7e7209 | ||
|
|
8446ba09c2 | ||
|
|
d450797895 | ||
|
|
a326c16f1c | ||
|
|
3df9df6d85 | ||
|
|
585ea41831 | ||
|
|
12de793b26 | ||
|
|
84a5e52fbf | ||
|
|
436358e7c1 | ||
|
|
25d61b3a78 | ||
|
|
f2c9e51fd3 | ||
|
|
cc5fcc1b02 | ||
|
|
bf9ba20ede | ||
|
|
dcc4e040a5 | ||
|
|
733d5f76c3 |
BIN
.github/模块化_Babel.zip
vendored
BIN
.github/模块化_Babel.zip
vendored
Binary file not shown.
BIN
.github/模块化_Webpack.zip
vendored
BIN
.github/模块化_Webpack.zip
vendored
Binary file not shown.
@@ -50,12 +50,12 @@ else {
|
||||
Stickyfill.add($("*").filter((a, b) => $(b).css('position') === 'sticky'))
|
||||
|
||||
ChatMsgAdapter.initMsgElementEvents()
|
||||
|
||||
ChatMsgAdapter.initInputResizer()
|
||||
ChatTabManager.initTabElementEvents()
|
||||
|
||||
const showLinkDialog = (link) => mdui.alert(decodeURI(link) + "<br/>如果你确认此链接是安全的, 那么请<a class=\"mdui-text-color-theme-accent\" href=\"" + link + "\">点我</a>", '链接', () => { }, { confirmText: "关闭" })
|
||||
const showLinkDialog = (link) => mdui.alert(decodeURI(link) + "<br/>如果你确认此链接是安全的, 那么请<a class=\"mdui-text-color-theme\" href=\"" + link + "\">点我</a>", '链接', () => { }, { confirmText: "关闭" })
|
||||
|
||||
const showImageDialog = (link, id, alt) => mdui.alert(`此图片链接来源未知: ${decodeURI(link)}<br/>如果你希望加载, 请<a class="mdui-text-color-theme-accent" mdui-dialog-close onclick="$('#${id}').html('<img src=\\'${link}\\' alt=\\'${decodeURI(alt)}\\' class=\\'message-image\\'></img>')">点我</a>`, '外部图片', () => { }, { confirmText: "关闭" })
|
||||
const showImageDialog = (link, id, alt) => mdui.alert(`此图片链接来源未知: ${decodeURI(link)}<br/>如果你希望加载, 请<a class="mdui-text-color-theme" mdui-dialog-close onclick="$('#${id}').html('<img src=\\'${link}\\' alt=\\'${decodeURI(alt)}\\' class=\\'message-image\\'></img>')">点我</a>`, '外部图片', () => { }, { confirmText: "关闭" })
|
||||
|
||||
const showCodeDialog = (code) => mdui.alert(`<pre><code>${decodeURI(code)}</code></pre>`, '代码块', () => { }, { confirmText: "关闭" })
|
||||
|
||||
@@ -70,7 +70,7 @@ const renderer = {
|
||||
return text
|
||||
},
|
||||
link(href, title, text) {
|
||||
return `<a class="mdui-text-color-theme-accent" onclick="showLinkDialog('${encodeURI(href)}')">[链接] ${text}</a>`
|
||||
return `<a class="mdui-text-color-theme" onclick="showLinkDialog('${encodeURI(href)}')">[链接] ${text}</a>`
|
||||
},
|
||||
image(href, title, text) {
|
||||
let h = Hash.sha256(href)
|
||||
@@ -81,10 +81,10 @@ const renderer = {
|
||||
if (out)
|
||||
return `<img src="${encodeURI(href)}" alt="${text}" class="message-image"></img>`
|
||||
else
|
||||
return `<div id="${h}"><a class="mdui-text-color-theme-accent" onclick="showImageDialog('${encodeURI(href)}', '${h}', '${encodeURI(text)}')">[外部图片] ${text}</a></div>`
|
||||
return `<div id="${h}"><a class="mdui-text-color-theme" onclick="showImageDialog('${encodeURI(href)}', '${h}', '${encodeURI(text)}')">[外部图片] ${text}</a></div>`
|
||||
},
|
||||
code(src) {
|
||||
return `<a class="mdui-text-color-theme-accent" onclick="showCodeDialog(\`${encodeURI(src)}\`)">[代码块]</a>`
|
||||
return `<a class="mdui-text-color-theme" onclick="showCodeDialog(\`${encodeURI(src)}\`)">[代码块]</a>`
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -9,12 +9,13 @@
|
||||
// ================================
|
||||
|
||||
class CurrentUser {
|
||||
/** @type { String } */
|
||||
static myAccessToken
|
||||
/**
|
||||
* 登录账号
|
||||
* @param {String} name
|
||||
* @param {String} passwd
|
||||
* @param {Function} callback
|
||||
* @param { String } name
|
||||
* @param { String } passwd
|
||||
* @param { Function } callback
|
||||
*/
|
||||
static signIn(name, passwd, cb) {
|
||||
client.emit("user.signIn", {
|
||||
@@ -29,9 +30,9 @@ class CurrentUser {
|
||||
}
|
||||
/**
|
||||
* 注册账号
|
||||
* @param {String} name
|
||||
* @param {String} passwd
|
||||
* @param {Function} callback
|
||||
* @param { String } name
|
||||
* @param { String } passwd
|
||||
* @param { Function } callback
|
||||
*/
|
||||
static signUp(name, passwd, cb) {
|
||||
client.emit("user.signUp", {
|
||||
@@ -46,8 +47,8 @@ class CurrentUser {
|
||||
}
|
||||
/**
|
||||
* 登录对话框中的登录逻辑
|
||||
* @param {String} name
|
||||
* @param {String} passwd
|
||||
* @param { String } name
|
||||
* @param { String } passwd
|
||||
*/
|
||||
static signInWithDialog(name, passwd) {
|
||||
this.signIn(name, passwd, (re) => {
|
||||
@@ -59,8 +60,8 @@ class CurrentUser {
|
||||
}
|
||||
/**
|
||||
* 设置昵称
|
||||
* @param {String} nick
|
||||
* @param {Function} callback
|
||||
* @param { String } nick
|
||||
* @param { Function } callback
|
||||
*/
|
||||
static async setNick(nick, cb) {
|
||||
client.emit("user.setNick", {
|
||||
@@ -75,16 +76,16 @@ class CurrentUser {
|
||||
}
|
||||
/**
|
||||
* 获取用户头像的链接
|
||||
* @param {String} name
|
||||
* @returns {String} headImageUrl
|
||||
* @param { String } name
|
||||
* @returns { String } headImageUrl
|
||||
*/
|
||||
static getUserHeadUrl(name) {
|
||||
return client.io.uri + "/users_head/" + name + ".png"
|
||||
}
|
||||
/**
|
||||
* 获取访问密钥
|
||||
* @param {String} name
|
||||
* @returns {Promise<String>} accessToken
|
||||
* @param { String } name
|
||||
* @returns { Promise<String> } accessToken
|
||||
*/
|
||||
static async getAccessToken(er) {
|
||||
if (this.myAccessToken == null)
|
||||
@@ -104,7 +105,7 @@ class CurrentUser {
|
||||
}
|
||||
/**
|
||||
* 上传头像回调事件
|
||||
* @param {Element} element
|
||||
* @param { Element } element
|
||||
*/
|
||||
static async uploadHeadImageCallback(self) {
|
||||
let img = self.files[0]
|
||||
@@ -169,7 +170,7 @@ class CurrentUser {
|
||||
}
|
||||
/**
|
||||
* 打开资料卡
|
||||
* @param {String} name
|
||||
* @param { String } name
|
||||
*/
|
||||
static async openProfileDialog(name) {
|
||||
viewBinding.dialogProfileHead.attr("src", CurrentUser.getUserHeadUrl(name))
|
||||
@@ -186,8 +187,8 @@ class NickCache {
|
||||
static data = {}
|
||||
/**
|
||||
* 获取昵称
|
||||
* @param {String} name
|
||||
* @returns {String} nick
|
||||
* @param { String } name
|
||||
* @returns { String } nick
|
||||
*/
|
||||
static async getNick(name) {
|
||||
return await new Promise((res, _rej) => {
|
||||
@@ -227,7 +228,7 @@ class ContactsList {
|
||||
for (let index in ls) {
|
||||
let name = ls[index]
|
||||
let dick = await NickCache.getNick(name)
|
||||
$($.parseHTML(`<li class="mdui-list-item mdui-ripple" mdui-drawer-close><div class="mdui-list-item-avatar"><img src="${ CurrentUser.getUserHeadUrl(name) }" onerror="this.src='res/default_head.png'" /></div><div class="mdui-list-item-content">` + dick + `</div></li>`)).appendTo(viewBinding.contactsList).click(() => {
|
||||
$($.parseHTML(`<li class="mdui-list-item mdui-ripple" mdui-drawer-close><div class="mdui-list-item-avatar"><img src="${CurrentUser.getUserHeadUrl(name)}" onerror="this.src='res/default_head.png'" /></div><div class="mdui-list-item-content">` + dick + `</div></li>`)).appendTo(viewBinding.contactsList).click(() => {
|
||||
ChatMsgAdapter.switchTo(name, "single")
|
||||
})
|
||||
}
|
||||
@@ -236,7 +237,7 @@ class ContactsList {
|
||||
}
|
||||
/**
|
||||
* 添加联系人/群峦
|
||||
* @param {String} nameOrId
|
||||
* @param { String } nameOrId
|
||||
*/
|
||||
static async add(name, type) {
|
||||
if (type == "single") {
|
||||
@@ -262,6 +263,8 @@ class ContactsList {
|
||||
// 消息核心
|
||||
// ================================
|
||||
|
||||
// 自古框架BUG多, 各种麻烦遭不住
|
||||
|
||||
class ChatTabManager {
|
||||
static tabs = {}
|
||||
/**
|
||||
@@ -270,7 +273,14 @@ class ChatTabManager {
|
||||
* @param { String } target
|
||||
*/
|
||||
static add(title, target) {
|
||||
$($.parseHTML(`<a onclick="ChatMsgAdapter.switchTo('${target}');" tag="chatTab" id="chatTab_${target}" class="mdui-ripple" style="text-transform: none;">${title}</a>`)).appendTo(viewBinding.chatTab)
|
||||
if (this.tabs[target]) return
|
||||
let tabElement = $($.parseHTML(`<a onclick="ChatMsgAdapter.switchTo('${target}');" tag="chatTab" id="chatTab_${target}" class="mdui-ripple" style="text-transform: none;">${title}</a>`))
|
||||
tabElement.appendTo(viewBinding.chatTab)
|
||||
// 就你MDUI的B事最多 加Tab还多一个下划线 删掉就解决了
|
||||
$(".mdui-tab-indicator").remove()
|
||||
new mdui.Tab(viewBinding.chatTab).handleUpdate()
|
||||
this.tabs[target] = tabElement
|
||||
if (Object.keys(this.tabs).length == 1) tabElement.addClass("mdui-tab-active")
|
||||
}
|
||||
/**
|
||||
* 寻找Tab
|
||||
@@ -278,14 +288,14 @@ class ChatTabManager {
|
||||
* @returns { jQuery } element
|
||||
*/
|
||||
static find(target) {
|
||||
return $("#chatTab_" + target)
|
||||
return this.tabs[target]
|
||||
}
|
||||
/**
|
||||
* 点击Tab
|
||||
* @param { String } target
|
||||
*/
|
||||
static click(target) {
|
||||
this.find(this.target).click()
|
||||
this.find(target).get(0).click()
|
||||
}
|
||||
/**
|
||||
* 删除Tab
|
||||
@@ -293,6 +303,64 @@ class ChatTabManager {
|
||||
*/
|
||||
static remove(target) {
|
||||
this.find(target).remove()
|
||||
this.tabs[target] = null
|
||||
}
|
||||
static initTabElementEvents() {
|
||||
let listeners = {}
|
||||
let menu
|
||||
let callback = (e) => {
|
||||
if (menu) menu.close()
|
||||
// 切到 div.message-content
|
||||
let ele = e.get(0)
|
||||
while ($(ele).attr("tag") != "msg-card")
|
||||
ele = ele.parentNode
|
||||
e = $(ele)
|
||||
let menuHtml = $.parseHTML(`<ul class="mdui-menu menu-on-message">
|
||||
<li class="mdui-menu-item">
|
||||
<a onclick="copyText(\`${e.find("#msg-content").text()}\`)" class="mdui-ripple">复制</a>
|
||||
</li>
|
||||
<li class="mdui-menu-item">
|
||||
<a onclick="mdui.alert(\`${e.find("#raw-msg-content").text()}\`, '消息原文', () => { }, { confirmText: '关闭' })" class="mdui-ripple">原文</a>
|
||||
</li>
|
||||
<li class="mdui-menu-item">
|
||||
<a onclick="mdui.alert('未制作功能', '提示', () => { }, { confirmText: '关闭' })" class="mdui-ripple">转发</a>
|
||||
</li>
|
||||
</ul>`)
|
||||
let $menu = $(menuHtml)
|
||||
e.before($menu)
|
||||
menu = new mdui.Menu(e.get(0), menuHtml, {
|
||||
position: "bottom",
|
||||
align: "auto",
|
||||
// covered: true,
|
||||
})
|
||||
$menu.on('closed.mdui.menu', () => {
|
||||
$(menuHtml).remove()
|
||||
})
|
||||
menu.open()
|
||||
}
|
||||
viewBinding.pageChatSeesion.on('contextmenu mousedown mouseup', '.message-content', (e) => {
|
||||
let eventType = e.type
|
||||
let self = $(e.target)
|
||||
|
||||
// 根据事件类型执行不同操作
|
||||
switch (eventType) {
|
||||
case 'contextmenu':
|
||||
e.preventDefault() // 阻止默认行为
|
||||
callback(self)
|
||||
break
|
||||
case 'mousedown':
|
||||
if (!isMobile()) return
|
||||
listeners[self + ""] = setTimeout(() => {
|
||||
callback(self)
|
||||
}, 300) // 300颗够吗 应该够吧
|
||||
break
|
||||
case 'mouseup':
|
||||
if (!isMobile()) return
|
||||
clearTimeout(listeners[self + ""])
|
||||
listeners[self + ""] = null
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +371,9 @@ class ChatPage {
|
||||
this.chatType = type
|
||||
ChatTabManager.add(title, this.chatTarget)
|
||||
this.chatPageElement = $($.parseHTML(`<div class="chat-seesion" id="chatPageTargetIs${this.chatTarget}" target="${this.chatTarget}"></div>`))
|
||||
this.chatPageElement.hide()
|
||||
this.chatPageElement.appendTo(viewBinding.pageChatSeesion)
|
||||
;(async () => await this.loadMore())()
|
||||
}
|
||||
/**
|
||||
* 获取当前的聊天栏
|
||||
@@ -316,7 +386,7 @@ class ChatPage {
|
||||
* 获取当前聊天页面
|
||||
* @returns { ChatPage }
|
||||
*/
|
||||
static getCurrentChatPage(name) {
|
||||
static getCurrentChatPage() {
|
||||
return ChatPage.cached[$(".chat-seesion[actived=true]").attr("target")]
|
||||
}
|
||||
/**
|
||||
@@ -331,17 +401,13 @@ class ChatPage {
|
||||
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")
|
||||
|
||||
ChatTabManager.click(this.chatTarget)
|
||||
|
||||
$(this.chatPageElement).show()
|
||||
ChatTabManager.find(this.chatTarget).addClass("mdui-tab-active")
|
||||
|
||||
await this.loadMore()
|
||||
ChatMsgAdapter.scrollToBottom()
|
||||
}
|
||||
/**
|
||||
* 连带Tab一起销毁
|
||||
@@ -353,7 +419,7 @@ class ChatPage {
|
||||
}
|
||||
/**
|
||||
* 加载更多聊天记录
|
||||
* @param {int} 加载数量
|
||||
* @param { int } 加载数量
|
||||
*/
|
||||
async loadMore(limit) {
|
||||
let histroy = await this.getHistroy(this.minMsgId, limit == null ? 13 : limit)
|
||||
@@ -362,25 +428,26 @@ class ChatPage {
|
||||
if (histroy.length == 0)
|
||||
return mdui.snackbar("已经加载完了~")
|
||||
|
||||
let re = this.minMsgId != null
|
||||
let doReverse = this.minMsgId != null
|
||||
this.minMsgId = histroy[0].msgid - 1
|
||||
let sc = 0
|
||||
if (re) histroy = histroy.reverse()
|
||||
// 英语水平不够(
|
||||
let scroll幅度 = 0
|
||||
if (doReverse) 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)
|
||||
let msgElement = await this.addMsg(i.name, i.msg, i.time, doReverse, i.msgid)
|
||||
// 因为某些因素直接DEBUG到吐血 断点继续都不报错 原因不明
|
||||
sc = sc + (e == null ? 35 : getOffsetTop(chatPager, e.get(0)))
|
||||
scroll幅度 = scroll幅度 + (msgElement == null ? 35 : getOffsetTop(chatPager, msgElement.get(0)))
|
||||
}
|
||||
chatPager.scrollBy({
|
||||
top: sc,
|
||||
top: scroll幅度,
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 获取聊天消息记录
|
||||
* @param {int} 起始点
|
||||
* @param {int} 获取数量
|
||||
* @param { int } 起始点
|
||||
* @param { int } 获取数量
|
||||
*/
|
||||
async getHistroy(start, limit) {
|
||||
if (this.chatType == "single")
|
||||
@@ -517,8 +584,8 @@ class ChatMsgAdapter {
|
||||
static resizeDick
|
||||
/**
|
||||
* 切换到某一个聊天对象
|
||||
* @param {String} name
|
||||
* @param {String} type
|
||||
* @param { String } name
|
||||
* @param { String } type
|
||||
*/
|
||||
static async switchTo(name, type) {
|
||||
if (!ChatPage.cached[name])
|
||||
@@ -528,7 +595,7 @@ class ChatMsgAdapter {
|
||||
}
|
||||
/**
|
||||
* 是否在底部
|
||||
* @returns {Boolean} 是否在底部
|
||||
* @returns { Boolean } 是否在底部
|
||||
*/
|
||||
static isAtBottom() {
|
||||
let elementRect = viewBinding.pageChatSeesion.get(0).getBoundingClientRect()
|
||||
|
||||
@@ -121,7 +121,6 @@
|
||||
<div class="mdui-tab mdui-accent-theme mdui-theme-color-auto" style="position: fixed; z-index: 114;width: 100%;"
|
||||
mdui-tab n-id="chatTab">
|
||||
<!-- 侧滑栏的 z-index 是2000, 在移动端会直接覆盖 -->
|
||||
<!-- <a href="#page-chat-seesion" n-id="tabChatSeesion" tag="chatTab" class="mdui-ripple" style="text-transform: none;"></a> -->
|
||||
</div>
|
||||
<!-- 滚动到底部咋这么难写... -->
|
||||
<div style="display: flex;flex-direction: column;">
|
||||
|
||||
24
readme.md
24
readme.md
@@ -1,31 +1,29 @@
|
||||
## 铃之椅
|
||||
[ 中文 | [English](readme_en.md) ]
|
||||
|
||||
<div align="center">
|
||||
<h2> 铃之椅 </h2>
|
||||
</div>
|
||||
|
||||
欢迎来到铃之椅! 这是一个即时通讯项目, 为通讯提供更多的选择, 为人民服务
|
||||
|
||||
> [!NOTE]
|
||||
> 本项目仍在实验阶段, [点我](final.md)可查看进展
|
||||
>
|
||||
> 如果有任何问题,欢迎你提出来,我会不定时查看
|
||||
>
|
||||
> 另外 Android 客户端也在开发, 但进展缓慢
|
||||
> 欢迎各位提出项目修改意见
|
||||
|
||||
### 使用
|
||||
|
||||
服务端:
|
||||
|
||||
0. 确保安装了 Node.js
|
||||
0. 安装 Node.js
|
||||
|
||||
1. 克隆本仓库源代码到本地,并运行 run_build.sh 构建网页
|
||||
1. 克隆或下载本仓库源代码,执行`npm install`,再执行`npm run start` 或者运行 run.bat / run.sh
|
||||
|
||||
2. 运行 run.sh
|
||||
客户端:
|
||||
|
||||
网页端:
|
||||
|
||||
* 直接使用和服务端集成的网页 (推荐)
|
||||
|
||||
* 克隆本仓库到本地并运行本地 HTTP 服务端
|
||||
* 使用服务端提供的网页 (推荐)
|
||||
|
||||
* 静态网页 (不推荐)
|
||||
* GitHub Pages (可能导致跨域问题)
|
||||
|
||||
### [Q&A](.github/QA.md)
|
||||
|
||||
|
||||
36
readme_en.md
Normal file
36
readme_en.md
Normal file
@@ -0,0 +1,36 @@
|
||||
[ [中文](readme.md) | English ]
|
||||
|
||||
<div align="center">
|
||||
<h2> LingChair </h2>
|
||||
</div>
|
||||
|
||||
Welcome to LingChair! This is an IM project that it provide a new communication way. And it serves people.
|
||||
|
||||
> [!NOTE]
|
||||
I'm sorry that I have no time to translate this project to English. This project is so large for me to rewrite, so there're still a lot of texts in Chinese.
|
||||
>
|
||||
> This project is still in progress, [Click me](final.md) to view progress. (Chinese)
|
||||
>
|
||||
> Comments and BUG report welcome
|
||||
|
||||
### Usage
|
||||
|
||||
Server:
|
||||
|
||||
0. Install Node.js
|
||||
|
||||
1. Clone this repo or download the source code, then run `npm install`, after that, run `npm run start` or run.bat / run.sh
|
||||
|
||||
Client:
|
||||
|
||||
* Use the pages that the server provide (Recommend)
|
||||
|
||||
* GitHub Pages (It may cause CORS security error)
|
||||
|
||||
### [Q&A (Chinese)](.github/QA.md)
|
||||
|
||||
### Credits
|
||||
|
||||
WIP
|
||||
|
||||
### [Do you know? (Chinese)](.github/do_you_know.md)
|
||||
Reference in New Issue
Block a user