Feature/add sticker picker

Squash to avoid storing commits with stickers in it to avoid possible copyright problems and excess storage space. --hj
This commit is contained in:
absturztaube 2019-07-24 19:35:52 +00:00 committed by HJ
parent 28f777cb8a
commit fa6210872f
8 changed files with 210 additions and 4 deletions

View file

@ -148,6 +148,37 @@ const getInstancePanel = async ({ store }) => {
} }
} }
const getStickers = async ({ store }) => {
try {
const res = await window.fetch('/static/stickers.json')
if (res.ok) {
const values = await res.json()
const stickers = (await Promise.all(
Object.entries(values).map(async ([name, path]) => {
const resPack = await window.fetch(path + 'pack.json')
var meta = {}
if (resPack.ok) {
meta = await resPack.json()
}
return {
pack: name,
path,
meta
}
})
)).sort((a, b) => {
return a.meta.title.localeCompare(b.meta.title)
})
store.dispatch('setInstanceOption', { name: 'stickers', value: stickers })
} else {
throw (res)
}
} catch (e) {
console.warn("Can't load stickers")
console.warn(e)
}
}
const getStaticEmoji = async ({ store }) => { const getStaticEmoji = async ({ store }) => {
try { try {
const res = await window.fetch('/static/emoji.json') const res = await window.fetch('/static/emoji.json')
@ -286,6 +317,7 @@ const afterStoreSetup = async ({ store, i18n }) => {
setConfig({ store }), setConfig({ store }),
getTOS({ store }), getTOS({ store }),
getInstancePanel({ store }), getInstancePanel({ store }),
getStickers({ store }),
getStaticEmoji({ store }), getStaticEmoji({ store }),
getCustomEmoji({ store }), getCustomEmoji({ store }),
getNodeInfo({ store }) getNodeInfo({ store })

View file

@ -3,6 +3,7 @@ import MediaUpload from '../media_upload/media_upload.vue'
import ScopeSelector from '../scope_selector/scope_selector.vue' import ScopeSelector from '../scope_selector/scope_selector.vue'
import EmojiInput from '../emoji-input/emoji-input.vue' import EmojiInput from '../emoji-input/emoji-input.vue'
import PollForm from '../poll/poll_form.vue' import PollForm from '../poll/poll_form.vue'
import StickerPicker from '../sticker_picker/sticker_picker.vue'
import fileTypeService from '../../services/file_type/file_type.service.js' import fileTypeService from '../../services/file_type/file_type.service.js'
import { reject, map, uniqBy } from 'lodash' import { reject, map, uniqBy } from 'lodash'
import suggestor from '../emoji-input/suggestor.js' import suggestor from '../emoji-input/suggestor.js'
@ -34,6 +35,7 @@ const PostStatusForm = {
MediaUpload, MediaUpload,
EmojiInput, EmojiInput,
PollForm, PollForm,
StickerPicker,
ScopeSelector ScopeSelector
}, },
mounted () { mounted () {
@ -82,7 +84,8 @@ const PostStatusForm = {
contentType contentType
}, },
caret: 0, caret: 0,
pollFormVisible: false pollFormVisible: false,
stickerPickerVisible: false
} }
}, },
computed: { computed: {
@ -158,6 +161,12 @@ const PostStatusForm = {
safeDMEnabled () { safeDMEnabled () {
return this.$store.state.instance.safeDM return this.$store.state.instance.safeDM
}, },
stickersAvailable () {
if (this.$store.state.instance.stickers) {
return this.$store.state.instance.stickers.length > 0
}
return 0
},
pollsAvailable () { pollsAvailable () {
return this.$store.state.instance.pollsAvailable && return this.$store.state.instance.pollsAvailable &&
this.$store.state.instance.pollLimits.max_options >= 2 this.$store.state.instance.pollLimits.max_options >= 2
@ -213,6 +222,7 @@ const PostStatusForm = {
poll: {} poll: {}
} }
this.pollFormVisible = false this.pollFormVisible = false
this.stickerPickerVisible = false
this.$refs.mediaUpload.clearFile() this.$refs.mediaUpload.clearFile()
this.clearPollForm() this.clearPollForm()
this.$emit('posted') this.$emit('posted')
@ -229,6 +239,7 @@ const PostStatusForm = {
addMediaFile (fileInfo) { addMediaFile (fileInfo) {
this.newStatus.files.push(fileInfo) this.newStatus.files.push(fileInfo)
this.enableSubmit() this.enableSubmit()
this.stickerPickerVisible = false
}, },
removeMediaFile (fileInfo) { removeMediaFile (fileInfo) {
let index = this.newStatus.files.indexOf(fileInfo) let index = this.newStatus.files.indexOf(fileInfo)
@ -288,6 +299,14 @@ const PostStatusForm = {
changeVis (visibility) { changeVis (visibility) {
this.newStatus.visibility = visibility this.newStatus.visibility = visibility
}, },
toggleStickerPicker () {
this.stickerPickerVisible = !this.stickerPickerVisible
},
clearStickerPicker () {
if (this.$refs.stickerPicker) {
this.$refs.stickerPicker.clear()
}
},
togglePollForm () { togglePollForm () {
this.pollFormVisible = !this.pollFormVisible this.pollFormVisible = !this.pollFormVisible
}, },

View file

@ -157,6 +157,17 @@
@uploaded="addMediaFile" @uploaded="addMediaFile"
@upload-failed="uploadFailed" @upload-failed="uploadFailed"
/> />
<div
v-if="stickersAvailable"
class="sticker-icon"
>
<i
:title="$t('stickers.add_sticker')"
class="icon-picture btn btn-default"
:class="{ selected: stickerPickerVisible }"
@click="toggleStickerPicker"
/>
</div>
<div <div
v-if="pollsAvailable" v-if="pollsAvailable"
class="poll-icon" class="poll-icon"
@ -169,7 +180,6 @@
/> />
</div> </div>
</div> </div>
<button <button
v-if="posting" v-if="posting"
disabled disabled
@ -248,6 +258,11 @@
<label for="filesSensitive">{{ $t('post_status.attachments_sensitive') }}</label> <label for="filesSensitive">{{ $t('post_status.attachments_sensitive') }}</label>
</div> </div>
</form> </form>
<sticker-picker
v-if="stickerPickerVisible"
ref="stickerPicker"
@uploaded="addMediaFile"
/>
</div> </div>
</template> </template>
@ -310,7 +325,7 @@
} }
} }
.poll-icon { .poll-icon, .sticker-icon {
font-size: 26px; font-size: 26px;
flex: 1; flex: 1;
@ -320,6 +335,11 @@
} }
} }
.sticker-icon {
flex: 0;
min-width: 50px;
}
.icon-chart-bar { .icon-chart-bar {
cursor: pointer; cursor: pointer;
} }

View file

@ -0,0 +1,52 @@
/* eslint-env browser */
import statusPosterService from '../../services/status_poster/status_poster.service.js'
import TabSwitcher from '../tab_switcher/tab_switcher.js'
const StickerPicker = {
components: [
TabSwitcher
],
data () {
return {
meta: {
stickers: []
},
path: ''
}
},
computed: {
pack () {
return this.$store.state.instance.stickers || []
}
},
methods: {
clear () {
this.meta = {
stickers: []
}
},
pick (sticker, name) {
const store = this.$store
// TODO remove this workaround by finding a way to bypass reuploads
fetch(sticker)
.then((res) => {
res.blob().then((blob) => {
var file = new File([blob], name, { mimetype: 'image/png' })
var formData = new FormData()
formData.append('file', file)
statusPosterService.uploadMedia({ store, formData })
.then((fileData) => {
this.$emit('uploaded', fileData)
this.clear()
}, (error) => {
console.warn("Can't attach sticker")
console.warn(error)
this.$emit('upload-failed', 'default')
})
})
})
}
}
}
export default StickerPicker

View file

@ -0,0 +1,62 @@
<template>
<div
class="sticker-picker"
>
<div
class="sticker-picker-panel"
>
<tab-switcher
:render-only-focused="true"
>
<div
v-for="stickerpack in pack"
:key="stickerpack.path"
:image-tooltip="stickerpack.meta.title"
:image="stickerpack.path + stickerpack.meta.tabIcon"
class="sticker-picker-content"
>
<div
v-for="sticker in stickerpack.meta.stickers"
:key="sticker"
class="sticker"
@click="pick(stickerpack.path + sticker, stickerpack.meta.title)"
>
<img
:src="stickerpack.path + sticker"
>
</div>
</div>
</tab-switcher>
</div>
</div>
</template>
<script src="./sticker_picker.js"></script>
<style lang="scss">
@import '../../_variables.scss';
.sticker-picker {
.sticker-picker-panel {
display: inline-block;
width: 100%;
.sticker-picker-content {
max-height: 300px;
overflow-y: scroll;
overflow-x: auto;
.sticker {
display: inline-block;
width: 20%;
height: 20%;
img {
width: 100%;
&:hover {
filter: drop-shadow(0 0 5px var(--link, $fallback--link));
}
}
}
}
}
}
</style>

View file

@ -45,7 +45,19 @@ export default Vue.component('tab-switcher', {
classesTab.push('active') classesTab.push('active')
classesWrapper.push('active') classesWrapper.push('active')
} }
if (slot.data.attrs.image) {
return (
<div class={ classesWrapper.join(' ')}>
<button
disabled={slot.data.attrs.disabled}
onClick={this.activateTab(index)}
class={classesTab.join(' ')}>
<img src={slot.data.attrs.image} title={slot.data.attrs['image-tooltip']}/>
{slot.data.attrs.label ? '' : slot.data.attrs.label}
</button>
</div>
)
}
return ( return (
<div class={ classesWrapper.join(' ')}> <div class={ classesWrapper.join(' ')}>
<button <button

View file

@ -53,6 +53,12 @@
background: transparent; background: transparent;
z-index: 5; z-index: 5;
} }
img {
max-height: 26px;
vertical-align: top;
margin-top: -5px;
}
} }
&:not(.active) { &:not(.active) {

View file

@ -106,6 +106,9 @@
"expired": "Poll ended {0} ago", "expired": "Poll ended {0} ago",
"not_enough_options": "Too few unique options in poll" "not_enough_options": "Too few unique options in poll"
}, },
"stickers": {
"add_sticker": "Add Sticker"
},
"interactions": { "interactions": {
"favs_repeats": "Repeats and Favorites", "favs_repeats": "Repeats and Favorites",
"follows": "New follows", "follows": "New follows",