commit: 4827e4d972f8ee11e606693e24ae4ca21711c6b1
parent: 28f777cb8a94f7a28edb6f99a62a6b864c2aa8dc
Author: HJ <30-hj@users.noreply.git.pleroma.social>
Date: Wed, 24 Jul 2019 19:35:52 +0000
Merge branch 'feature/add-sticker-picker' into 'develop'
Feature/add sticker picker
See merge request pleroma/pleroma-fe!885
Diffstat:
8 files changed, 210 insertions(+), 4 deletions(-)
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
@@ -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 })
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
@@ -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
},
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
@@ -158,6 +158,17 @@
@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;
}
diff --git a/src/components/sticker_picker/sticker_picker.js b/src/components/sticker_picker/sticker_picker.js
@@ -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
diff --git a/src/components/sticker_picker/sticker_picker.vue b/src/components/sticker_picker/sticker_picker.vue
@@ -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>
diff --git a/src/components/tab_switcher/tab_switcher.js b/src/components/tab_switcher/tab_switcher.js
@@ -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
diff --git a/src/components/tab_switcher/tab_switcher.scss b/src/components/tab_switcher/tab_switcher.scss
@@ -53,6 +53,12 @@
background: transparent;
z-index: 5;
}
+
+ img {
+ max-height: 26px;
+ vertical-align: top;
+ margin-top: -5px;
+ }
}
&:not(.active) {
diff --git a/src/i18n/en.json b/src/i18n/en.json
@@ -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",