From 1a333aabba759fb68449ead9c47e986f456e8c8c Mon Sep 17 00:00:00 2001
From: Sean King <seanking2919@protonmail.com>
Date: Tue, 7 Jun 2022 21:31:48 -0600
Subject: Add edit status functionality

---
 src/App.vue | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/App.vue')

diff --git a/src/App.vue b/src/App.vue
index 5b448972..9484f993 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -52,6 +52,7 @@
     <MobilePostStatusButton />
     <UserReportingModal />
     <PostStatusModal />
+    <EditStatusModal />
     <SettingsModal />
     <div id="modal" />
     <GlobalNoticeList />
-- 
cgit v1.2.3-70-g09d2


From fa5d35523dd081b6948d38325374cac5707b7868 Mon Sep 17 00:00:00 2001
From: Sean King <seanking2919@protonmail.com>
Date: Mon, 20 Jun 2022 22:52:08 -0600
Subject: Add ability to view status history for edited statuses

---
 src/App.js                                         |  2 +
 src/App.vue                                        |  1 +
 src/components/extra_buttons/extra_buttons.js      | 23 ++++++++-
 src/components/extra_buttons/extra_buttons.vue     | 11 ++++
 .../status_history_modal/status_history_modal.js   | 60 ++++++++++++++++++++++
 .../status_history_modal/status_history_modal.vue  | 46 +++++++++++++++++
 src/i18n/en.json                                   |  3 +-
 src/main.js                                        |  2 +
 src/modules/statusHistory.js                       | 25 +++++++++
 src/modules/statuses.js                            |  3 ++
 src/services/api/api.service.js                    | 17 +++---
 .../entity_normalizer/entity_normalizer.service.js |  6 ++-
 12 files changed, 186 insertions(+), 13 deletions(-)
 create mode 100644 src/components/status_history_modal/status_history_modal.js
 create mode 100644 src/components/status_history_modal/status_history_modal.vue
 create mode 100644 src/modules/statusHistory.js

(limited to 'src/App.vue')

diff --git a/src/App.js b/src/App.js
index 6e0e34a8..af638a1c 100644
--- a/src/App.js
+++ b/src/App.js
@@ -13,6 +13,7 @@ import DesktopNav from './components/desktop_nav/desktop_nav.vue'
 import UserReportingModal from './components/user_reporting_modal/user_reporting_modal.vue'
 import EditStatusModal from './components/edit_status_modal/edit_status_modal.vue'
 import PostStatusModal from './components/post_status_modal/post_status_modal.vue'
+import StatusHistoryModal from './components/status_history_modal/status_history_modal.vue'
 import GlobalNoticeList from './components/global_notice_list/global_notice_list.vue'
 import { windowWidth, windowHeight } from './services/window_utils/window_utils'
 import { mapGetters } from 'vuex'
@@ -37,6 +38,7 @@ export default {
     UserReportingModal,
     PostStatusModal,
     EditStatusModal,
+    StatusHistoryModal,
     GlobalNoticeList
   },
   data: () => ({
diff --git a/src/App.vue b/src/App.vue
index 9484f993..1b513e08 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -53,6 +53,7 @@
     <UserReportingModal />
     <PostStatusModal />
     <EditStatusModal />
+    <StatusHistoryModal />
     <SettingsModal />
     <div id="modal" />
     <GlobalNoticeList />
diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js
index 8a703953..11fe3235 100644
--- a/src/components/extra_buttons/extra_buttons.js
+++ b/src/components/extra_buttons/extra_buttons.js
@@ -6,7 +6,8 @@ import {
   faEyeSlash,
   faThumbtack,
   faShareAlt,
-  faExternalLinkAlt
+  faExternalLinkAlt,
+  faHistory
 } from '@fortawesome/free-solid-svg-icons'
 import {
   faBookmark as faBookmarkReg,
@@ -21,7 +22,8 @@ library.add(
   faThumbtack,
   faShareAlt,
   faExternalLinkAlt,
-  faFlag
+  faFlag,
+  faHistory
 )
 
 const ExtraButtons = {
@@ -84,6 +86,20 @@ const ExtraButtons = {
           visibility: this.status.visibility,
           statusContentType: data.content_type
         }))
+    },
+    showStatusHistory () {
+      let originalStatus = {}
+      Object.assign(originalStatus, this.status)
+      delete originalStatus.attachments
+      delete originalStatus.created_at
+      delete originalStatus.emojis
+      delete originalStatus.text
+      delete originalStatus.raw_html
+      delete originalStatus.nsfw
+      delete originalStatus.poll
+      delete originalStatus.summary
+      delete originalStatus.summary_raw_html
+      this.$store.dispatch('openStatusHistoryModal', originalStatus)
     }
   },
   computed: {
@@ -104,6 +120,9 @@ const ExtraButtons = {
     },
     statusLink () {
       return `${this.$store.state.instance.server}${this.$router.resolve({ name: 'conversation', params: { id: this.status.id } }).href}`
+    },
+    isEdited () {
+      return this.status.edited_at !== null
     }
   }
 }
diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue
index 8e90ee27..877e1424 100644
--- a/src/components/extra_buttons/extra_buttons.vue
+++ b/src/components/extra_buttons/extra_buttons.vue
@@ -84,6 +84,17 @@
             icon="pen"
           /><span>{{ $t("status.edit") }}</span>
         </button>
+        <button
+          v-if="isEdited"
+          class="button-default dropdown-item dropdown-item-icon"
+          @click.prevent="showStatusHistory"
+          @click="close"
+        >
+          <FAIcon
+            fixed-width
+            icon="history"
+          /><span>{{ $t("status.status_history") }}</span>
+        </button>
         <button
           v-if="canDelete"
           class="button-default dropdown-item dropdown-item-icon"
diff --git a/src/components/status_history_modal/status_history_modal.js b/src/components/status_history_modal/status_history_modal.js
new file mode 100644
index 00000000..3941a56f
--- /dev/null
+++ b/src/components/status_history_modal/status_history_modal.js
@@ -0,0 +1,60 @@
+import { get } from 'lodash'
+import Modal from '../modal/modal.vue'
+import Status from '../status/status.vue'
+
+const StatusHistoryModal = {
+  components: {
+    Modal,
+    Status
+  },
+  data () {
+    return {
+      statuses: []
+    }
+  },
+  computed: {
+    modalActivated () {
+      return this.$store.state.statusHistory.modalActivated
+    },
+    params () {
+      return this.$store.state.statusHistory.params
+    },
+    statusId () {
+      return this.params.id
+    },
+    historyCount () {
+      return this.statuses.length
+    },
+    history () {
+      return this.statuses
+    }
+  },
+  watch: {
+    params (newVal, oldVal) {
+      const newStatusId = get(newVal, 'id') !== get(oldVal, 'id')
+      if (newStatusId) {
+        this.resetHistory()
+      }
+
+      if (newStatusId || get(newVal, 'edited_at') !== get(oldVal, 'edited_at')) {
+        this.fetchStatusHistory()
+      }
+    }
+  },
+  methods: {
+    resetHistory () {
+      this.statuses = []
+    },
+    fetchStatusHistory () {
+      this.$store.dispatch('fetchStatusHistory', this.params)
+        .then(data => {
+          this.statuses = data
+        })
+    },
+    closeModal () {
+      this.$store.dispatch('closeStatusHistoryModal')
+    }
+  }
+}
+
+export default StatusHistoryModal
diff --git a/src/components/status_history_modal/status_history_modal.vue b/src/components/status_history_modal/status_history_modal.vue
new file mode 100644
index 00000000..d6680df2
--- /dev/null
+++ b/src/components/status_history_modal/status_history_modal.vue
@@ -0,0 +1,46 @@
+<template>
+  <Modal
+    v-if="modalActivated"
+    class="status-history-modal-view"
+    @backdropClicked="closeModal"
+  >
+    <div class="status-history-modal-panel panel">
+      <div class="panel-heading">
+        {{ $t('status.status_history') }} ({{ historyCount }})
+      </div>
+      <div class="panel-body">
+        <div
+          v-if="historyCount > 0"
+          class="history-body"
+        >
+          <status
+            v-for="status in history"
+            :key="status.id"
+            :statusoid="status"
+            :isPreview="true"
+            class="conversation-status status-fadein panel-body"
+        />
+        </div>
+      </div>
+    </div>
+  </Modal>
+</template>
+
+<script src="./status_history_modal.js"></script>
+
+<style lang="scss">
+.modal-view.status-history-modal-view {
+  align-items: flex-start;
+}
+.status-history-modal-panel {
+  flex-shrink: 0;
+  margin-top: 25%;
+  margin-bottom: 2em;
+  width: 100%;
+  max-width: 700px;
+
+  @media (orientation: landscape) {
+    margin-top: 8%;
+  }
+}
+</style>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 7b1bac79..f125c249 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -795,7 +795,8 @@
     "ancestor_follow_with_icon": "{icon} {text}",
     "show_all_conversation_with_icon": "{icon} {text}",
     "show_all_conversation": "Show full conversation ({numStatus} other status) | Show full conversation ({numStatus} other statuses)",
-    "show_only_conversation_under_this": "Only show replies to this status"
+    "show_only_conversation_under_this": "Only show replies to this status",
+    "status_history": "Status history"
   },
   "user_card": {
     "approve": "Approve",
diff --git a/src/main.js b/src/main.js
index f11b80ed..032b4c58 100644
--- a/src/main.js
+++ b/src/main.js
@@ -19,6 +19,7 @@ import reportsModule from './modules/reports.js'
 import pollsModule from './modules/polls.js'
 import postStatusModule from './modules/postStatus.js'
 import editStatusModule from './modules/editStatus.js'
+import statusHistoryModule from './modules/statusHistory.js'
 
 import chatsModule from './modules/chats.js'
 
@@ -84,6 +85,7 @@ const persistedStateOptions = {
       polls: pollsModule,
       postStatus: postStatusModule,
       editStatus: editStatusModule,
+      statusHistory: statusHistoryModule,
       chats: chatsModule
     },
     plugins,
diff --git a/src/modules/statusHistory.js b/src/modules/statusHistory.js
new file mode 100644
index 00000000..db3d6d4b
--- /dev/null
+++ b/src/modules/statusHistory.js
@@ -0,0 +1,25 @@
+const statusHistory = {
+  state: {
+    params: {},
+    modalActivated: false
+  },
+  mutations: {
+    openStatusHistoryModal (state, params) {
+      state.params = params
+      state.modalActivated = true
+    },
+    closeStatusHistoryModal (state) {
+      state.modalActivated = false
+    }
+  },
+  actions: {
+    openStatusHistoryModal ({ commit }, params) {
+      commit('openStatusHistoryModal', params)
+    },
+    closeStatusHistoryModal ({ commit }) {
+      commit('closeStatusHistoryModal')
+    }
+  }
+}
+
+export default statusHistory
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
index 19220442..a2e27b87 100644
--- a/src/modules/statuses.js
+++ b/src/modules/statuses.js
@@ -606,6 +606,9 @@ const statuses = {
     fetchStatusSource ({ rootState, dispatch }, status) {
       return apiService.fetchStatusSource({ id: status.id, credentials: rootState.users.currentUser.credentials })
     },
+    fetchStatusHistory ({ rootState, dispatch }, status) {
+      return apiService.fetchStatusHistory({ status })
+    },
     deleteStatus ({ rootState, commit }, status) {
       commit('setDeleted', { status })
       apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index 49b7e95a..9bc420c3 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -425,17 +425,16 @@ const fetchStatusSource = ({ id, credentials }) => {
     .then((data) => parseSource(data))
 }
 
-const fetchStatusHistory = ({ id, credentials }) => {
-  let url = MASTODON_STATUS_HISTORY_URL(id)
-  return fetch(url, { headers: authHeaders(credentials) })
+const fetchStatusHistory = ({ status, credentials }) => {
+  let url = MASTODON_STATUS_HISTORY_URL(status.id)
+  return promisedRequest({ url, credentials })
     .then((data) => {
-      if (data.ok) {
-        return data
-      }
-      throw new Error('Error fetching history', data)
+      data.reverse()
+      return data.map((item) => {
+        item.originalStatus = status
+        return parseStatus(item)
+      })
     })
-    .then((data) => data.json())
-    .then((data) => parseStatus(data))
 }
 
 const tagUser = ({ tag, credentials, user }) => {
diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index 82c19655..2a186ba1 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -275,7 +275,7 @@ export const parseStatus = (data) => {
 
     output.tags = data.tags
 
-    output.is_edited = data.edited_at !== null
+    output.edited_at = data.edited_at
 
     if (data.pleroma) {
       const { pleroma } = data
@@ -378,6 +378,10 @@ export const parseStatus = (data) => {
   output.favoritedBy = []
   output.rebloggedBy = []
 
+  if (data.hasOwnProperty('originalStatus')) {
+    Object.assign(output, data.originalStatus)
+  }
+
   return output
 }
 
-- 
cgit v1.2.3-70-g09d2


From f56298adbd6fc4b30f759f58336f881e9ebfab65 Mon Sep 17 00:00:00 2001
From: Sean King <seanking2919@protonmail.com>
Date: Tue, 21 Jun 2022 17:44:04 -0600
Subject: Add ability to detect whether backend supports editing

---
 src/App.js                                     | 1 +
 src/App.vue                                    | 4 ++--
 src/boot/after_store.js                        | 1 +
 src/components/extra_buttons/extra_buttons.js  | 3 ++-
 src/components/extra_buttons/extra_buttons.vue | 4 ++--
 5 files changed, 8 insertions(+), 5 deletions(-)

(limited to 'src/App.vue')

diff --git a/src/App.js b/src/App.js
index af638a1c..f87b3962 100644
--- a/src/App.js
+++ b/src/App.js
@@ -94,6 +94,7 @@ export default {
       return this.$store.getters.mergedConfig.alwaysShowNewPostButton || this.layoutType === 'mobile'
     },
     showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
+    editingAvailable () { return this.$store.state.instance.editingAvailable },
     shoutboxPosition () {
       return this.$store.getters.mergedConfig.alwaysShowNewPostButton || false
     },
diff --git a/src/App.vue b/src/App.vue
index 1b513e08..c20a4d9d 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -52,8 +52,8 @@
     <MobilePostStatusButton />
     <UserReportingModal />
     <PostStatusModal />
-    <EditStatusModal />
-    <StatusHistoryModal />
+    <EditStatusModal v-if="editingAvailable" />
+    <StatusHistoryModal v-if="editingAvailable" />
     <SettingsModal />
     <div id="modal" />
     <GlobalNoticeList />
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
index f655c38f..55e46c85 100644
--- a/src/boot/after_store.js
+++ b/src/boot/after_store.js
@@ -251,6 +251,7 @@ const getNodeInfo = async ({ store }) => {
       store.dispatch('setInstanceOption', { name: 'pleromaChatMessagesAvailable', value: features.includes('pleroma_chat_messages') })
       store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') })
       store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: features.includes('polls') })
+      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 })
 
diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js
index 11fe3235..92eb63b0 100644
--- a/src/components/extra_buttons/extra_buttons.js
+++ b/src/components/extra_buttons/extra_buttons.js
@@ -123,7 +123,8 @@ const ExtraButtons = {
     },
     isEdited () {
       return this.status.edited_at !== null
-    }
+    },
+    editingAvailable () { return this.$store.state.instance.editingAvailable }
   }
 }
 
diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue
index 877e1424..eda838dc 100644
--- a/src/components/extra_buttons/extra_buttons.vue
+++ b/src/components/extra_buttons/extra_buttons.vue
@@ -74,7 +74,7 @@
           /><span>{{ $t("status.unbookmark") }}</span>
         </button>
         <button
-          v-if="ownStatus"
+          v-if="ownStatus && editingAvailable"
           class="button-default dropdown-item dropdown-item-icon"
           @click.prevent="editStatus"
           @click="close"
@@ -85,7 +85,7 @@
           /><span>{{ $t("status.edit") }}</span>
         </button>
         <button
-          v-if="isEdited"
+          v-if="isEdited && editingAvailable"
           class="button-default dropdown-item dropdown-item-icon"
           @click.prevent="showStatusHistory"
           @click="close"
-- 
cgit v1.2.3-70-g09d2

