From d72486f3e4f2af9db95535f3f93e6257c675e155 Mon Sep 17 00:00:00 2001
From: tusooa <tusooa@kazv.moe>
Date: Wed, 12 Jul 2023 21:34:19 -0400
Subject: [PATCH] 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 b75fee691..731761326 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 86c1f9073..e06b88b2e 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"
           />
         </div>
+        <div
+          v-if="quotable"
+          role="radiogroup"
+          class="reply-or-quote-selector"
+        >
+          <div
+            class="reply-or-quote-option"
+            tabindex="0"
+            role="radio"
+            :aria-checked="!newStatus.quoting"
+            @click="newStatus.quoting = false"
+          >
+            <input
+              type="radio"
+              :checked="!newStatus.quoting"
+            >
+            <label class="reply-or-quote-option-text">
+              {{ $t('post_status.reply_option') }}
+            </label>
+          </div>
+          <div
+            class="reply-or-quote-option"
+            tabindex="0"
+            role="radio"
+            :aria-checked="newStatus.quoting"
+            @click="newStatus.quoting = true"
+          >
+            <input
+              type="radio"
+              :checked="newStatus.quoting"
+            >
+            <label class="reply-or-quote-option-text">
+              {{ $t('post_status.quote_option') }}
+            </label>
+          </div>
+        </div>
         <EmojiInput
           v-if="!disableSubject && (newStatus.spoilerText || alwaysShowSubject)"
           v-model="newStatus.spoilerText"
@@ -420,6 +456,20 @@
     margin: 0;
   }
 
+  .reply-or-quote-selector {
+    display: flex;
+    flex-direction: column;
+
+    .reply-or-quote-option {
+      display: flex;
+      align-items: center;
+
+      .reply-or-quote-option-text::before {
+        vertical-align: middle;
+      }
+    }
+  }
+
   .text-format {
     .only-format {
       color: $fallback--faint;
diff --git a/src/i18n/en.json b/src/i18n/en.json
index a7ab451f4..4bfad3f93 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -261,6 +261,8 @@
   "post_status": {
     "edit_status": "Edit status",
     "new_status": "Post new status",
+    "reply_option": "Reply to this status",
+    "quote_option": "Quote this status",
     "account_not_locked_warning": "Your account is not {0}. Anyone can follow you to view your follower-only posts.",
     "account_not_locked_warning_link": "locked",
     "attachments_sensitive": "Mark attachments as sensitive",
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index ac715678b..c6bca10b4 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -827,6 +827,7 @@ const postStatus = ({
   poll,
   mediaIds = [],
   inReplyToStatusId,
+  quoteId,
   contentType,
   preview,
   idempotencyKey
@@ -859,6 +860,9 @@ const postStatus = ({
   if (inReplyToStatusId) {
     form.append('in_reply_to_id', inReplyToStatusId)
   }
+  if (quoteId) {
+    form.append('quote_id', quoteId)
+  }
   if (preview) {
     form.append('preview', 'true')
   }
diff --git a/src/services/status_poster/status_poster.service.js b/src/services/status_poster/status_poster.service.js
index 1eb10bb61..aaef5a7a8 100644
--- a/src/services/status_poster/status_poster.service.js
+++ b/src/services/status_poster/status_poster.service.js
@@ -10,6 +10,7 @@ const postStatus = ({
   poll,
   media = [],
   inReplyToStatusId = undefined,
+  quoteId = undefined,
   contentType = 'text/plain',
   preview = false,
   idempotencyKey = ''
@@ -24,6 +25,7 @@ const postStatus = ({
     sensitive,
     mediaIds,
     inReplyToStatusId,
+    quoteId,
     contentType,
     poll,
     preview,
-- 
GitLab