From 00704bd88cfc191db457cc022057c0ab5ac0a919 Mon Sep 17 00:00:00 2001 From: solidsanek Date: Fri, 17 Feb 2023 13:56:01 +0100 Subject: [PATCH 1/7] Post: Add drafting feature --- .../post_status_form/post_status_form.js | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index b7c66fc7..2d42d8cc 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,50 @@ 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) { + if(this.newStatus.status) { + console.log(this.newStatus.status) + } + if(this.newStatus.spoilerText) { + console.log(this.newStatus.spoilerText) + } + if(this.newStatus.files) { + console.log(this.newStatus.files) + } + if(this.newStatus.poll) { + console.log(this.newStatus.poll) + } + console.log('not empty?'); + 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 From 2c007f06e35ffee25d55b2cc0bbf84202c473dbc Mon Sep 17 00:00:00 2001 From: solidsanek Date: Sun, 19 Feb 2023 18:58:53 +0100 Subject: [PATCH 2/7] Post: remove debug logs --- .../post_status_form/post_status_form.js | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 2d42d8cc..f7fef499 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -459,25 +459,13 @@ const PostStatusForm = { } if (this.newStatus.status || this.newStatus.spoilerText || this.newStatus.files.length > 0 || this.newStatus.poll.length > 0) { - if(this.newStatus.status) { - console.log(this.newStatus.status) - } - if(this.newStatus.spoilerText) { - console.log(this.newStatus.spoilerText) - } - if(this.newStatus.files) { - console.log(this.newStatus.files) - } - if(this.newStatus.poll) { - console.log(this.newStatus.poll) - } - console.log('not empty?'); - draftData[draftKey] = { + draftData[draftKey] = { updatedAt: new Date(), data: this.newStatus, }; localStorage.setItem('drafts', JSON.stringify(draftData)); + } else { deleteDraft(draftKey); } From 996ce3dde3496172ce507502753b168cda4e1994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yh=C3=ABhtozr?= Date: Mon, 20 Feb 2023 23:18:04 +0900 Subject: [PATCH 3/7] support oblong reactions --- src/components/emoji_reactions/emoji_reactions.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue index 3fe81f77..4eacaa75 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: 100cqw; height: 2.55em !important; margin-right: 0.25em; } From 6d7761c7e537eb2c1dada0a7b5955d3a47076273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yh=C3=ABhtozr?= Date: Mon, 20 Feb 2023 23:27:41 +0900 Subject: [PATCH 4/7] perhaps more graceful cqw --- src/components/emoji_reactions/emoji_reactions.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue index 4eacaa75..599611b2 100644 --- a/src/components/emoji_reactions/emoji_reactions.vue +++ b/src/components/emoji_reactions/emoji_reactions.vue @@ -66,7 +66,7 @@ box-sizing: border-box; .reaction-emoji { width: auto; - max-width: 100cqw; + max-width: 96cqw; height: 2.55em !important; margin-right: 0.25em; } From 626c8800382e93ba0672bdc13d4b597470cc41f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yh=C3=ABhtozr?= Date: Wed, 22 Feb 2023 10:20:25 +0900 Subject: [PATCH 5/7] oblong emoji in status --- src/components/rich_content/rich_content.scss | 1 - src/components/status_body/status_body.scss | 3 --- src/components/status_content/status_content.vue | 3 +-- 3 files changed, 1 insertion(+), 6 deletions(-) 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; } } From 999c38594ea2955c9e38fde32a60999b2e504088 Mon Sep 17 00:00:00 2001 From: flisk Date: Fri, 24 Feb 2023 00:23:53 +0100 Subject: [PATCH 6/7] fix realtime updates in 'following' replies filter i'm not sure how this code was supposed to work, but the way it was written would only add statuses to the timeline if they were in reply to someone the user is following and erroneously filter out posts that aren't replies. --- src/modules/api.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) 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, From 6fdef479d00e8b3c05bfc3918d5e7f74e5d23abc Mon Sep 17 00:00:00 2001 From: flisk Date: Fri, 10 Mar 2023 19:10:42 +0000 Subject: [PATCH 7/7] add recently used emojis panel to emoji picker (#283) ~~(not intended for merging yet, just submitting this for preliminary review and discussion)~~ this patch adds a tab with recently used emojis to the emoji picker: https://akko.lain.gay/notice/ASoGCtyoiXbYPJjqpk there's a couple of things i'm ~~still trying to work out~~ not totally happy with and i'd appreciate any feedback on them: * the recentEmojis getter is called very frequently and has to do a possibly somewhat expensive lookup of emoji objects by their `displayName` each time, which i'm not sure is ideal * ~~emoji reactions on posts added through the picker are picked up by the recentEmojis module, but clicks on existing emoji reactions are not, because `addReaction` in `react_button.js` only currently receives the replacement and not the full emoji object (if there even is one wherever that method is called from)~~ this works now and does the same stupid full search of all emojis by their name which i guess is less bad because this only happens when you hit a reaction emoji button that already existed Reviewed-on: https://akkoma.dev/AkkomaGang/akkoma-fe/pulls/283 Co-authored-by: flisk Co-committed-by: flisk --- src/components/emoji_picker/emoji_picker.js | 11 ++++ .../emoji_reactions/emoji_reactions.js | 7 +++ src/i18n/en.json | 3 +- src/lib/persisted_state.js | 3 +- src/main.js | 7 ++- src/modules/recentEmojis.js | 50 +++++++++++++++++++ 6 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 src/modules/recentEmojis.js 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/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/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