Compare commits
10 Commits
c23fdbf310
...
edf35b7dd0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edf35b7dd0 | ||
|
|
de886dcfcc | ||
|
|
67f019713a | ||
|
|
2af396a2b8 | ||
|
|
20f12c97c1 | ||
|
|
15c4bcd48e | ||
|
|
e429bbbcdb | ||
|
|
020fd63c97 | ||
|
|
2771503b6f | ||
|
|
65458cf491 |
@@ -8,6 +8,7 @@ import ReactDOM from 'react-dom/client'
|
|||||||
|
|
||||||
import './ui/custom-elements/chat-image.ts'
|
import './ui/custom-elements/chat-image.ts'
|
||||||
import './ui/custom-elements/chat-video.ts'
|
import './ui/custom-elements/chat-video.ts'
|
||||||
|
import './ui/custom-elements/chat-file.ts'
|
||||||
|
|
||||||
const urlParams = new URL(location.href).searchParams
|
const urlParams = new URL(location.href).searchParams
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ const markedInstance = new marked.Marked({
|
|||||||
if (/uploaded_files\/[A-Za-z0-9]+$/.test(href)) {
|
if (/uploaded_files\/[A-Za-z0-9]+$/.test(href)) {
|
||||||
return ({
|
return ({
|
||||||
Image: `<chat-image src="${href}" alt="${text}"></chat-image>`,
|
Image: `<chat-image src="${href}" alt="${text}"></chat-image>`,
|
||||||
Video: `<chat-video src="${href}" alt="${text}"></chat-video>`,
|
Video: `<chat-video src="${href}"></chat-video>`,
|
||||||
File: `<chat-file src="${href}" alt="${text}"></chat-file>`,
|
File: `<chat-file href="${href}" name="${/^Video|File=(.*)/.exec(text)?.[1] || 'Unnamed file'}"></chat-file>`,
|
||||||
})?.[type] || ``
|
})?.[type] || ``
|
||||||
}
|
}
|
||||||
return ``
|
return ``
|
||||||
@@ -144,7 +144,7 @@ export default function ChatFragment({ target, showReturnButton, onReturnButtonC
|
|||||||
target,
|
target,
|
||||||
data: cachedFiles.current[fileName],
|
data: cachedFiles.current[fileName],
|
||||||
}, 5000)
|
}, 5000)
|
||||||
if (checkApiSuccessOrSncakbar(re, `文件[${fileName}] 上傳失敗`)) return
|
if (checkApiSuccessOrSncakbar(re, `文件[${fileName}] 上傳失敗`)) return setIsMessageSending(false)
|
||||||
text = text.replaceAll('(' + fileName + ')', '(' + re.data!.file_path as string + ')')
|
text = text.replaceAll('(' + fileName + ')', '(' + re.data!.file_path as string + ')')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ export default function ChatFragment({ target, showReturnButton, onReturnButtonC
|
|||||||
target,
|
target,
|
||||||
text,
|
text,
|
||||||
}, 5000)
|
}, 5000)
|
||||||
if (checkApiSuccessOrSncakbar(re, "發送失敗")) return
|
if (checkApiSuccessOrSncakbar(re, "發送失敗")) return setIsMessageSending(false)
|
||||||
inputRef.current!.value = ''
|
inputRef.current!.value = ''
|
||||||
cachedFiles.current = {}
|
cachedFiles.current = {}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -280,6 +280,7 @@ export default function ChatFragment({ target, showReturnButton, onReturnButtonC
|
|||||||
'src',
|
'src',
|
||||||
'alt',
|
'alt',
|
||||||
'href',
|
'href',
|
||||||
|
'name',
|
||||||
],
|
],
|
||||||
}).replaceAll('\n', '<br>')
|
}).replaceAll('\n', '<br>')
|
||||||
const lastDate = date
|
const lastDate = date
|
||||||
@@ -299,11 +300,14 @@ export default function ChatFragment({ target, showReturnButton, onReturnButtonC
|
|||||||
{
|
{
|
||||||
(date.getMinutes() != lastDate.getMinutes() || date.getDate() != lastDate.getDate() || date.getMonth() != lastDate.getMonth() || date.getFullYear() != lastDate.getFullYear())
|
(date.getMinutes() != lastDate.getMinutes() || date.getDate() != lastDate.getDate() || date.getMonth() != lastDate.getMonth() || date.getFullYear() != lastDate.getFullYear())
|
||||||
&& <mdui-tooltip content={`${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日 ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`}>
|
&& <mdui-tooltip content={`${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日 ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`}>
|
||||||
<div>
|
<div style={{
|
||||||
|
fontSize: '87%',
|
||||||
|
marginTop: '10px',
|
||||||
|
}}>
|
||||||
{
|
{
|
||||||
(date.getFullYear() != lastDate.getFullYear() ? `${date.getFullYear()}年` : '')
|
(date.getFullYear() != lastDate.getFullYear() ? `${date.getFullYear()}年` : '')
|
||||||
+ (date.getMonth() != lastDate.getMonth() ? `${date.getMonth() + 1}月` : '')
|
+ `${date.getMonth() + 1}月`
|
||||||
+ (date.getDate() != lastDate.getDate() ? `${date.getDate()}日` : '')
|
+ `${date.getDate()}日`
|
||||||
+ ` ${date.getHours()}:${date.getMinutes()}`
|
+ ` ${date.getHours()}:${date.getMinutes()}`
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -349,7 +353,7 @@ export default function ChatFragment({ target, showReturnButton, onReturnButtonC
|
|||||||
// 即便是 no-cors 還是殘廢, 因此暫時沒有什麽想法
|
// 即便是 no-cors 還是殘廢, 因此暫時沒有什麽想法
|
||||||
const re = await fetch(url)
|
const re = await fetch(url)
|
||||||
const type = re.headers.get("Content-Type")
|
const type = re.headers.get("Content-Type")
|
||||||
if (type?.startsWith("image/"))
|
if (type && re.ok)
|
||||||
addFile(type as string, getFileNameOrRandom(url), re)
|
addFile(type as string, getFileNameOrRandom(url), re)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
snackbar({
|
snackbar({
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ export default function Message({ userId, rawData, renderHTML, message, ...props
|
|||||||
|
|
||||||
const dropDownRef = React.useRef<Dropdown>(null)
|
const dropDownRef = React.useRef<Dropdown>(null)
|
||||||
const messageJsonDialogRef = React.useRef<Dialog>(null)
|
const messageJsonDialogRef = React.useRef<Dialog>(null)
|
||||||
|
useEventListener(messageJsonDialogRef, 'click', (e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
})
|
||||||
|
|
||||||
const [isDropDownOpen, setDropDownOpen] = React.useState(false)
|
const [isDropDownOpen, setDropDownOpen] = React.useState(false)
|
||||||
|
|
||||||
@@ -116,7 +119,10 @@ export default function Message({ userId, rawData, renderHTML, message, ...props
|
|||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: renderHTML
|
__html: renderHTML
|
||||||
}} />
|
}} />
|
||||||
<mdui-menu onClick={(e) => e.stopPropagation()}>
|
<mdui-menu onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
setDropDownOpen(false)
|
||||||
|
}}>
|
||||||
<mdui-menu-item icon="content_copy" onClick={() => copyToClipboard($(dropDownRef.current as HTMLElement).find('#msg').text())}>複製文字</mdui-menu-item>
|
<mdui-menu-item icon="content_copy" onClick={() => copyToClipboard($(dropDownRef.current as HTMLElement).find('#msg').text())}>複製文字</mdui-menu-item>
|
||||||
<mdui-menu-item icon="content_copy" onClick={() => copyToClipboard(rawData)}>複製原文</mdui-menu-item>
|
<mdui-menu-item icon="content_copy" onClick={() => copyToClipboard(rawData)}>複製原文</mdui-menu-item>
|
||||||
<mdui-menu-item icon="info" onClick={() => messageJsonDialogRef.current.open = true}>查看詳情</mdui-menu-item>
|
<mdui-menu-item icon="info" onClick={() => messageJsonDialogRef.current.open = true}>查看詳情</mdui-menu-item>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export default function copyToClipboard(text: string) {
|
export default function copyToClipboard(text: string) {
|
||||||
if (navigator.clipboard)
|
if (navigator.clipboard)
|
||||||
return navigator.clipboard.writeText(text)
|
return navigator.clipboard.writeText(text)
|
||||||
return new Promise((res rej) => {
|
return new Promise((res, rej) => {
|
||||||
if (document.hasFocus()) {
|
if (document.hasFocus()) {
|
||||||
const a = document.createElement("textarea")
|
const a = document.createElement("textarea")
|
||||||
document.body.appendChild(a)
|
document.body.appendChild(a)
|
||||||
@@ -12,7 +12,7 @@ export default function copyToClipboard(text: string) {
|
|||||||
a.select()
|
a.select()
|
||||||
document.execCommand("copy", true)
|
document.execCommand("copy", true)
|
||||||
document.body.removeChild(a)
|
document.body.removeChild(a)
|
||||||
res()
|
res(null)
|
||||||
} else {
|
} else {
|
||||||
rej()
|
rej()
|
||||||
}
|
}
|
||||||
|
|||||||
30
client/ui/custom-elements/chat-file.ts
Normal file
30
client/ui/custom-elements/chat-file.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { $ } from 'mdui/jq'
|
||||||
|
|
||||||
|
customElements.define('chat-file', class extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
connectedCallback() {
|
||||||
|
this.style.display = 'block'
|
||||||
|
const e = new DOMParser().parseFromString(`
|
||||||
|
<a style="width: 100%;height: 100%;">
|
||||||
|
<mdui-card variant="outlined" clickable style="display: flex;align-items: center;">
|
||||||
|
<mdui-icon name="insert_drive_file" style="margin: 13px;font-size: 34px;"></mdui-icon>
|
||||||
|
<span style="margin-right: 13px;"></span>
|
||||||
|
</mdui-card>
|
||||||
|
</a>`, 'text/html').body.firstChild as HTMLElement
|
||||||
|
$(e).find('span').text($(this).attr("name"))
|
||||||
|
const href = $(this).attr('href')
|
||||||
|
$(e).attr('href', href)
|
||||||
|
$(e).attr('target', '_blank')
|
||||||
|
$(e).attr('download', href)
|
||||||
|
e.style.textDecoration = 'none'
|
||||||
|
e.style.color = 'inherit'
|
||||||
|
// deno-lint-ignore no-window
|
||||||
|
e.onclick = (e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
window.open(href, '_blank')
|
||||||
|
}
|
||||||
|
this.appendChild(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -6,12 +6,12 @@ customElements.define('chat-video', class extends HTMLElement {
|
|||||||
}
|
}
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.style.display = 'block'
|
this.style.display = 'block'
|
||||||
const e = new DOMParser().parseFromString(`<video controls>視頻無法播放</video>`, 'text/html').body.firstChild as Node
|
const e = new DOMParser().parseFromString(`<video controls>視頻無法播放</video>`, 'text/html').body.firstChild as HTMLVideoElement
|
||||||
e.style.width = "100%"
|
e.style.width = "100%"
|
||||||
e.style.height = "100%"
|
e.style.height = "100%"
|
||||||
e.style.borderRadius = "var(--mdui-shape-corner-medium)"
|
e.style.borderRadius = "var(--mdui-shape-corner-medium)"
|
||||||
e.alt = $(this).attr('alt') || ""
|
|
||||||
e.src = $(this).attr('src') as string
|
e.src = $(this).attr('src') as string
|
||||||
|
e.onclick = (e) => e.stopPropagation()
|
||||||
this.appendChild(e)
|
this.appendChild(e)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ app.get('/uploaded_files/:hash', (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fileName = encodeURIComponent(file!.getName()?.replaceAll('"', ''))
|
||||||
|
res.setHeader('Content-Disposition', `inline; filename="${fileName}"`)
|
||||||
res.setHeader('Content-Type', file!.getMime())
|
res.setHeader('Content-Type', file!.getMime())
|
||||||
res.sendFile(path.resolve(file!.getFilePath()))
|
res.sendFile(path.resolve(file!.getFilePath()))
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user