mirror of
https://github.com/LingChair/LingChair-V0.git
synced 2025-12-08 18:15:50 +08:00
feat: 右键消息菜单, change: 服务端配置文件位置, fix: 美化造成的对话框按钮CSS异常
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"appTitle": "",
|
"appTitle": "",
|
||||||
"canChangeServer": false
|
"canChangeServer": true
|
||||||
}
|
}
|
||||||
@@ -39,4 +39,7 @@ body {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-on-message {
|
||||||
|
margin-top: 60px;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,14 +32,14 @@
|
|||||||
<!-- <script src='https://polyfill.io/v3/polyfill.min.js?features=default%2Cdom4%2Ces2015%2Ces2016%2Ces2017%2Ces2018%2Ces2019%2Ces2020%2Ces2021%2Ces2022%2Ces5%2Ces6%2Ces7'></script>
|
<!-- <script src='https://polyfill.io/v3/polyfill.min.js?features=default%2Cdom4%2Ces2015%2Ces2016%2Ces2017%2Ces2018%2Ces2019%2Ces2020%2Ces2021%2Ces2022%2Ces5%2Ces6%2Ces7'></script>
|
||||||
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> -->
|
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> -->
|
||||||
|
|
||||||
<!-- 代替私人 fixed 并提供更好的兼容性 -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/gh/wilddeer/stickyfill@2.1.0/dist/stickyfill.min.js"></script>
|
|
||||||
|
|
||||||
<!-- Styles -->
|
<!-- Styles -->
|
||||||
<link rel="stylesheet" href="https://unpkg.com/mdui@1.0.2/dist/css/mdui.min.css" />
|
<link rel="stylesheet" href="https://unpkg.com/mdui@1.0.2/dist/css/mdui.min.css" />
|
||||||
<link rel="stylesheet" href="index.css" />
|
<link rel="stylesheet" href="index.css" />
|
||||||
<link rel="stylesheet" href="chat-message.css" />
|
<link rel="stylesheet" href="chat-message.css" />
|
||||||
<link rel="stylesheet" href="mdui-prettier.css" />
|
<link rel="stylesheet" href="mdui-prettier.css" />
|
||||||
|
|
||||||
|
<!-- 代替私人 fixed 并提供更好的兼容性 -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/gh/wilddeer/stickyfill@2.1.0/dist/stickyfill.min.js"></script>
|
||||||
<script src="https://unpkg.com/jquery@3.7.1/dist/jquery.min.js"></script>
|
<script src="https://unpkg.com/jquery@3.7.1/dist/jquery.min.js"></script>
|
||||||
<link rel="icon" href="icon.ico" />
|
<link rel="icon" href="icon.ico" />
|
||||||
<title>铃之椅</title>
|
<title>铃之椅</title>
|
||||||
@@ -49,6 +49,8 @@
|
|||||||
class="mdui-theme-primary-teal mdui-theme-accent-teal mdui-drawer-body-left mdui-appbar-with-toolbar mdui-theme-layout-auto"
|
class="mdui-theme-primary-teal mdui-theme-accent-teal mdui-drawer-body-left mdui-appbar-with-toolbar mdui-theme-layout-auto"
|
||||||
id="app">
|
id="app">
|
||||||
|
|
||||||
|
<input n-id="textCopier" class="mdui-hidden" />
|
||||||
|
|
||||||
<div id="lingchair-app" style="height: 100%;">
|
<div id="lingchair-app" style="height: 100%;">
|
||||||
<!-- 侧滑栏 -->
|
<!-- 侧滑栏 -->
|
||||||
<div class="mdui-drawer" id="main-drawer">
|
<div class="mdui-drawer" id="main-drawer">
|
||||||
@@ -139,7 +141,7 @@
|
|||||||
<!-- 妈的黑化了 私人玩意这么难整 早知道 z-index 弄死它得了 浪费我时间 我就没试过这么离谱的样式表 第三方库真难写CSS 就应该先写后端的 啊啊啊啊啊啊 -->
|
<!-- 妈的黑化了 私人玩意这么难整 早知道 z-index 弄死它得了 浪费我时间 我就没试过这么离谱的样式表 第三方库真难写CSS 就应该先写后端的 啊啊啊啊啊啊 -->
|
||||||
<!-- 不黑化了 因为 stickyfill -->
|
<!-- 不黑化了 因为 stickyfill -->
|
||||||
<div class="mdui-toolbar mdui-theme-color-auto"
|
<div class="mdui-toolbar mdui-theme-color-auto"
|
||||||
style="position: sticky;max-width: 100%;margin-bottom: -30px;bottom: 0;">
|
style="position: sticky;max-width: 100%;margin-bottom: -30px;bottom: 0;z-index: 101;">
|
||||||
<ul class="mdui-menu" id="msg-input-more">
|
<ul class="mdui-menu" id="msg-input-more">
|
||||||
<li class="mdui-menu-item">
|
<li class="mdui-menu-item">
|
||||||
<a class="mdui-ripple">插入图片</a>
|
<a class="mdui-ripple">插入图片</a>
|
||||||
|
|||||||
@@ -21,6 +21,28 @@
|
|||||||
|
|
||||||
const UrlArgs = new URL(location.href).searchParams
|
const UrlArgs = new URL(location.href).searchParams
|
||||||
|
|
||||||
|
function setOnRightClick(e, cb) {
|
||||||
|
if (!(e instanceof jQuery))
|
||||||
|
e = $(e)
|
||||||
|
|
||||||
|
let longPressTimer
|
||||||
|
if (!cb) throw new Error("定义回调!!!!")
|
||||||
|
e.on('contextmenu', function (e) {
|
||||||
|
e.preventDefault() // 阻止默认右键菜单
|
||||||
|
cb()
|
||||||
|
})
|
||||||
|
|
||||||
|
e.on('mousedown', function () {
|
||||||
|
longPressTimer = setTimeout(function () {
|
||||||
|
cb()
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
|
|
||||||
|
e.on('mouseup', function () {
|
||||||
|
clearTimeout(longPressTimer)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (UrlArgs.get("debug")) {
|
if (UrlArgs.get("debug")) {
|
||||||
let script = document.createElement('script')
|
let script = document.createElement('script')
|
||||||
script.src = "//cdn.jsdelivr.net/npm/eruda"
|
script.src = "//cdn.jsdelivr.net/npm/eruda"
|
||||||
@@ -130,10 +152,7 @@ viewBinding.drawerChangeServer.click(() => {
|
|||||||
|
|
||||||
viewBinding.drawerSignOut.click(() => {
|
viewBinding.drawerSignOut.click(() => {
|
||||||
mdui.confirm('确定要登出账号吗', () => {
|
mdui.confirm('确定要登出账号吗', () => {
|
||||||
localStorage.refreshToken = ""
|
User.signOutAndReload()
|
||||||
localStorage.isSignIn = false
|
|
||||||
|
|
||||||
setTimeout(() => location.reload(), 300)
|
|
||||||
}, () => { }, {
|
}, () => { }, {
|
||||||
confirmText: "确定",
|
confirmText: "确定",
|
||||||
cancelText: "取消"
|
cancelText: "取消"
|
||||||
@@ -157,17 +176,26 @@ viewBinding.dialogSignInPasswd.keydown((e) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
viewBinding.switchNotifications.click((a) => {
|
viewBinding.switchNotifications.click((a) => {
|
||||||
if (localStorage.useNotifications === "true" || localStorage.useNotifications != null) {
|
if ((localStorage.useNotifications == "true" || localStorage.useNotifications != null) && localStorage.useNotifications != "false") {
|
||||||
localStorage.useNotifications = false
|
localStorage.useNotifications = "false"
|
||||||
viewBinding.switchNotificationsIcon.text("notifications_off")
|
viewBinding.switchNotificationsIcon.text("notifications_off")
|
||||||
} else {
|
} else {
|
||||||
localStorage.useNotifications = true
|
localStorage.useNotifications = "true"
|
||||||
viewBinding.switchNotificationsIcon.text("notifications")
|
viewBinding.switchNotificationsIcon.text("notifications")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (localStorage.useNotifications === "true")
|
if (localStorage.useNotifications == "true")
|
||||||
viewBinding.switchNotificationsIcon.text("notifications")
|
viewBinding.switchNotificationsIcon.text("notifications")
|
||||||
|
|
||||||
|
// https://www.runoob.com/w3cnote/javascript-copy-clipboard.html
|
||||||
|
function copyText(t) {
|
||||||
|
let cp = viewBinding.textCopier.get(0)
|
||||||
|
cp.value = t
|
||||||
|
cp.select()
|
||||||
|
cp.setSelectionRange(0, 99999)
|
||||||
|
navigator.clipboard.writeText(cp.value)
|
||||||
|
}
|
||||||
|
|
||||||
// https://zhuanlan.zhihu.com/p/162910462
|
// https://zhuanlan.zhihu.com/p/162910462
|
||||||
Date.prototype.format = function (tms, format) {
|
Date.prototype.format = function (tms, format) {
|
||||||
let tmd = new Date(tms)
|
let tmd = new Date(tms)
|
||||||
@@ -335,7 +363,7 @@ class ChatMsgAdapter {
|
|||||||
// 错了 应该是客户端少发条才对 不然不能多设备同步
|
// 错了 应该是客户端少发条才对 不然不能多设备同步
|
||||||
if (ChatMsgAdapter.target !== localStorage.userName && ChatMsgAdapter.type === "single") {
|
if (ChatMsgAdapter.target !== localStorage.userName && ChatMsgAdapter.type === "single") {
|
||||||
let i = ChatMsgAdapter.isAtBottom()
|
let i = ChatMsgAdapter.isAtBottom()
|
||||||
await ChatMsgAdapter.addMsg(localStorage.userName, msg, re.data.time)
|
await ChatMsgAdapter.addMsg(localStorage.userName, msg, re.data.time, re.data.msgid)
|
||||||
if (i) ChatMsgAdapter.scrollToBottom()
|
if (i) ChatMsgAdapter.scrollToBottom()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -367,9 +395,9 @@ class ChatMsgAdapter {
|
|||||||
if (re) histroy = histroy.reverse()
|
if (re) histroy = histroy.reverse()
|
||||||
for (let index in histroy) {
|
for (let index in histroy) {
|
||||||
let i = histroy[index]
|
let i = histroy[index]
|
||||||
let e = await this.addMsg(i.name, i.msg, i.time, re, true)
|
let e = await this.addMsg(i.name, i.msg, i.time, re, i.msgid)
|
||||||
// 因为某些因素直接DEBUG到吐血 断点继续都不报错 原因不明
|
// 因为某些因素直接DEBUG到吐血 断点继续都不报错 原因不明
|
||||||
sc = sc + (e == null ? 20 : e.get(0).offsetTop)
|
sc = sc + (e == null ? 25 : e.get(0).offsetTop)
|
||||||
}
|
}
|
||||||
window.scrollBy({
|
window.scrollBy({
|
||||||
top: sc,
|
top: sc,
|
||||||
@@ -385,11 +413,11 @@ class ChatMsgAdapter {
|
|||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
static isAtBottom() {
|
static isAtBottom() {
|
||||||
let elementRect = viewBinding.pageChatSeesion.get(0).getBoundingClientRect();
|
let elementRect = viewBinding.pageChatSeesion.get(0).getBoundingClientRect()
|
||||||
return (elementRect.bottom <= window.innerHeight);
|
return (elementRect.bottom <= window.innerHeight)
|
||||||
}
|
}
|
||||||
// 不会压栈 只添加消息 返回消息的JQ对象
|
// 不会压栈 只添加消息 返回消息的JQ对象
|
||||||
static async addMsg(name, m, t, re) {
|
static async addMsg(name, m, t, re, msgid) {
|
||||||
|
|
||||||
let nick = await NickCache.getNick(name) // re.data == null ? name : re.data.nick
|
let nick = await NickCache.getNick(name) // re.data == null ? name : re.data.nick
|
||||||
|
|
||||||
@@ -400,8 +428,8 @@ class ChatMsgAdapter {
|
|||||||
temp = `<div class="chat-message-right">
|
temp = `<div class="chat-message-right">
|
||||||
<div class="message-content-with-nickname-right">
|
<div class="message-content-with-nickname-right">
|
||||||
<span class="nickname">` + nick + `</span>
|
<span class="nickname">` + nick + `</span>
|
||||||
<div class="message-content mdui-card">
|
<div class="message-content mdui-card" id="msgid_` + msgid + `">
|
||||||
<span>` + msg + `</span>
|
<span id="msg-content">` + msg + `</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<img class="avatar" src="` + User.getUserHeadUrl(name) + `" onerror="this.src='default_head.png'" />
|
<img class="avatar" src="` + User.getUserHeadUrl(name) + `" onerror="this.src='default_head.png'" />
|
||||||
@@ -411,8 +439,8 @@ class ChatMsgAdapter {
|
|||||||
<img class="avatar" src="` + User.getUserHeadUrl(name) + `" onerror="this.src='default_head.png'" />
|
<img class="avatar" src="` + User.getUserHeadUrl(name) + `" onerror="this.src='default_head.png'" />
|
||||||
<div class="message-content-with-nickname-left">
|
<div class="message-content-with-nickname-left">
|
||||||
<span class="nickname">` + nick + `</span>
|
<span class="nickname">` + nick + `</span>
|
||||||
<div class="message-content mdui-card">
|
<div class="message-content mdui-card" id="msgid_` + msgid + `">
|
||||||
<span>` + msg + `</span>
|
<span id="msg-content">` + msg + `</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
@@ -477,6 +505,58 @@ class ChatMsgAdapter {
|
|||||||
static saveToLocal() {
|
static saveToLocal() {
|
||||||
localStorage["chat_msg_" + this.target] = JSON.stringify(this.msgList)
|
localStorage["chat_msg_" + this.target] = JSON.stringify(this.msgList)
|
||||||
}*/
|
}*/
|
||||||
|
// 为消息设置长按/右键事件
|
||||||
|
static initMsgElementEvents() {
|
||||||
|
let listeners = {}
|
||||||
|
let menu
|
||||||
|
let callback = (e) => {
|
||||||
|
if (menu) menu.close()
|
||||||
|
// 从 span 切到 div
|
||||||
|
if (e.get(0).tagName.toLowerCase() != "div") e = $(e.get(0).parentNode)
|
||||||
|
// 从 消息框 切到 更上层
|
||||||
|
e = $(e.get(0).parentNode)
|
||||||
|
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('未制作功能', '提示', () => { }, { confirmText: '关闭' })" class="mdui-ripple">转发</a>
|
||||||
|
</li>
|
||||||
|
</ul>`)
|
||||||
|
let $menu = $(menuHtml)
|
||||||
|
e.before($menu)
|
||||||
|
menu = new mdui.Menu(e.get(0), menuHtml, {
|
||||||
|
position: "bottom",
|
||||||
|
align: "right",
|
||||||
|
// 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':
|
||||||
|
listeners[self + ""] = setTimeout(() => {
|
||||||
|
callback(self)
|
||||||
|
}, 300) // 300颗够吗 应该够吧
|
||||||
|
break
|
||||||
|
case 'mouseup':
|
||||||
|
clearTimeout(listeners[self + ""])
|
||||||
|
listeners[self + ""] = null
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Hash {
|
class Hash {
|
||||||
@@ -562,10 +642,24 @@ class User {
|
|||||||
client.emit("user.auth", { name: localStorage.userName, refreshToken: localStorage.refreshToken }, (re) => {
|
client.emit("user.auth", { name: localStorage.userName, refreshToken: localStorage.refreshToken }, (re) => {
|
||||||
if (re.code !== 0) {
|
if (re.code !== 0) {
|
||||||
console.error(re)
|
console.error(re)
|
||||||
|
if (!re.invalid)
|
||||||
return mdui.snackbar("验证用户失败!")
|
return mdui.snackbar("验证用户失败!")
|
||||||
|
|
||||||
|
mdui.alert("账号刷新令牌已过期, 请重新登录哦", "提示", () => User.signOutAndReload(), {
|
||||||
|
confirmText: "确定",
|
||||||
|
closeOnConfirm: false,
|
||||||
|
closeOnEsc: false,
|
||||||
|
modal: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
static signOutAndReload() {
|
||||||
|
localStorage.refreshToken = ""
|
||||||
|
localStorage.isSignIn = false
|
||||||
|
|
||||||
|
setTimeout(() => location.reload(), 300)
|
||||||
|
}
|
||||||
static registerCallback() {
|
static registerCallback() {
|
||||||
client.on("msg.receive", async (a) => {
|
client.on("msg.receive", async (a) => {
|
||||||
if (checkEmpty([a.target, a.msg, a.type]))
|
if (checkEmpty([a.target, a.msg, a.type]))
|
||||||
@@ -579,7 +673,7 @@ class User {
|
|||||||
|
|
||||||
let n = new 通知().setTitle("新消息 - " + await NickCache.getNick(a.target)).setMessage(a.msg.msg).setIcon(User.getUserHeadUrl(a.target)).show(async () => {
|
let n = new 通知().setTitle("新消息 - " + await NickCache.getNick(a.target)).setMessage(a.msg.msg).setIcon(User.getUserHeadUrl(a.target)).show(async () => {
|
||||||
await ChatMsgAdapter.switchTo(a.target, a.type)
|
await ChatMsgAdapter.switchTo(a.target, a.type)
|
||||||
ChatMsgAdapter.scrollToBottom()
|
location.replace("#msgid_" + a.msg.msgid)
|
||||||
n.close()
|
n.close()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -642,3 +736,5 @@ else {
|
|||||||
|
|
||||||
// 感谢AI的力量
|
// 感谢AI的力量
|
||||||
Stickyfill.add($("*").filter((a, b) => $(b).css('position') === 'sticky'))
|
Stickyfill.add($("*").filter((a, b) => $(b).css('position') === 'sticky'))
|
||||||
|
|
||||||
|
ChatMsgAdapter.initMsgElementEvents()
|
||||||
|
|||||||
@@ -21,16 +21,19 @@ body {
|
|||||||
.mdui-menu-item > a {
|
.mdui-menu-item > a {
|
||||||
padding-right: 3px;
|
padding-right: 3px;
|
||||||
}
|
}
|
||||||
.mdui-dialog-actions a,
|
.mdui-btn:not(.mdui-btn-icon, .mdui-dialog-actions button, .mdui-dialog-actions a) {
|
||||||
.mdui-dialog-actions button {
|
|
||||||
border-radius: 40px;
|
|
||||||
}
|
|
||||||
.mdui-btn {
|
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
.mdui-dialog-actions a,
|
||||||
|
.mdui-dialog-actions button {
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
/* 配色方案 */
|
/* 配色方案 */
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const io = require("./iolib")
|
|||||||
let vals = {}
|
let vals = {}
|
||||||
|
|
||||||
// 配置目录
|
// 配置目录
|
||||||
vals.LINGCHAIR_CONFIG_DIR = "ling_chair_config"
|
vals.LINGCHAIR_CONFIG_DIR = "ling_chair_data"
|
||||||
// HTTP 服务器资源目录
|
// HTTP 服务器资源目录
|
||||||
vals.LINGCHAIR_HTTP_DIR = "ling_chair_http"
|
vals.LINGCHAIR_HTTP_DIR = "ling_chair_http"
|
||||||
// 服务端配置
|
// 服务端配置
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ let api = {
|
|||||||
return cb({ msg: "参数缺失", code: -1 })
|
return cb({ msg: "参数缺失", code: -1 })
|
||||||
|
|
||||||
if (!users.checkRefreshToken(a.name, a.refreshToken))
|
if (!users.checkRefreshToken(a.name, a.refreshToken))
|
||||||
return cb({ code: -1, msg: "刷新令牌错误" })
|
return cb({ code: -1, msg: "刷新令牌错误", invalid: true })
|
||||||
|
|
||||||
log(color.yellow + "客户端 " + client.handshake.address + " 完成了用户 " + a.name + " 的验证" + color.none)
|
log(color.yellow + "客户端 " + client.handshake.address + " 完成了用户 " + a.name + " 的验证" + color.none)
|
||||||
|
|
||||||
@@ -193,7 +193,7 @@ let api = {
|
|||||||
log("尝试向客户端 " + v.handshake.address + " 发送事件 [msg.receive], 参数为 " + JSON.stringify(args))
|
log("尝试向客户端 " + v.handshake.address + " 发送事件 [msg.receive], 参数为 " + JSON.stringify(args))
|
||||||
})
|
})
|
||||||
|
|
||||||
cb({ msg: msg, code: 0, data: { time: time } })
|
cb({ msg: msg, code: 0, data: { time: time, msgid: msgid } })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 单聊获取历史记录
|
// 单聊获取历史记录
|
||||||
|
|||||||
Reference in New Issue
Block a user