1
0
Fork 0
mirror of https://github.com/Luzifer/share.git synced 2024-12-29 23:11:18 +00:00
share/frontend-src/display.vue
Knut Ahlers 549f0d1f36
Port frontend to Vue 3 / Bootstrap 5.3
Signed-off-by: Knut Ahlers <knut@ahlers.me>
2024-03-18 20:22:31 +01:00

220 lines
5.3 KiB
Vue

<template>
<div>
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a
class="navbar-brand"
href="#"
@click.prevent=""
>
<i class="fas fa-fw fa-share-alt-square mr-1" /> Share
</a>
</div>
</nav>
<div class="container mt-4">
<div class="row">
<div class="col">
<div
v-if="loading"
class="card"
>
<div class="card-body text-center">
<h2><i class="fas fa-spinner fa-pulse" /></h2>
{{ $t('loading') }}
</div>
</div>
<template v-else>
<div
v-if="error"
class="card text-bg-danger"
>
<div class="card-body text-center">
<h2><i class="fas fa-exclamation-circle" /></h2>
{{ error }}
</div>
</div>
<div
v-else-if="fileType.startsWith('image/')"
class="card"
>
<div class="card-body text-center">
<a :href="path">
<img
:src="path"
class="img-fluid"
>
</a>
</div>
</div>
<div
v-else-if="fileType.startsWith('video/')"
class="card"
>
<div class="card-body text-center">
<div class="ratio ratio-16x9">
<video controls>
<source :src="path">
</video>
</div>
</div>
</div>
<div
v-else-if="fileType.startsWith('audio/')"
class="card"
>
<div class="card-body text-center">
<audio
controls
:src="path"
/>
</div>
</div>
<div
v-else-if="fileType.startsWith('text/markdown')"
class="card"
>
<div
class="card-body"
v-html="renderMarkdown(text)"
/>
</div>
<div
v-else-if="fileType.startsWith('text/')"
class="card"
>
<div class="card-body">
<pre><code>{{ text }}</code></pre>
</div>
</div>
<div
v-else
class="card"
>
<div class="card-body text-center">
<h2><i class="fas fa-cloud-download-alt" /></h2>
<button class="btn btn-success">
{{ fileName }}
</button>
</div>
</div>
</template>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import hljs from 'highlight.js'
import showdown from 'showdown'
const rewrites = {
'application/javascript': 'text/javascript',
}
export default defineComponent({
data() {
return {
error: null,
fileName: '',
fileType: null,
loading: true,
path: '',
text: '',
}
},
methods: {
hashChange() {
const hash = window.location.hash
if (hash.length > 0) {
this.path = hash.substring(1)
} else {
this.error = this.$i18n.t('fileNotFound')
this.loading = false
}
},
renderMarkdown(text) {
return new showdown.Converter().makeHtml(text)
},
},
mounted() {
window.onhashchange = this.hashChange
this.hashChange()
},
name: 'ShareContentDisplay',
watch: {
fileType(v) {
// Rewrite known file types not matching the expectations above
if (rewrites[v]) {
this.fileType = rewrites[v]
return
}
// Load text files directly and highlight them
if (v.startsWith('text/')) {
this.loading = true
fetch(this.path)
.then(resp => resp.text())
.then(text => {
this.text = text
if (this.text.length < 200 * 1024 && v !== 'text/plain') {
// Only highlight up to 200k and not on text/plain
window.setTimeout(() => hljs.initHighlighting(), 100)
}
this.loading = false
})
.catch(err => console.log(err))
}
},
path() {
if (this.path.indexOf('://') >= 0) {
// Strictly disallow loading files having any protocol in them
this.error = this.$i18n.t('notPermitted')
this.loading = false
return
}
fetch(this.path, {
method: 'HEAD',
})
.then(resp => {
this.loading = false
switch (resp.status) {
case 200:
break
case 403:
this.error = this.$i18n.t('notPermitted')
return
case 404:
this.error = this.$i18n.t('fileNotFound')
return
default:
this.error = this.$i18n.t('genericError', { status: resp.status })
return
}
this.fileType = resp?.headers?.get('content-type') || 'application/octet-stream'
this.fileName = this.path.substring(this.path.lastIndexOf('/') + 1)
})
},
},
})
</script>