This commit is contained in:
parent
fad7acd5f7
commit
bdeeff3794
7 changed files with 334 additions and 136 deletions
|
@ -39,11 +39,12 @@ export default class ArticleRoutes extends OriginalArticleRoutes {
|
|||
ctx.body = {
|
||||
token: Client.createJwt({ iss: media.iss }, media.secret),
|
||||
path: config.get('media:directFilePath'),
|
||||
resize: config.get('media:path'),
|
||||
}
|
||||
}
|
||||
|
||||
/** PUT: /api/auth/articles/:id */
|
||||
async updateCreateArticle(ctx) {
|
||||
return this.private_getUpdateArticle(ctx, ctx.req.body, null, ctx.req.body.media)
|
||||
return this.private_getUpdateArticle(ctx, ctx.req.body, ctx.req.body.banner, ctx.req.body.media)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,13 +21,37 @@ const Input = {
|
|||
this.tempus = null
|
||||
this.subscription = null
|
||||
this.input = null
|
||||
this.preview = null
|
||||
},
|
||||
|
||||
onremove: function(vnode) {
|
||||
if (!this.tempus) return
|
||||
if (this.subscription) this.subscription.unsubscribe()
|
||||
if (this.tempus) {
|
||||
this.tempus.dispose()
|
||||
this.tempus = null
|
||||
}
|
||||
if (this.preview) {
|
||||
this.preview.clear()
|
||||
}
|
||||
},
|
||||
|
||||
imageChanged: function(vnode, e) {
|
||||
let file = vnode.attrs.form[vnode.attrs.formKey] = e.currentTarget.files?.[0] || null
|
||||
if (this.preview) {
|
||||
this.preview.clear()
|
||||
this.preview = null
|
||||
}
|
||||
if (!file) return
|
||||
|
||||
if (file.type.startsWith('image')) {
|
||||
this.preview = {
|
||||
file: file,
|
||||
preview: URL.createObjectURL(file),
|
||||
clear: function() {
|
||||
URL.revokeObjectURL(this.preview)
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getInput: function(vnode) {
|
||||
|
@ -67,6 +91,21 @@ const Input = {
|
|||
onclick: () => { this.tempus.toggle(); return false },
|
||||
})
|
||||
])
|
||||
case 'image':
|
||||
let imageLink = this.preview && this.preview.preview || vnode.attrs.form[vnode.attrs.formKey]
|
||||
|
||||
return m('div.form-row.image-banner', {
|
||||
style: {
|
||||
'background-image': typeof imageLink === 'string' ? 'url("' + (imageLink) + '")' : null,
|
||||
},
|
||||
}, [
|
||||
m('input.cover', {
|
||||
type: 'file',
|
||||
accept: vnode.attrs.accept,
|
||||
disabled: api.loading,
|
||||
onchange: this.imageChanged.bind(this, vnode),
|
||||
}),
|
||||
])
|
||||
default:
|
||||
return m('input', {
|
||||
disabled: api.loading,
|
||||
|
|
|
@ -39,6 +39,8 @@ const i18n = {
|
|||
'Dagsetning vantar'],
|
||||
upload_missing_file: ['Video file missing',
|
||||
'Myndaskrá vantar'],
|
||||
upload_missing_banner: ['Poster image missing',
|
||||
'Mynd vantar'],
|
||||
upload_error: ['Error while uploading: {0}',
|
||||
'Villa við að hlaða upp myndefni: {0}'],
|
||||
unsplash: ['Photo by {0} on {1}',
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const m = require('mithril')
|
||||
const api = require('./api')
|
||||
const Authentication = require('./authentication')
|
||||
const Input = require('./input')
|
||||
const lang = require('./lang')
|
||||
|
||||
const Article = {
|
||||
|
@ -9,6 +10,15 @@ const Article = {
|
|||
this.error = ''
|
||||
this.path = ''
|
||||
this.data = null
|
||||
this.editing = false
|
||||
this.form = {
|
||||
title: 'Sunnudagssamkoma',
|
||||
date: new Date(),
|
||||
banner: null,
|
||||
metadata: {
|
||||
speaker: '',
|
||||
},
|
||||
}
|
||||
this.onbeforeupdate(vnode)
|
||||
},
|
||||
|
||||
|
@ -30,16 +40,25 @@ const Article = {
|
|||
})
|
||||
.then((result) => {
|
||||
this.data = result.article
|
||||
this.afterData()
|
||||
this.gotArticle(vnode)
|
||||
}, (err) => {
|
||||
this.error = err.message
|
||||
})
|
||||
},
|
||||
|
||||
afterData: function() {
|
||||
gotArticle: function(vnode) {
|
||||
if (!this.data) {
|
||||
this.error = 'Article not found'
|
||||
return this.error = 'Article not found'
|
||||
}
|
||||
this.form.title = this.data.name
|
||||
this.form.date = new Date(this.data.publish_at)
|
||||
this.form.banner = this.data.banner_path
|
||||
this.form.metadata.speaker = this.data.content.speaker
|
||||
},
|
||||
|
||||
updatevideo: function(vnode, e) {
|
||||
this.error = ''
|
||||
if (this.error) return false
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
|
@ -58,7 +77,7 @@ const Article = {
|
|||
crossorigin: '',
|
||||
controls: true,
|
||||
preload: 'none',
|
||||
poster: '/assets/placeholder.avif',
|
||||
poster: this.data.banner_path || '/assets/placeholder.avif',
|
||||
}, [
|
||||
m('source', {
|
||||
src: this.data.media_path
|
||||
|
@ -67,6 +86,46 @@ const Article = {
|
|||
]),
|
||||
]
|
||||
: null,
|
||||
this.editing
|
||||
? m('form.article', {
|
||||
onsubmit: this.updatevideo.bind(this, vnode),
|
||||
}, [
|
||||
m('div.form-row', [
|
||||
m('div.form-columns', [
|
||||
m(Input, {
|
||||
label: 'Mynd',
|
||||
type: 'file',
|
||||
accept: 'image/*',
|
||||
utility: 'image',
|
||||
form: this.form,
|
||||
formKey: 'banner',
|
||||
}),
|
||||
]),
|
||||
m('div.form-columns', [
|
||||
m(Input, {
|
||||
label: 'Title',
|
||||
form: this.form,
|
||||
formKey: 'title',
|
||||
}),
|
||||
m(Input, {
|
||||
label: 'Date (dd.mm.yyyy)',
|
||||
type: 'text',
|
||||
utility: 'datetime',
|
||||
form: this.form,
|
||||
formKey: 'date',
|
||||
}),
|
||||
]),
|
||||
]),
|
||||
m('p.separator', 'Optional'),
|
||||
m(Input, {
|
||||
label: 'Speaker',
|
||||
form: this.form.metadata,
|
||||
formKey: 'speaker',
|
||||
}),
|
||||
])
|
||||
: m('div.article', [
|
||||
m('h1', this.data.name)
|
||||
]),
|
||||
]
|
||||
},
|
||||
}
|
||||
|
|
|
@ -11,7 +11,10 @@ const Browse = {
|
|||
|
||||
mArticles: function(vnode, articles) {
|
||||
return articles.map(article => {
|
||||
return m(m.route.Link, { href: ['', article.publish_at.getFullYear(), article.publish_at.getMonth() + 1, article.id].join('/') }, [
|
||||
return m(m.route.Link, {
|
||||
href: ['', article.publish_at.getFullYear(), article.publish_at.getMonth() + 1, article.id].join('/'),
|
||||
style: article.avif_preview ? `background-image: url('${article.avif_preview}')` : null,
|
||||
}, [
|
||||
m('span', article.publish_at.toUTCString()),
|
||||
m('span', article.name),
|
||||
])
|
||||
|
|
|
@ -16,12 +16,14 @@ const Upload = {
|
|||
d.setSeconds(0)
|
||||
d.setMilliseconds(0)
|
||||
|
||||
this.cache = null
|
||||
this.cacheVideo = null
|
||||
this.cacheImage = null
|
||||
this.uploading = null
|
||||
this.form = {
|
||||
title: 'Sunnudagssamkoma',
|
||||
date: d,
|
||||
file: null,
|
||||
banner: null,
|
||||
metadata: {
|
||||
speaker: '',
|
||||
},
|
||||
|
@ -33,7 +35,8 @@ const Upload = {
|
|||
|
||||
if (!this.form.title) this.error = lang.upload_missing_title // Title is missing
|
||||
if (!this.form.date) this.error = lang.upload_missing_date // Date is missing
|
||||
if (!this.form.file) this.error = lang.upload_missing_file // Video file missing
|
||||
// if (!this.form.file) this.error = lang.upload_missing_file // Video file missing
|
||||
if (!this.form.banner) this.error = lang.upload_missing_banner // Video file missing
|
||||
|
||||
if (this.error) return false
|
||||
|
||||
|
@ -43,8 +46,69 @@ const Upload = {
|
|||
body: this.form,
|
||||
})
|
||||
.then(res => {
|
||||
if (this.cache?.file === this.form.file) {
|
||||
return this.cache
|
||||
if (this.cacheImage?.file === this.form.banner) {
|
||||
return this.cacheImage
|
||||
}
|
||||
|
||||
var data = new FormData()
|
||||
data.append('file', this.form.banner)
|
||||
data.append('preview', JSON.stringify({
|
||||
"out": "base64",
|
||||
"format": "avif",
|
||||
"resize": {
|
||||
"width": 360,
|
||||
"height": 203,
|
||||
"fit": "cover",
|
||||
"withoutEnlargement": true,
|
||||
"kernel": "mitchell"
|
||||
},
|
||||
"avif": {
|
||||
"quality": 50,
|
||||
"effort": 9
|
||||
}
|
||||
}))
|
||||
data.append('medium', JSON.stringify({
|
||||
"format": "avif",
|
||||
"resize": {
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"fit": "cover",
|
||||
"withoutEnlargement": true,
|
||||
"kernel": "mitchell"
|
||||
},
|
||||
"avif": {
|
||||
"quality": 75,
|
||||
"effort": 3
|
||||
}
|
||||
}))
|
||||
|
||||
return api.sendRequest({
|
||||
method: 'POST',
|
||||
url: res.resize + '?token=' + res.token,
|
||||
body: data,
|
||||
})
|
||||
.then(banner => {
|
||||
this.cacheImage = {
|
||||
file: this.form.banner,
|
||||
medium: {
|
||||
filename: banner.medium.filename,
|
||||
path: banner.medium.path,
|
||||
},
|
||||
preview: {
|
||||
base64: banner.preview.base64,
|
||||
},
|
||||
}
|
||||
api.sendRequest({
|
||||
method: 'DELETE',
|
||||
url: res.resize.replace('resize', banner.filename) + '?token=' + res.token,
|
||||
}).catch(err => console.log(err))
|
||||
|
||||
return res
|
||||
})
|
||||
})
|
||||
.then(res => {
|
||||
if (this.cacheVideo?.file === this.form.file) {
|
||||
return this.cacheVideo
|
||||
}
|
||||
|
||||
return api.uploadFileProgress({
|
||||
|
@ -59,7 +123,7 @@ const Upload = {
|
|||
})
|
||||
})
|
||||
.then(res => {
|
||||
this.cache = {
|
||||
this.cacheVideo = {
|
||||
file: this.form.file,
|
||||
filename: res.filename,
|
||||
path: res.path,
|
||||
|
@ -79,10 +143,19 @@ const Upload = {
|
|||
is_featured: false,
|
||||
media: {
|
||||
filename: res.filename,
|
||||
type: this.form.file.type,
|
||||
path: res.path,
|
||||
type: this.form.file.type,
|
||||
size: this.form.file.size,
|
||||
}
|
||||
},
|
||||
banner: {
|
||||
filename: this.cacheImage.medium.filename,
|
||||
path: this.cacheImage.medium.path,
|
||||
type: 'image/avif',
|
||||
size: this.form.file.size,
|
||||
preview: {
|
||||
base64: this.cacheImage.preview.base64,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
@ -138,6 +211,14 @@ const Upload = {
|
|||
form: this.form,
|
||||
formKey: 'file',
|
||||
}),
|
||||
m(Input, {
|
||||
label: 'Mynd',
|
||||
type: 'file',
|
||||
accept: 'image/*',
|
||||
utility: 'image',
|
||||
form: this.form,
|
||||
formKey: 'banner',
|
||||
}),
|
||||
m('p.separator', 'Optional'),
|
||||
m(Input, {
|
||||
label: 'Speaker',
|
||||
|
|
|
@ -79,104 +79,6 @@ a, a:visited, button {
|
|||
background: transparent;
|
||||
}
|
||||
|
||||
|
||||
input[type=text],
|
||||
input[type=password],
|
||||
input[type=datetime] {
|
||||
border: 1px solid var(--main);
|
||||
background: #fff;
|
||||
color: var(--color);
|
||||
border-radius: 0;
|
||||
padding: 0.25rem;
|
||||
line-height: 1rem;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input[type=text]:disabled,
|
||||
input[type=password]:disabled,
|
||||
input[type=datetime]:disabled {
|
||||
background: var(--bg-component);
|
||||
border-color: var(--color-alt);
|
||||
color: var(--color-alt);
|
||||
}
|
||||
|
||||
.form-row input:disabled + button {
|
||||
border-color: var(--color-alt);
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-row input {
|
||||
flex: 2 1 auto;
|
||||
}
|
||||
|
||||
.form-row button {
|
||||
min-width: 30px;
|
||||
text-align: center;
|
||||
border: 1px solid var(--main);
|
||||
border-left: none;
|
||||
background: var(--bg-component);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.form-row .cover {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button, input[type=submit] {
|
||||
background: var(--main);
|
||||
color:var(--main-fg);
|
||||
border-radius: 10px;
|
||||
padding: 0.25rem 1rem;
|
||||
border: none;
|
||||
margin: 1rem 0 2rem;
|
||||
align-self: center;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.button.spinner, input[type=submit].spinner {
|
||||
height: 2rem;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.button-alert {
|
||||
background: var(--error-bg);
|
||||
color: var(--color);
|
||||
}
|
||||
|
||||
.loading-bar {
|
||||
border: 1px solid var(--main);
|
||||
background: var(--bg-component);
|
||||
}
|
||||
|
||||
.loading-bar::after {
|
||||
height: 1rem;
|
||||
background: var(--main);
|
||||
min-width: 1px;
|
||||
content: '';
|
||||
display: block;
|
||||
width: var(--progress);
|
||||
transition: width 3s;
|
||||
}
|
||||
|
||||
input[type=text]:focus,
|
||||
input[type=password]:focus,
|
||||
input[type=datetime]:focus {
|
||||
outline: 1px solid var(--main);
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
@ -206,20 +108,6 @@ h1 {
|
|||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
form p, label {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
margin: 0.75rem 0 0.5rem 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
form p.separator {
|
||||
color: var(--color-alt);
|
||||
margin-top: 1.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--color-alt);
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--error);
|
||||
}
|
||||
|
@ -266,6 +154,7 @@ form p.separator {
|
|||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-image: url('./assets/bg_admin.avif');
|
||||
}
|
||||
|
||||
.page-upload footer {
|
||||
|
@ -277,11 +166,133 @@ form p.separator {
|
|||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.avifsupport .page-upload {
|
||||
background-image: url('./assets/bg_admin.avif');
|
||||
/* Common components */
|
||||
|
||||
input[type=text],
|
||||
input[type=password],
|
||||
input[type=datetime] {
|
||||
border: 1px solid var(--main);
|
||||
background: #fff;
|
||||
color: var(--color);
|
||||
border-radius: 0;
|
||||
padding: 0.25rem;
|
||||
line-height: 1rem;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
}
|
||||
.jpegonly .page-upload {
|
||||
background-image: url('./assets/bg_admin.jpg');
|
||||
|
||||
input[type=text]:disabled,
|
||||
input[type=password]:disabled,
|
||||
input[type=datetime]:disabled {
|
||||
background: var(--bg-component);
|
||||
border-color: var(--color-alt);
|
||||
color: var(--color-alt);
|
||||
}
|
||||
|
||||
.form-row input:disabled + button {
|
||||
border-color: var(--color-alt);
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-row input {
|
||||
flex: 2 1 auto;
|
||||
}
|
||||
|
||||
.form-row .form-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.form-row button {
|
||||
min-width: 30px;
|
||||
text-align: center;
|
||||
border: 1px solid var(--main);
|
||||
border-left: none;
|
||||
background: var(--bg-component);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.form-row.image-banner {
|
||||
background-size: cover;
|
||||
background-color: white;
|
||||
border: 2px dashed var(--main);
|
||||
aspect-ratio: 16 / 9;
|
||||
align-self: center;
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
}
|
||||
|
||||
.form-row .cover {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type=text]:focus,
|
||||
input[type=password]:focus,
|
||||
input[type=datetime]:focus {
|
||||
outline: 1px solid var(--main);
|
||||
}
|
||||
|
||||
.button, input[type=submit] {
|
||||
background: var(--main);
|
||||
color:var(--main-fg);
|
||||
border-radius: 10px;
|
||||
padding: 0.25rem 1rem;
|
||||
border: none;
|
||||
margin: 1rem 0 2rem;
|
||||
align-self: center;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.button.spinner, input[type=submit].spinner {
|
||||
height: 2rem;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.button-alert {
|
||||
background: var(--error-bg);
|
||||
color: var(--color);
|
||||
}
|
||||
|
||||
.loading-bar {
|
||||
border: 1px solid var(--main);
|
||||
background: var(--bg-component);
|
||||
}
|
||||
|
||||
.loading-bar::after {
|
||||
height: 1rem;
|
||||
background: var(--main);
|
||||
min-width: 1px;
|
||||
content: '';
|
||||
display: block;
|
||||
width: var(--progress);
|
||||
transition: width 3s;
|
||||
}
|
||||
|
||||
form p, label {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
margin: 0.75rem 0 0.5rem 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
form p.separator {
|
||||
color: var(--color-alt);
|
||||
margin-top: 1.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--color-alt);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
|
@ -463,13 +474,15 @@ footer a {
|
|||
justify-content: flex-end;
|
||||
background: url('./assets/placeholder.avif') center no-repeat;
|
||||
background-size: cover;
|
||||
padding: 1rem;
|
||||
margin: 0 1rem 1rem;
|
||||
text-align: center;
|
||||
border: 1px solid var(--main);
|
||||
text-shadow: 2px 0 10px #fffc, -2px 0 10px #fffc, 0 2px 10px #fffc, 0 -2px 10px #fffc,
|
||||
1px 1px 10px #fffc, -1px -1px 10px #fffc, 1px -1px 10px #fffc, -1px 1px 10px #fffc;
|
||||
}
|
||||
|
||||
.gallery .group a span {
|
||||
align-self: stretch;
|
||||
text-align: center;
|
||||
background: #fffb;
|
||||
}
|
||||
|
||||
/* Player */
|
||||
|
|
Loading…
Reference in a new issue