diff --git a/src/components/emoji_picker/emoji_picker.js b/src/components/emoji_picker/emoji_picker.js index 76934e53..c0391f6c 100644 --- a/src/components/emoji_picker/emoji_picker.js +++ b/src/components/emoji_picker/emoji_picker.js @@ -51,6 +51,7 @@ const EmojiPicker = { onEmoji (emoji) { const value = emoji.imageUrl ? `:${emoji.displayText}:` : emoji.replacement this.$emit('emoji', { insertion: value, keepOpen: this.keepOpen }) + this.$store.commit('emojiUsed', emoji) }, onWheel (e) { e.preventDefault() @@ -96,6 +97,7 @@ const EmojiPicker = { ) }, emojis () { + const recentEmojis = this.$store.getters.recentEmojis const standardEmojis = this.$store.state.instance.emoji || [] const customEmojis = this.sortedEmoji const emojiPacks = [] @@ -108,6 +110,15 @@ const EmojiPicker = { }) }) return [ + { + id: 'recent', + text: this.$t('emoji.recent'), + first: { + imageUrl: '', + replacement: '🕒', + }, + emojis: this.filterByKeyword(recentEmojis) + }, { id: 'standard', text: this.$t('emoji.unicode'), diff --git a/src/components/emoji_reactions/emoji_reactions.js b/src/components/emoji_reactions/emoji_reactions.js index 06e21f6e..75ab252e 100644 --- a/src/components/emoji_reactions/emoji_reactions.js +++ b/src/components/emoji_reactions/emoji_reactions.js @@ -3,6 +3,11 @@ import UserListPopover from '../user_list_popover/user_list_popover.vue' const EMOJI_REACTION_COUNT_CUTOFF = 12 +const findEmojiByReplacement = (state, replacement) => { + const allEmojis = state.instance.emoji.concat(state.instance.customEmoji) + return allEmojis.find(emoji => emoji.replacement === replacement) +} + const EmojiReactions = { name: 'EmojiReactions', components: { @@ -54,6 +59,8 @@ const EmojiReactions = { }, reactWith (emoji) { this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) + const emojiObject = findEmojiByReplacement(this.$store.state, emoji) + this.$store.commit('emojiUsed', emojiObject) }, unreact (emoji) { this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji }) diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue index 3fe81f77..599611b2 100644 --- a/src/components/emoji_reactions/emoji_reactions.vue +++ b/src/components/emoji_reactions/emoji_reactions.vue @@ -18,7 +18,6 @@ :src="reaction.url" :title="reaction.name" class="reaction-emoji" - width="2.55em" height="2.55em" > {{ reaction.count }} @@ -50,6 +49,7 @@ display: flex; margin-top: 0.25em; flex-wrap: wrap; + container-type: inline-size; } .unicode-emoji { @@ -65,7 +65,8 @@ justify-content: center; box-sizing: border-box; .reaction-emoji { - width: 2.55em !important; + width: auto; + max-width: 96cqw; height: 2.55em !important; margin-right: 0.25em; } diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index b7c66fc7..f7fef499 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -54,6 +54,14 @@ const pxStringToNumber = (str) => { return Number(str.substring(0, str.length - 2)) } +const deleteDraft = (draftKey) => { + const draftData = JSON.parse(localStorage.getItem('drafts') || '{}'); + + delete draftData[draftKey]; + + localStorage.setItem('drafts', JSON.stringify(draftData)); +} + const PostStatusForm = { props: [ 'statusId', @@ -161,6 +169,34 @@ const PostStatusForm = { } } + let draftKey = 'status'; + if (this.replyTo) { + draftKey = 'reply:' + this.replyTo; + } else if (this.quoteId) { + draftKey = 'quote:' + this.quoteId; + } + + const draft = JSON.parse(localStorage.getItem('drafts') || '{}')[draftKey]; + + if (draft) { + statusParams = { + spoilerText: draft.data.spoilerText, + status: draft.data.status, + sensitiveIfSubject, + nsfw: draft.data.nsfw, + files: draft.data.files, + poll: draft.data.poll, + mediaDescriptions: draft.data.mediaDescriptions, + visibility: draft.data.visibility, + language: draft.data.language, + contentType: draft.data.contentType + } + + if (draft.data.poll) { + this.togglePollForm(); + } + } + return { dropFiles: [], uploadingFiles: false, @@ -280,6 +316,7 @@ const PostStatusForm = { statusChanged () { this.autoPreview() this.updateIdempotencyKey() + this.saveDraft() }, clearStatus () { const newStatus = this.newStatus @@ -401,8 +438,38 @@ const PostStatusForm = { }).finally(() => { this.previewLoading = false }) + + let draftKey = 'status'; + if (this.replyTo) { + draftKey = 'reply:' + this.replyTo; + } else if (this.quoteId) { + draftKey = 'quote:' + this.quoteId; + } + deleteDraft(draftKey) }, debouncePreviewStatus: debounce(function () { this.previewStatus() }, 500), + saveDraft() { + const draftData = JSON.parse(localStorage.getItem('drafts') || '{}'); + + let draftKey = 'status'; + if (this.replyTo) { + draftKey = 'reply:' + this.replyTo; + } else if (this.quoteId) { + draftKey = 'quote:' + this.quoteId; + } + + if (this.newStatus.status || this.newStatus.spoilerText || this.newStatus.files.length > 0 || this.newStatus.poll.length > 0) { + draftData[draftKey] = { + updatedAt: new Date(), + data: this.newStatus, + }; + + localStorage.setItem('drafts', JSON.stringify(draftData)); + + } else { + deleteDraft(draftKey); + } + }, autoPreview () { if (!this.preview) return this.previewLoading = true diff --git a/src/components/rich_content/rich_content.scss b/src/components/rich_content/rich_content.scss index db08ef1e..63df7d74 100644 --- a/src/components/rich_content/rich_content.scss +++ b/src/components/rich_content/rich_content.scss @@ -50,7 +50,6 @@ .emoji { display: inline-block; - width: var(--emoji-size, 32px); height: var(--emoji-size, 32px); } diff --git a/src/components/status_body/status_body.scss b/src/components/status_body/status_body.scss index 230a27ac..d618f65e 100644 --- a/src/components/status_body/status_body.scss +++ b/src/components/status_body/status_body.scss @@ -22,21 +22,18 @@ ._mfm_x2_ { .emoji { - width: 100px; height: 100px; } } ._mfm_x3_ { .emoji { - width: 150px; height: 150px; } } ._mfm_x4_ { .emoji { - width: 200px; height: 200px; } } diff --git a/src/components/status_content/status_content.vue b/src/components/status_content/status_content.vue index ab13141e..62acf5ac 100644 --- a/src/components/status_content/status_content.vue +++ b/src/components/status_content/status_content.vue @@ -71,7 +71,7 @@ img, video { &.emoji { - width: 50px; + max-width: 100%; height: 50px; } } @@ -89,7 +89,6 @@ animation: none !important; } .emoji { - width: 32px !important; height: 32px !important; } } diff --git a/src/i18n/en.json b/src/i18n/en.json index 46890345..92618047 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -86,7 +86,8 @@ "load_all_hint": "Loaded first {saneAmount} emoji, loading all emoji may cause performance issues.", "search_emoji": "Search for an emoji", "stickers": "Stickers", - "unicode": "Unicode emoji" + "unicode": "Unicode emoji", + "recent": "Recently used" }, "errors": { "storage_unavailable": "Pleroma could not access browser storage. Your login or your local settings won't be saved and you might encounter unexpected issues. Try enabling cookies." diff --git a/src/lib/persisted_state.js b/src/lib/persisted_state.js index 735a10de..a9a9d307 100644 --- a/src/lib/persisted_state.js +++ b/src/lib/persisted_state.js @@ -19,7 +19,8 @@ const saveImmedeatelyActions = [ 'setOption', 'setClientData', 'setToken', - 'clearToken' + 'clearToken', + 'emojiUsed', ] const defaultStorage = (() => { diff --git a/src/main.js b/src/main.js index 01a44c96..cad887e5 100644 --- a/src/main.js +++ b/src/main.js @@ -22,6 +22,7 @@ import announcementsModule from './modules/announcements.js' import editStatusModule from './modules/editStatus.js' import statusHistoryModule from './modules/statusHistory.js' import tagModule from './modules/tags.js' +import recentEmojisModule from './modules/recentEmojis.js' import { createI18n } from 'vue-i18n' @@ -47,7 +48,8 @@ const persistedStateOptions = { paths: [ 'config', 'users.lastLoginName', - 'oauth' + 'oauth', + 'recentEmojis.emojis', ] }; @@ -98,7 +100,8 @@ const persistedStateOptions = { announcements: announcementsModule, editStatus: editStatusModule, statusHistory: statusHistoryModule, - tags: tagModule + tags: tagModule, + recentEmojis: recentEmojisModule, }, plugins, strict: false // Socket modifies itself, let's ignore this for now. diff --git a/src/modules/api.js b/src/modules/api.js index 8de1449b..352d7774 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -5,15 +5,25 @@ import { map } from 'lodash' const retryTimeout = (multiplier) => 1000 * multiplier const isVisible = (store, message, visibility) => { - if (visibility === 'all') { + if (visibility == 'all') { return true - } else if (visibility === 'following') { - return store.getters.relationship(message.in_reply_to_user_id).following - } else if (visibility === 'self') { + } + + if (visibility == 'following') { + if (message.in_reply_to_user_id === null) { + return true + } else { + return store.getters.relationship(message.in_reply_to_user_id).following + } + } + + if (visibility == 'self') { return message.in_reply_to_user_id === store.rootState.users.currentUser.id } + return false } + const api = { state: { retryMultiplier: 1, diff --git a/src/modules/recentEmojis.js b/src/modules/recentEmojis.js new file mode 100644 index 00000000..baab1c52 --- /dev/null +++ b/src/modules/recentEmojis.js @@ -0,0 +1,50 @@ +// each row is 7 emojis, 6 rows chosen arbitrarily. i don't think more than +// that are going to be useful. +const RECENT_MAX = 7 * 6 + +const defaultState = { + emojis: [], +} + +const recentEmojis = { + state: defaultState, + + mutations: { + emojiUsed ({ emojis }, emoji) { + if (emoji.displayText === undefined || emoji.displayText === null) { + console.error('emojiUsed was called with a bad emoji object: ', emoji) + return + } else if (emoji.displayText.includes('@')) { + console.error('emojiUsed was called with a remote emoji: ', emoji) + return + } + + const i = emojis.indexOf(emoji.displayText) + + if (i === -1) { + // not in `emojis` yet, insert and truncate if necessary + const newLength = emojis.unshift(emoji.displayText) + if (newLength > RECENT_MAX) { + emojis.pop() + } + } else if (i !== 0) { + // emoji is already in `emojis` but needs to be bumped to the top + emojis.splice(i, 1) + emojis.unshift(emoji.displayText) + } + }, + }, + + getters: { + recentEmojis: (state, getters, rootState) => state.emojis.reduce((objects, displayText) => { + const allEmojis = rootState.instance.emoji.concat(rootState.instance.customEmoji) + let emojiObject = allEmojis.find(emoji => emoji.displayText === displayText) + if (emojiObject !== undefined) { + objects.push(emojiObject) + } + return objects + }, []), + }, +} + +export default recentEmojis