From 1c2048749480800e2d44b3876378beee421a79ba Mon Sep 17 00:00:00 2001 From: tusooa Date: Wed, 12 Jul 2023 20:45:44 -0400 Subject: [PATCH 1/8] Implement showing quotes --- src/components/status/status.js | 3 +++ src/components/status/status.scss | 6 ++++++ src/components/status/status.vue | 9 +++++++++ src/modules/statuses.js | 4 ++++ .../entity_normalizer/entity_normalizer.service.js | 4 ++++ 5 files changed, 26 insertions(+) diff --git a/src/components/status/status.js b/src/components/status/status.js index 9a9bca7aa0..c10c6cf5f5 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -401,6 +401,9 @@ const Status = { }, editingAvailable () { return this.$store.state.instance.editingAvailable + }, + quotedStatus () { + return this.status.quote_id ? this.$store.state.statuses.allStatusesObject[this.status.quote_id] : undefined } }, methods: { diff --git a/src/components/status/status.scss b/src/components/status/status.scss index 448128675b..4dfd1f87e9 100644 --- a/src/components/status/status.scss +++ b/src/components/status/status.scss @@ -422,4 +422,10 @@ } } } + + .quoted-status { + margin-top: 0.5em; + border: 1px solid var(--border, $fallback--border); + border-radius: var(--attachmentRadius, $fallback--attachmentRadius); + } } diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 35b153627a..58fc0ecad6 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -364,6 +364,15 @@ @parseReady="setHeadTailLinks" /> +
+ +
+
{ output.thread_muted = pleroma.thread_muted output.emoji_reactions = pleroma.emoji_reactions output.parent_visible = pleroma.parent_visible === undefined ? true : pleroma.parent_visible + output.quote = pleroma.quote ? parseStatus(pleroma.quote) : undefined + output.quote_id = output.quote ? output.quote.id : undefined + output.quote_url = pleroma.quote_url + output.quote_visible = pleroma.quote_visible } else { output.text = data.content output.summary = data.spoiler_text From d72486f3e4f2af9db95535f3f93e6257c675e155 Mon Sep 17 00:00:00 2001 From: tusooa Date: Wed, 12 Jul 2023 21:34:19 -0400 Subject: [PATCH 2/8] Implement sending quote posts --- .../post_status_form/post_status_form.js | 31 ++++++++++-- .../post_status_form/post_status_form.vue | 50 +++++++++++++++++++ src/i18n/en.json | 2 + src/services/api/api.service.js | 4 ++ .../status_poster/status_poster.service.js | 2 + 5 files changed, 85 insertions(+), 4 deletions(-) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index b75fee6911..7317613260 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -156,7 +156,8 @@ const PostStatusForm = { poll: this.statusPoll || {}, mediaDescriptions: this.statusMediaDescriptions || {}, visibility: this.statusScope || scope, - contentType: statusContentType + contentType: statusContentType, + quoting: false } } @@ -265,6 +266,24 @@ const PostStatusForm = { isEdit () { return typeof this.statusId !== 'undefined' && this.statusId.trim() !== '' }, + quotable () { + if (!this.replyTo) { + return false + } + + const repliedStatus = this.$store.state.statuses.allStatusesObject[this.replyTo] + if (!repliedStatus) { + return false + } + + if (repliedStatus.visibility === 'public' || + repliedStatus.visibility === 'unlisted' || + repliedStatus.visibility === 'local') { + return true + } else if (repliedStatus.visibility === 'private') { + return repliedStatus.account.id === this.$store.state.users.currentUser.id + } + }, ...mapGetters(['mergedConfig']), ...mapState({ mobileLayout: state => state.interface.mobileLayout @@ -292,7 +311,8 @@ const PostStatusForm = { visibility: newStatus.visibility, contentType: newStatus.contentType, poll: {}, - mediaDescriptions: {} + mediaDescriptions: {}, + quoting: false } this.pollFormVisible = false this.$refs.mediaUpload && this.$refs.mediaUpload.clearFile() @@ -340,6 +360,8 @@ const PostStatusForm = { return } + const replyOrQuoteAttr = newStatus.quoting ? 'quoteId' : 'inReplyToStatusId' + const postingOptions = { status: newStatus.status, spoilerText: newStatus.spoilerText || null, @@ -347,7 +369,7 @@ const PostStatusForm = { sensitive: newStatus.nsfw, media: newStatus.files, store: this.$store, - inReplyToStatusId: this.replyTo, + [replyOrQuoteAttr]: this.replyTo, contentType: newStatus.contentType, poll, idempotencyKey: this.idempotencyKey @@ -373,6 +395,7 @@ const PostStatusForm = { } const newStatus = this.newStatus this.previewLoading = true + const replyOrQuoteAttr = newStatus.quoting ? 'quoteId' : 'inReplyToStatusId' statusPoster.postStatus({ status: newStatus.status, spoilerText: newStatus.spoilerText || null, @@ -380,7 +403,7 @@ const PostStatusForm = { sensitive: newStatus.nsfw, media: [], store: this.$store, - inReplyToStatusId: this.replyTo, + [replyOrQuoteAttr]: this.replyTo, contentType: newStatus.contentType, poll: {}, preview: true diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 86c1f9073f..e06b88b2eb 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -126,6 +126,42 @@ class="preview-status" />
+
+ + +
Date: Thu, 13 Jul 2023 00:37:57 -0400 Subject: [PATCH 3/8] Handle recursive quotes --- .../post_status_form/post_status_form.js | 2 +- src/components/status/status.js | 25 ++++++++++++++- src/components/status/status.scss | 12 +++++++ src/components/status/status.vue | 32 ++++++++++++++++++- src/i18n/en.json | 5 ++- .../entity_normalizer.service.js | 2 +- 6 files changed, 73 insertions(+), 5 deletions(-) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 7317613260..1735099899 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -281,7 +281,7 @@ const PostStatusForm = { repliedStatus.visibility === 'local') { return true } else if (repliedStatus.visibility === 'private') { - return repliedStatus.account.id === this.$store.state.users.currentUser.id + return repliedStatus.user.id === this.$store.state.users.currentUser.id } }, ...mapGetters(['mergedConfig']), diff --git a/src/components/status/status.js b/src/components/status/status.js index c10c6cf5f5..e722a635e8 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -133,6 +133,7 @@ const Status = { 'showPinned', 'inProfile', 'profileUserId', + 'inQuote', 'simpleTree', 'controlledThreadDisplayStatus', @@ -159,7 +160,8 @@ const Status = { uncontrolledMediaPlaying: [], suspendable: true, error: null, - headTailLinks: null + headTailLinks: null, + displayQuote: !this.inQuote } }, computed: { @@ -402,8 +404,17 @@ const Status = { editingAvailable () { return this.$store.state.instance.editingAvailable }, + hasVisibleQuote () { + return this.status.quote_url && this.status.quote_visible + }, + hasInvisibleQuote () { + return this.status.quote_url && !this.status.quote_visible + }, quotedStatus () { return this.status.quote_id ? this.$store.state.statuses.allStatusesObject[this.status.quote_id] : undefined + }, + shouldDisplayQuote () { + return this.quotedStatus && this.displayQuote } }, methods: { @@ -472,6 +483,18 @@ const Status = { window.scrollBy(0, rect.bottom - window.innerHeight + 50) } } + }, + toggleDisplayQuote () { + if (this.shouldDisplayQuote) { + this.displayQuote = false + } else if (!this.quotedStatus) { + this.$store.dispatch('fetchStatus', this.status.quote_id) + .then(() => { + this.displayQuote = true + }) + } else { + this.displayQuote = true + } } }, watch: { diff --git a/src/components/status/status.scss b/src/components/status/status.scss index 4dfd1f87e9..760c6ac1af 100644 --- a/src/components/status/status.scss +++ b/src/components/status/status.scss @@ -427,5 +427,17 @@ margin-top: 0.5em; border: 1px solid var(--border, $fallback--border); border-radius: var(--attachmentRadius, $fallback--attachmentRadius); + + &.-unavailable-prompt { + padding: 0.5em; + } + } + + .display-quoted-status-button { + margin: 0.5em; + + &-icon { + color: inherit; + } } } diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 58fc0ecad6..c49a9e7b4f 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -365,13 +365,43 @@ />
+
+

+ + + +

{ output.emoji_reactions = pleroma.emoji_reactions output.parent_visible = pleroma.parent_visible === undefined ? true : pleroma.parent_visible output.quote = pleroma.quote ? parseStatus(pleroma.quote) : undefined - output.quote_id = output.quote ? output.quote.id : undefined + output.quote_id = pleroma.quote_id ? pleroma.quote_id : (output.quote ? output.quote.id : undefined) output.quote_url = pleroma.quote_url output.quote_visible = pleroma.quote_visible } else { From 63f56cfb274165e157378888f5d448bfb194a52f Mon Sep 17 00:00:00 2001 From: tusooa Date: Thu, 13 Jul 2023 00:43:55 -0400 Subject: [PATCH 4/8] Add aria-labels for reply/quote selector --- src/components/post_status_form/post_status_form.js | 1 + src/components/post_status_form/post_status_form.vue | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 1735099899..65066d542a 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -162,6 +162,7 @@ const PostStatusForm = { } return { + randomSeed: `${Math.random()}`.replace('.', '-'), dropFiles: [], uploadingFiles: false, error: null, diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index e06b88b2eb..0d79388544 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -135,6 +135,7 @@ class="reply-or-quote-option" tabindex="0" role="radio" + :aria-labelledby="`reply-or-quote-option-${randomSeed}-reply`" :aria-checked="!newStatus.quoting" @click="newStatus.quoting = false" > @@ -143,13 +144,16 @@ :checked="!newStatus.quoting" >
From 99cff7e28b6f99620e18936af8b4fe8f725eb156 Mon Sep 17 00:00:00 2001 From: tusooa Date: Mon, 17 Jul 2023 11:29:36 -0400 Subject: [PATCH 5/8] Add changelog for quote --- changelog.d/quote.add | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/quote.add diff --git a/changelog.d/quote.add b/changelog.d/quote.add new file mode 100644 index 0000000000..b43b6abad3 --- /dev/null +++ b/changelog.d/quote.add @@ -0,0 +1 @@ +Implement quoting From 441eea36837a132b841d6c7423e146990b48ab16 Mon Sep 17 00:00:00 2001 From: tusooa Date: Mon, 17 Jul 2023 17:28:36 -0400 Subject: [PATCH 6/8] Do not show quote options for servers without quote support --- src/boot/after_store.js | 1 + src/components/post_status_form/post_status_form.js | 4 ++++ src/modules/instance.js | 1 + 3 files changed, 6 insertions(+) diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 9c1f007bd5..395d483449 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -259,6 +259,7 @@ const getNodeInfo = async ({ store }) => { store.dispatch('setInstanceOption', { name: 'editingAvailable', value: features.includes('editing') }) store.dispatch('setInstanceOption', { name: 'pollLimits', value: metadata.pollLimits }) store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled }) + store.dispatch('setInstanceOption', { name: 'quotingAvailable', value: features.includes('quote_posting') }) const uploadLimits = metadata.uploadLimits store.dispatch('setInstanceOption', { name: 'uploadlimit', value: parseInt(uploadLimits.general) }) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 65066d542a..97d3cb2299 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -268,6 +268,10 @@ const PostStatusForm = { return typeof this.statusId !== 'undefined' && this.statusId.trim() !== '' }, quotable () { + if (!this.$store.state.instance.quotingAvailable) { + return false + } + if (!this.replyTo) { return false } diff --git a/src/modules/instance.js b/src/modules/instance.js index bb0292da0b..1ee64552e6 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -128,6 +128,7 @@ const defaultState = { mediaProxyAvailable: false, suggestionsEnabled: false, suggestionsWeb: '', + quotingAvailable: false, // Html stuff instanceSpecificPanelContent: '', From a314ad7ccca05c91d4a717d956c207a0b0c47005 Mon Sep 17 00:00:00 2001 From: tusooa Date: Mon, 17 Jul 2023 17:29:18 -0400 Subject: [PATCH 7/8] Add fallback return false for quotable judgements --- src/components/post_status_form/post_status_form.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 97d3cb2299..ba49961d78 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -288,6 +288,8 @@ const PostStatusForm = { } else if (repliedStatus.visibility === 'private') { return repliedStatus.user.id === this.$store.state.users.currentUser.id } + + return false }, ...mapGetters(['mergedConfig']), ...mapState({ From 1b081a927288ff2f43025d688e2fa15cf6ce6be1 Mon Sep 17 00:00:00 2001 From: tusooa Date: Wed, 19 Jul 2023 17:00:46 -0400 Subject: [PATCH 8/8] Make reply-or-quote selection display as horizontal button group --- .../post_status_form/post_status_form.vue | 50 ++++++------------- 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 0d79388544..9b108a5aa1 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -129,44 +129,32 @@
- -
+
+ {{ $t('post_status.quote_option') }} +