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:
parent
28f777cb8a
commit
fa6210872f
8 changed files with 210 additions and 4 deletions
|
@ -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 })
|
||||||
|
|
|
@ -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
|
||||||
},
|
},
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
52
src/components/sticker_picker/sticker_picker.js
Normal file
52
src/components/sticker_picker/sticker_picker.js
Normal 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
|
62
src/components/sticker_picker/sticker_picker.vue
Normal file
62
src/components/sticker_picker/sticker_picker.vue
Normal 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>
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue