forked from AkkomaGang/akkoma-fe
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 }) => {
|
||||
try {
|
||||
const res = await window.fetch('/static/emoji.json')
|
||||
|
@ -286,6 +317,7 @@ const afterStoreSetup = async ({ store, i18n }) => {
|
|||
setConfig({ store }),
|
||||
getTOS({ store }),
|
||||
getInstancePanel({ store }),
|
||||
getStickers({ store }),
|
||||
getStaticEmoji({ store }),
|
||||
getCustomEmoji({ store }),
|
||||
getNodeInfo({ store })
|
||||
|
|
|
@ -3,6 +3,7 @@ import MediaUpload from '../media_upload/media_upload.vue'
|
|||
import ScopeSelector from '../scope_selector/scope_selector.vue'
|
||||
import EmojiInput from '../emoji-input/emoji-input.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 { reject, map, uniqBy } from 'lodash'
|
||||
import suggestor from '../emoji-input/suggestor.js'
|
||||
|
@ -34,6 +35,7 @@ const PostStatusForm = {
|
|||
MediaUpload,
|
||||
EmojiInput,
|
||||
PollForm,
|
||||
StickerPicker,
|
||||
ScopeSelector
|
||||
},
|
||||
mounted () {
|
||||
|
@ -82,7 +84,8 @@ const PostStatusForm = {
|
|||
contentType
|
||||
},
|
||||
caret: 0,
|
||||
pollFormVisible: false
|
||||
pollFormVisible: false,
|
||||
stickerPickerVisible: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -158,6 +161,12 @@ const PostStatusForm = {
|
|||
safeDMEnabled () {
|
||||
return this.$store.state.instance.safeDM
|
||||
},
|
||||
stickersAvailable () {
|
||||
if (this.$store.state.instance.stickers) {
|
||||
return this.$store.state.instance.stickers.length > 0
|
||||
}
|
||||
return 0
|
||||
},
|
||||
pollsAvailable () {
|
||||
return this.$store.state.instance.pollsAvailable &&
|
||||
this.$store.state.instance.pollLimits.max_options >= 2
|
||||
|
@ -213,6 +222,7 @@ const PostStatusForm = {
|
|||
poll: {}
|
||||
}
|
||||
this.pollFormVisible = false
|
||||
this.stickerPickerVisible = false
|
||||
this.$refs.mediaUpload.clearFile()
|
||||
this.clearPollForm()
|
||||
this.$emit('posted')
|
||||
|
@ -229,6 +239,7 @@ const PostStatusForm = {
|
|||
addMediaFile (fileInfo) {
|
||||
this.newStatus.files.push(fileInfo)
|
||||
this.enableSubmit()
|
||||
this.stickerPickerVisible = false
|
||||
},
|
||||
removeMediaFile (fileInfo) {
|
||||
let index = this.newStatus.files.indexOf(fileInfo)
|
||||
|
@ -288,6 +299,14 @@ const PostStatusForm = {
|
|||
changeVis (visibility) {
|
||||
this.newStatus.visibility = visibility
|
||||
},
|
||||
toggleStickerPicker () {
|
||||
this.stickerPickerVisible = !this.stickerPickerVisible
|
||||
},
|
||||
clearStickerPicker () {
|
||||
if (this.$refs.stickerPicker) {
|
||||
this.$refs.stickerPicker.clear()
|
||||
}
|
||||
},
|
||||
togglePollForm () {
|
||||
this.pollFormVisible = !this.pollFormVisible
|
||||
},
|
||||
|
|
|
@ -157,6 +157,17 @@
|
|||
@uploaded="addMediaFile"
|
||||
@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
|
||||
v-if="pollsAvailable"
|
||||
class="poll-icon"
|
||||
|
@ -169,7 +180,6 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
v-if="posting"
|
||||
disabled
|
||||
|
@ -248,6 +258,11 @@
|
|||
<label for="filesSensitive">{{ $t('post_status.attachments_sensitive') }}</label>
|
||||
</div>
|
||||
</form>
|
||||
<sticker-picker
|
||||
v-if="stickerPickerVisible"
|
||||
ref="stickerPicker"
|
||||
@uploaded="addMediaFile"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -310,7 +325,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.poll-icon {
|
||||
.poll-icon, .sticker-icon {
|
||||
font-size: 26px;
|
||||
flex: 1;
|
||||
|
||||
|
@ -320,6 +335,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.sticker-icon {
|
||||
flex: 0;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.icon-chart-bar {
|
||||
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')
|
||||
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 (
|
||||
<div class={ classesWrapper.join(' ')}>
|
||||
<button
|
||||
|
|
|
@ -53,6 +53,12 @@
|
|||
background: transparent;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
img {
|
||||
max-height: 26px;
|
||||
vertical-align: top;
|
||||
margin-top: -5px;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.active) {
|
||||
|
|
|
@ -106,6 +106,9 @@
|
|||
"expired": "Poll ended {0} ago",
|
||||
"not_enough_options": "Too few unique options in poll"
|
||||
},
|
||||
"stickers": {
|
||||
"add_sticker": "Add Sticker"
|
||||
},
|
||||
"interactions": {
|
||||
"favs_repeats": "Repeats and Favorites",
|
||||
"follows": "New follows",
|
||||
|
|
Loading…
Reference in a new issue