iceshrimp-legacy/src/web/app/desktop/tags/post-form.tag
2017-11-01 10:22:40 +09:00

535 lines
13 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<mk-post-form ondragover={ ondragover } ondragenter={ ondragenter } ondragleave={ ondragleave } ondrop={ ondrop }>
<div class="content">
<textarea class={ with: (files.length != 0 || poll) } ref="text" disabled={ wait } oninput={ update } onkeydown={ onkeydown } onpaste={ onpaste } placeholder={ placeholder }></textarea>
<div class="medias { with: poll }" if={ files.length != 0 }>
<ul>
<li each={ files }>
<div class="img" style="background-image: url({ url + '?thumbnail&size=64' })" title={ name }></div>
<img class="remove" onclick={ removeFile } src="/assets/desktop/remove.png" title="%i18n:desktop.tags.mk-post-form.attach-cancel%" alt=""/>
</li>
<li class="add" if={ files.length < 4 } title="%i18n:desktop.tags.mk-post-form.attach-media-from-local%" onclick={ selectFile }><i class="fa fa-plus"></i></li>
</ul>
<p class="remain">{ 4 - files.length }/4</p>
</div>
<mk-poll-editor if={ poll } ref="poll" ondestroy={ onPollDestroyed }/>
</div>
<mk-uploader ref="uploader"/>
<button ref="upload" title="%i18n:desktop.tags.mk-post-form.attach-media-from-local%" onclick={ selectFile }><i class="fa fa-upload"></i></button>
<button ref="drive" title="%i18n:desktop.tags.mk-post-form.attach-media-from-drive%" onclick={ selectFileFromDrive }><i class="fa fa-cloud"></i></button>
<button class="kao" title="%i18n:desktop.tags.mk-post-form.insert-a-kao%" onclick={ kao }><i class="fa fa-smile-o"></i></button>
<button class="poll" title="%i18n:desktop.tags.mk-post-form.create-poll%" onclick={ addPoll }><i class="fa fa-pie-chart"></i></button>
<p class="text-count { over: refs.text.value.length > 1000 }">{ '%i18n:desktop.tags.mk-post-form.text-remain%'.replace('{}', 1000 - refs.text.value.length) }</p>
<button class={ wait: wait } ref="submit" disabled={ wait || (refs.text.value.length == 0 && files.length == 0 && !poll && !repost) } onclick={ post }>
{ wait ? '%i18n:desktop.tags.mk-post-form.posting%' : submitText }<mk-ellipsis if={ wait }/>
</button>
<input ref="file" type="file" accept="image/*" multiple="multiple" tabindex="-1" onchange={ changeFile }/>
<div class="dropzone" if={ draghover }></div>
<style>
:scope
display block
padding 16px
background lighten($theme-color, 95%)
&:after
content ""
display block
clear both
> .content
[ref='text']
display block
padding 12px
margin 0
width 100%
max-width 100%
min-width 100%
min-height calc(16px + 12px + 12px)
font-size 16px
color #333
background #fff
outline none
border solid 1px rgba($theme-color, 0.1)
border-radius 4px
transition border-color .3s ease
&:hover
border-color rgba($theme-color, 0.2)
transition border-color .1s ease
& + *
& + * + *
border-color rgba($theme-color, 0.2)
transition border-color .1s ease
&:focus
color $theme-color
border-color rgba($theme-color, 0.5)
transition border-color 0s ease
& + *
& + * + *
border-color rgba($theme-color, 0.5)
transition border-color 0s ease
&:disabled
opacity 0.5
&::-webkit-input-placeholder
color rgba($theme-color, 0.3)
&.with
border-bottom solid 1px rgba($theme-color, 0.1) !important
border-radius 4px 4px 0 0
> .medias
margin 0
padding 0
background lighten($theme-color, 98%)
border solid 1px rgba($theme-color, 0.1)
border-top none
border-radius 0 0 4px 4px
transition border-color .3s ease
&.with
border-bottom solid 1px rgba($theme-color, 0.1) !important
border-radius 0
> .remain
display block
position absolute
top 8px
right 8px
margin 0
padding 0
color rgba($theme-color, 0.4)
> ul
display block
margin 0
padding 4px
list-style none
&:after
content ""
display block
clear both
> li
display block
float left
margin 4px
padding 0
cursor move
&:hover > .remove
display block
> .img
width 64px
height 64px
background-size cover
background-position center center
> .remove
display none
position absolute
top -6px
right -6px
width 16px
height 16px
cursor pointer
> .add
display block
float left
margin 4px
padding 0
border dashed 2px rgba($theme-color, 0.2)
cursor pointer
&:hover
border-color rgba($theme-color, 0.3)
> i
color rgba($theme-color, 0.4)
> i
display block
width 60px
height 60px
line-height 60px
text-align center
font-size 1.2em
color rgba($theme-color, 0.2)
> mk-poll-editor
background lighten($theme-color, 98%)
border solid 1px rgba($theme-color, 0.1)
border-top none
border-radius 0 0 4px 4px
transition border-color .3s ease
> mk-uploader
margin 8px 0 0 0
padding 8px
border solid 1px rgba($theme-color, 0.2)
border-radius 4px
[ref='file']
display none
.text-count
pointer-events none
display block
position absolute
bottom 16px
right 138px
margin 0
line-height 40px
color rgba($theme-color, 0.5)
&.over
color #ec3828
[ref='submit']
display block
position absolute
bottom 16px
right 16px
cursor pointer
padding 0
margin 0
width 110px
height 40px
font-size 1em
color $theme-color-foreground
background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
outline none
border solid 1px lighten($theme-color, 15%)
border-radius 4px
&:not(:disabled)
font-weight bold
&:hover:not(:disabled)
background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%)
border-color $theme-color
&:active:not(:disabled)
background $theme-color
border-color $theme-color
&:focus
&:after
content ""
pointer-events none
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
&:disabled
opacity 0.7
cursor default
&.wait
background linear-gradient(
45deg,
darken($theme-color, 10%) 25%,
$theme-color 25%,
$theme-color 50%,
darken($theme-color, 10%) 50%,
darken($theme-color, 10%) 75%,
$theme-color 75%,
$theme-color
)
background-size 32px 32px
animation stripe-bg 1.5s linear infinite
opacity 0.7
cursor wait
@keyframes stripe-bg
from {background-position: 0 0;}
to {background-position: -64px 32px;}
[ref='upload']
[ref='drive']
.kao
.poll
display inline-block
cursor pointer
padding 0
margin 8px 4px 0 0
width 40px
height 40px
font-size 1em
color rgba($theme-color, 0.5)
background transparent
outline none
border solid 1px transparent
border-radius 4px
&:hover
background transparent
border-color rgba($theme-color, 0.3)
&:active
color rgba($theme-color, 0.6)
background linear-gradient(to bottom, lighten($theme-color, 80%) 0%, lighten($theme-color, 90%) 100%)
border-color rgba($theme-color, 0.5)
box-shadow 0 2px 4px rgba(0, 0, 0, 0.15) inset
&:focus
&:after
content ""
pointer-events none
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
> .dropzone
position absolute
left 0
top 0
width 100%
height 100%
border dashed 2px rgba($theme-color, 0.5)
pointer-events none
</style>
<script>
import getKao from '../../common/scripts/get-kao';
import notify from '../scripts/notify';
import Autocomplete from '../scripts/autocomplete';
this.mixin('api');
this.wait = false;
this.uploadings = [];
this.files = [];
this.autocomplete = null;
this.poll = false;
this.inReplyToPost = this.opts.reply;
this.repost = this.opts.repost;
this.placeholder = this.repost
? '%i18n:desktop.tags.mk-post-form.quote-placeholder%'
: this.inReplyToPost
? '%i18n:desktop.tags.mk-post-form.reply-placeholder%'
: '%i18n:desktop.tags.mk-post-form.post-placeholder%';
this.submitText = this.repost
? '%i18n:desktop.tags.mk-post-form.repost%'
: this.inReplyToPost
? '%i18n:desktop.tags.mk-post-form.reply%'
: '%i18n:desktop.tags.mk-post-form.post%';
this.draftId = this.repost
? 'repost:' + this.repost.id
: this.inReplyToPost
? 'reply:' + this.inReplyToPost.id
: 'post';
this.on('mount', () => {
this.refs.uploader.on('uploaded', file => {
this.addFile(file);
});
this.refs.uploader.on('change-uploads', uploads => {
this.trigger('change-uploading-files', uploads);
});
this.autocomplete = new Autocomplete(this.refs.text);
this.autocomplete.attach();
// 書きかけの投稿を復元
const draft = JSON.parse(localStorage.getItem('drafts') || '{}')[this.draftId];
if (draft) {
this.refs.text.value = draft.data.text;
this.files = draft.data.files;
if (draft.data.poll) {
this.poll = true;
this.update();
this.refs.poll.set(draft.data.poll);
}
this.trigger('change-files', this.files);
this.update();
}
});
this.on('unmount', () => {
this.autocomplete.detach();
});
this.focus = () => {
this.refs.text.focus();
};
this.clear = () => {
this.refs.text.value = '';
this.files = [];
this.poll = false;
this.trigger('change-files');
this.update();
};
this.ondragover = e => {
e.preventDefault();
e.stopPropagation();
this.draghover = true;
e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
};
this.ondragenter = e => {
this.draghover = true;
};
this.ondragleave = e => {
this.draghover = false;
};
this.ondrop = e => {
e.preventDefault();
e.stopPropagation();
this.draghover = false;
// ファイルだったら
if (e.dataTransfer.files.length > 0) {
e.dataTransfer.files.forEach(this.upload);
}
};
this.onkeydown = e => {
if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey)) this.post();
};
this.onpaste = e => {
e.clipboardData.items.forEach(item => {
if (item.kind == 'file') {
this.upload(item.getAsFile());
}
});
};
this.selectFile = () => {
this.refs.file.click();
};
this.selectFileFromDrive = () => {
const i = riot.mount(document.body.appendChild(document.createElement('mk-select-file-from-drive-window')), {
multiple: true
})[0];
i.one('selected', files => {
files.forEach(this.addFile);
});
};
this.changeFile = () => {
this.refs.file.files.forEach(this.upload);
};
this.upload = file => {
this.refs.uploader.upload(file);
};
this.addFile = file => {
this.files.push(file);
this.trigger('change-files', this.files);
this.update();
};
this.removeFile = e => {
const file = e.item;
this.files = this.files.filter(x => x.id != file.id);
this.trigger('change-files', this.files);
this.update();
};
this.addPoll = () => {
this.poll = true;
};
this.onPollDestroyed = () => {
this.update({
poll: false
});
};
this.post = e => {
this.wait = true;
const files = this.files && this.files.length > 0
? this.files.map(f => f.id)
: undefined;
this.api('posts/create', {
text: this.refs.text.value == '' ? undefined : this.refs.text.value,
media_ids: files,
reply_id: this.inReplyToPost ? this.inReplyToPost.id : undefined,
repost_id: this.repost ? this.repost.id : undefined,
poll: this.poll ? this.refs.poll.get() : undefined
}).then(data => {
this.clear();
this.removeDraft();
this.trigger('post');
notify(this.repost
? '%i18n:desktop.tags.mk-post-form.reposted%'
: this.inReplyToPost
? '%i18n:desktop.tags.mk-post-form.replied%'
: '%i18n:desktop.tags.mk-post-form.posted%');
}).catch(err => {
notify(this.repost
? '%i18n:desktop.tags.mk-post-form.repost-failed%'
: this.inReplyToPost
? '%i18n:desktop.tags.mk-post-form.reply-failed%'
: '%i18n:desktop.tags.mk-post-form.post-failed%');
}).then(() => {
this.update({
wait: false
});
});
};
this.kao = () => {
this.refs.text.value += getKao();
};
this.on('update', () => {
this.saveDraft();
});
this.saveDraft = () => {
const data = JSON.parse(localStorage.getItem('drafts') || '{}');
data[this.draftId] = {
updated_at: new Date(),
data: {
text: this.refs.text.value,
files: this.files,
poll: this.poll && this.refs.poll ? this.refs.poll.get() : undefined
}
}
localStorage.setItem('drafts', JSON.stringify(data));
};
this.removeDraft = () => {
const data = JSON.parse(localStorage.getItem('drafts') || '{}');
delete data[this.draftId];
localStorage.setItem('drafts', JSON.stringify(data));
};
</script>
</mk-post-form>