Closed itohatweb closed 5 months ago
I have this ejs code from an old project I used with a web component, would love to see this integrated here as I much prefer using React to build my UIs and I hope it can help others :)
EJS:
<% const textRegex = /\.(?:txt|md|log|c\+\+|cpp|cc|c|h|hpp|mm|m|json|js|jsx|rb|rake|py|asm|fs|cgi|bat|rss|java|graphml|idb|lua|o|gml|prl|sls|conf|cmake|make|sln|vbe|cxx|wbf|vbs|r|wml|php|bash|applescript|fcgi|yaml|ex|exs|sh|ml|actionscript|html|xhtml|htm|xml|xls|xsd|css|styl|scss|go)$/i%>
<% const isAudio = /\.(mp3|ogg|wav|flac)$/i.test(attachment.name) && attachment.url %>
<message-attachment class='<%= isAudio ? ' audio' : '' %>'>
<div class='data'>
<img src='https://discord.com/assets/<%= attachment.iconHash %>.svg' alt='' class='icon'/>
<div class='details'>
<a href='<%= attachment.url %>' target='_blank'><%= attachment.name %></a>
<span><%= attachment.formattedBytes %></span>
</div>
<% if (textRegex.test(attachment.name)) { %>
<div class='preview'>
<svg width='20' height='20' viewBox='0 0 18 20'>
<path fill='currentColor'
d='M15 15H3V13H15Zm0-4H3V9H15Zm0-4H3V5H15ZM0 20l1.5-1.5L3 20l1.5-1.5L6 20l1.5-1.5L9 20l1.5-1.5L12 20l1.5-1.5L15 20l1.5-1.5L18 20V0L16.5 1.5 15 0 13.5 1.5 12 0 10.5 1.5 9 0 7.5 1.5 6 0 4.5 1.5 3 0 1.5 1.5 0 0Z'/>
</svg>
</div>
<% } %>
<a href='<%= attachment.url %>' target='_blank' class='download'>
<svg width='24' height='24' viewBox='0 0 24 24'>
<path fill='currentColor' d='M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z'/>
</svg>
</a>
</div>
<% if (isAudio) { %>
<audio src='<%= attachment.url %>' controls controlsList='nodownload'></audio>
<% } %>
</message-attachment>
Web Component JS:
import e from '../utils/createElement'
class MessageAttachment extends HTMLElement {
constructor () {
super()
this.onClick = this.onClick.bind(this)
}
connectedCallback () {
const el = this.querySelector('.preview')
if (el) el.addEventListener('click', this.onClick)
}
async onClick () {
this.renderModal('LOADING')
const file = this.querySelector('a').href
const res = await fetch(file) //.replace('https://cdn.discordapp.com', window.GLOBAL_ENV.HOSTNAME)
if (!res.ok) {
return this.renderModal('ERRORED')
}
this.renderModal('FETCHED', await res.text())
}
renderModal (state, contents) {
const name = this.querySelector('a').textContent
let container = document.querySelector('.modal-container')
if (!container) {
const close = () => {
const el = document.querySelector('.modal-container')
el.classList.add('leaving')
setTimeout(() => el.remove(), 150)
}
container = e('div', {
class: 'modal-container entering',
bindEvents: { click: close }
}, e('div', {
class: 'modal-inner',
bindEvents: { click: e => e.stopPropagation() }
}, e('div', { class: 'modal' }, [
e('div', { class: 'modal-header' }, 'Attachment'),
e('div', { class: 'modal-body' }, [
e('div', null, [ e('b', null, 'name:'), ` ${name}` ]),
e('div', null, [ e('b', null, 'File size:'), ` ${this.querySelector('span').textContent}` ]),
e('div', { class: 'attachment-details' })
]),
e('div', { class: 'modal-footer' }, [
e('button', { bindEvents: { click: close } }, 'Got it'),
e('a', {
href: this.querySelector('a').href,
target: '_blank'
}, 'Download')
])
])))
document.body.appendChild(container)
}
let el = null
switch (state) {
case 'LOADING':
el = e('div', { class: 'spinner' })
break
case 'FETCHED':
el = e('div', { class: 'contents' }, [
e('div', { class: 'lang' }, name.split('.').pop()),
e('div', { class: 'shitcode' }, [
e('div', { class: 'lines' }),
e('code', null, contents)
]),
e('div', { class: 'copy' }, 'Copy')
])
break
case 'ERRORED':
el = e('div', { class: 'error' }, 'Failed to load attachment contents.')
break
}
const inner = container.querySelector('.attachment-details')
inner.innerHTML = ''
inner.appendChild(el)
}
}
customElements.define('message-attachment', MessageAttachment)
SCSS:
message-attachment {
display: block;
border: 1px solid;
max-width: 520px;
width: 100%;
padding: 10px;
border-radius: 3px;
border-color: rgba(47, 49, 54, .6);
background-color: rgba(47, 49, 54, .3);
&.audio {
max-width: 400px;
}
.data {
display: flex;
align-items: center;
.icon {
width: 30px;
height: 40px;
margin-right: 8px;
margin-top: 0;
flex-shrink: 0;
}
.details {
flex: 1;
display: flex;
flex-direction: column;
a {
opacity: .85;
text-overflow: ellipsis;
overflow: hidden;
line-height: 16px;
}
span {
font-weight: 300;
color: #72767d;
margin-right: 8px;
line-height: 16px;
font-size: 12px;
}
}
.preview, .download {
display: flex;
align-items: center;
justify-content: center;
color: #4f545c;
width: 24px;
height: 24px;
cursor: pointer;
}
}
audio {
width: 100%;
margin-top: 10px;
}
}
audio {
height: 32px;
width: 380px;
border-radius: 3px;
// noinspection CssInvalidPseudoSelector
&::-webkit-media-controls-enclosure {
border-radius: 3px;
}
}
I also have it for videos.
EJS:
<div class='decorated-video'
style='width: <%= attachment.displayMaxWidth %>; height: <%= attachment.displayMaxHeight %>'>
<div class='metadata'>
<div class='details'>
<span class='name'><%= attachment.name %></span>
<span class='size'><%= attachment.formattedBytes %></span>
</div>
<a href='<%= attachment.url %>' target='_blank' class='download'>
<svg width='24' height='24' viewBox='0 0 24 24'>
<path fill='currentColor' d='M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z'/>
</svg>
</a>
</div>
<video src='<%= attachment.url %>' poster='<%= attachment.proxyURL %>?format=jpeg' controls
controlsList='nodownload' disablePictureInPicture></video>
</div>
SCSS:
.decorated-video {
width: 380px;
max-height: 540px;
position: relative;
overflow: hidden;
.metadata {
z-index: 666;
position: absolute;
top: 0;
right: 0;
left: 0;
display: flex;
background-image: linear-gradient(0deg, transparent, rgba(0, 0, 0, .9));
padding: 12px;
transform: translateY(-100%);
transition: transform .15s;
color: #fff;
.details {
flex: 1;
display: flex;
flex-direction: column;
.name, .size {
font-weight: 500;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.name {
font-size: 16px;
line-height: 20px;
}
.size {
font-size: 12px;
line-height: 16px;
opacity: .7;
}
}
.download {
opacity: .6;
color: #fff;
}
}
video {
width: 100%;
}
&:hover .metadata {
transform: translateY(0);
}
}
Added in v4.0.0-alpha.20
Use the discord-file-attachment
component
Describe the solution you'd like A way to add non image attachments, aka. classic text files for example:
Describe alternatives you've considered None