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/components/extra_buttons/extra_buttons.vue | 11 +++++++++++
 1 file changed, 11 insertions(+)

(limited to 'src/components/extra_buttons/extra_buttons.vue')

diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue
index a3c3c767..8e90ee27 100644
--- a/src/components/extra_buttons/extra_buttons.vue
+++ b/src/components/extra_buttons/extra_buttons.vue
@@ -73,6 +73,17 @@
             icon="bookmark"
           /><span>{{ $t("status.unbookmark") }}</span>
         </button>
+        <button
+          v-if="ownStatus"
+          class="button-default dropdown-item dropdown-item-icon"
+          @click.prevent="editStatus"
+          @click="close"
+        >
+          <FAIcon
+            fixed-width
+            icon="pen"
+          /><span>{{ $t("status.edit") }}</span>
+        </button>
         <button
           v-if="canDelete"
           class="button-default dropdown-item dropdown-item-icon"
-- 
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/components/extra_buttons/extra_buttons.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/components/extra_buttons/extra_buttons.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


From b2e482774102a4e92b79c21cc57353e6677adf13 Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Sun, 16 Jan 2022 01:01:19 -0500
Subject: Add badges to status interacting buttons

Now, the following badges will be added:
0: (+) sign to reply, favourite, repeat, react and extra buttons
1: (-) sign to unfavourite and unrepeat
2: (x) sign to close reply form, close react popover, and close extra buttons popover
3: Check mark to favourited and repeated statuses

https://git.pleroma.social/pleroma/pleroma-fe/-/issues/1092
---
 src/App.scss                                       |  2 +-
 src/components/extra_buttons/extra_buttons.js      | 19 +++++++++-
 src/components/extra_buttons/extra_buttons.vue     | 34 +++++++++++++++--
 src/components/favorite_button/favorite_button.js  | 12 +++++-
 src/components/favorite_button/favorite_button.vue | 43 +++++++++++++++++++---
 src/components/react_button/react_button.js        | 17 ++++++++-
 src/components/react_button/react_button.vue       | 35 +++++++++++++++---
 src/components/reply_button/reply_button.js        | 12 +++++-
 src/components/reply_button/reply_button.vue       | 32 ++++++++++++++--
 src/components/retweet_button/retweet_button.js    | 14 ++++++-
 src/components/retweet_button/retweet_button.vue   | 43 +++++++++++++++++++---
 11 files changed, 229 insertions(+), 34 deletions(-)

(limited to 'src/components/extra_buttons/extra_buttons.vue')

diff --git a/src/App.scss b/src/App.scss
index ab025d63..3e23ebfa 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -752,7 +752,7 @@ option {
 }
 
 .fa-old-padding {
-  &.svg-inline--fa {
+  &.svg-inline--fa, &-layer {
     padding: 0 0.3em;
   }
 }
diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js
index 22ffb65a..68fa66ad 100644
--- a/src/components/extra_buttons/extra_buttons.js
+++ b/src/components/extra_buttons/extra_buttons.js
@@ -6,7 +6,9 @@ import {
   faEyeSlash,
   faThumbtack,
   faShareAlt,
-  faExternalLinkAlt
+  faExternalLinkAlt,
+  faPlus,
+  faTimes
 } from '@fortawesome/free-solid-svg-icons'
 import {
   faBookmark as faBookmarkReg,
@@ -21,13 +23,26 @@ library.add(
   faThumbtack,
   faShareAlt,
   faExternalLinkAlt,
-  faFlag
+  faFlag,
+  faPlus,
+  faTimes
 )
 
 const ExtraButtons = {
   props: ['status'],
   components: { Popover },
+  data () {
+    return {
+      expanded: false
+    }
+  },
   methods: {
+    onShow () {
+      this.expanded = true
+    },
+    onClose () {
+      this.expanded = false
+    },
     deleteStatus () {
       const confirmed = window.confirm(this.$t('status.delete_confirm'))
       if (confirmed) {
diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue
index 2c893bf3..23d74fce 100644
--- a/src/components/extra_buttons/extra_buttons.vue
+++ b/src/components/extra_buttons/extra_buttons.vue
@@ -6,6 +6,8 @@
     :offset="{ y: 5 }"
     :bound-to="{ x: 'container' }"
     remove-padding
+    @show="onShow"
+    @close="onClose"
   >
     <template #content="{close}">
       <div class="dropdown-menu">
@@ -122,10 +124,24 @@
     </template>
     <template #trigger>
       <span class="button-unstyled popover-trigger">
-        <FAIcon
-          class="fa-scale-110 fa-old-padding"
-          icon="ellipsis-h"
-        />
+        <FALayers class="fa-old-padding-layer">
+          <FAIcon
+            class="fa-scale-110 "
+            icon="ellipsis-h"
+          />
+          <FAIcon
+            v-if="!expanded"
+            class="focus-marker"
+            transform="shrink-6 up-8 right-16"
+            icon="plus"
+          />
+          <FAIcon
+            v-else
+            class="focus-marker"
+            transform="shrink-6 up-8 right-16"
+            icon="times"
+          />
+        </FALayers>
       </span>
     </template>
   </Popover>
@@ -151,6 +167,16 @@
       color: $fallback--text;
       color: var(--text, $fallback--text);
     }
+
+    .focus-marker {
+      visibility: hidden;
+    }
+
+    &:hover, &:focus {
+      .focus-marker {
+        visibility: visible;
+      }
+    }
   }
 }
 </style>
diff --git a/src/components/favorite_button/favorite_button.js b/src/components/favorite_button/favorite_button.js
index 5cd05f73..c996cba2 100644
--- a/src/components/favorite_button/favorite_button.js
+++ b/src/components/favorite_button/favorite_button.js
@@ -1,13 +1,21 @@
 import { mapGetters } from 'vuex'
 import { library } from '@fortawesome/fontawesome-svg-core'
-import { faStar } from '@fortawesome/free-solid-svg-icons'
+import {
+  faStar,
+  faPlus,
+  faMinus,
+  faCheck
+} from '@fortawesome/free-solid-svg-icons'
 import {
   faStar as faStarRegular
 } from '@fortawesome/free-regular-svg-icons'
 
 library.add(
   faStar,
-  faStarRegular
+  faStarRegular,
+  faPlus,
+  faMinus,
+  faCheck
 )
 
 const FavoriteButton = {
diff --git a/src/components/favorite_button/favorite_button.vue b/src/components/favorite_button/favorite_button.vue
index d5c4c61e..4015e1e6 100644
--- a/src/components/favorite_button/favorite_button.vue
+++ b/src/components/favorite_button/favorite_button.vue
@@ -7,11 +7,31 @@
       :title="$t('tool_tip.favorite')"
       @click.prevent="favorite()"
     >
-      <FAIcon
-        class="fa-scale-110 fa-old-padding"
-        :icon="[status.favorited ? 'fas' : 'far', 'star']"
-        :spin="animated"
-      />
+      <FALayers class="fa-scale-110 fa-old-padding-layer">
+        <FAIcon
+          class="fa-scale-110"
+          :icon="[status.favorited ? 'fas' : 'far', 'star']"
+          :spin="animated"
+        />
+        <FAIcon
+          v-if="status.favorited"
+          class="active-marker"
+          transform="shrink-6 up-9 right-12"
+          icon="check"
+        />
+        <FAIcon
+          v-if="!status.favorited"
+          class="focus-marker"
+          transform="shrink-6 up-9 right-12"
+          icon="plus"
+        />
+        <FAIcon
+          v-else
+          class="focus-marker"
+          transform="shrink-6 up-9 right-12"
+          icon="minus"
+        />
+      </FALayers>
     </button>
     <span v-else>
       <FAIcon
@@ -57,6 +77,19 @@
       color: $fallback--cOrange;
       color: var(--cOrange, $fallback--cOrange);
     }
+
+    .focus-marker {
+      visibility: hidden;
+    }
+
+    &:hover, &:focus {
+      .focus-marker {
+        visibility: visible;
+      }
+      .active-marker {
+        visibility: hidden;
+      }
+    }
   }
 }
 </style>
diff --git a/src/components/react_button/react_button.js b/src/components/react_button/react_button.js
index 37d6e7d0..5e052e1e 100644
--- a/src/components/react_button/react_button.js
+++ b/src/components/react_button/react_button.js
@@ -1,15 +1,21 @@
 import Popover from '../popover/popover.vue'
 import { library } from '@fortawesome/fontawesome-svg-core'
+import { faPlus, faTimes } from '@fortawesome/free-solid-svg-icons'
 import { faSmileBeam } from '@fortawesome/free-regular-svg-icons'
 import { trim } from 'lodash'
 
-library.add(faSmileBeam)
+library.add(
+  faPlus,
+  faTimes,
+  faSmileBeam
+)
 
 const ReactButton = {
   props: ['status'],
   data () {
     return {
-      filterWord: ''
+      filterWord: '',
+      expanded: false
     }
   },
   components: {
@@ -25,6 +31,13 @@ const ReactButton = {
       }
       close()
     },
+    onShow () {
+      this.expanded = true
+      this.focusInput()
+    },
+    onClose () {
+      this.expanded = false
+    },
     focusInput () {
       this.$nextTick(() => {
         const input = this.$el.querySelector('input')
diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue
index 5a809847..e30f6b68 100644
--- a/src/components/react_button/react_button.vue
+++ b/src/components/react_button/react_button.vue
@@ -7,7 +7,8 @@
     :bound-to="{ x: 'container' }"
     remove-padding
     popover-class="ReactButton popover-default"
-    @show="focusInput"
+    @show="onShow"
+    @close="onClose"
   >
     <template #content="{close}">
       <div class="reaction-picker-filter">
@@ -46,10 +47,24 @@
         class="button-unstyled popover-trigger"
         :title="$t('tool_tip.add_reaction')"
       >
-        <FAIcon
-          class="fa-scale-110 fa-old-padding"
-          :icon="['far', 'smile-beam']"
-        />
+        <FALayers>
+          <FAIcon
+            class="fa-scale-110 fa-old-padding"
+            :icon="['far', 'smile-beam']"
+          />
+          <FAIcon
+            v-if="!expanded"
+            class="focus-marker"
+            transform="shrink-6 up-9 right-17"
+            icon="plus"
+          />
+          <FAIcon
+            v-else
+            class="focus-marker"
+            transform="shrink-6 up-9 right-17"
+            icon="times"
+          />
+        </FALayers>
       </span>
     </template>
   </Popover>
@@ -125,6 +140,16 @@
       color: $fallback--text;
       color: var(--text, $fallback--text);
     }
+
+    .focus-marker {
+      visibility: hidden;
+    }
+
+    &:hover, &:focus {
+      .focus-marker {
+        visibility: visible;
+      }
+    }
   }
 }
 
diff --git a/src/components/reply_button/reply_button.js b/src/components/reply_button/reply_button.js
index c7bd2a2b..d6382982 100644
--- a/src/components/reply_button/reply_button.js
+++ b/src/components/reply_button/reply_button.js
@@ -1,7 +1,15 @@
 import { library } from '@fortawesome/fontawesome-svg-core'
-import { faReply } from '@fortawesome/free-solid-svg-icons'
+import {
+  faReply,
+  faPlus,
+  faTimes
+} from '@fortawesome/free-solid-svg-icons'
 
-library.add(faReply)
+library.add(
+  faReply,
+  faPlus,
+  faTimes
+)
 
 const ReplyButton = {
   name: 'ReplyButton',
diff --git a/src/components/reply_button/reply_button.vue b/src/components/reply_button/reply_button.vue
index c17041da..56156c55 100644
--- a/src/components/reply_button/reply_button.vue
+++ b/src/components/reply_button/reply_button.vue
@@ -7,10 +7,24 @@
       :title="$t('tool_tip.reply')"
       @click.prevent="$emit('toggle')"
     >
-      <FAIcon
-        class="fa-scale-110 fa-old-padding"
-        icon="reply"
-      />
+      <FALayers class="fa-old-padding-layer">
+        <FAIcon
+          class="fa-scale-110"
+          icon="reply"
+        />
+        <FAIcon
+          v-if="!replying"
+          class="focus-marker"
+          transform="shrink-6 up-8 right-16"
+          icon="plus"
+        />
+        <FAIcon
+          v-else
+          class="focus-marker"
+          transform="shrink-6 up-8 right-16"
+          icon="times"
+        />
+      </FALayers>
     </button>
     <span v-else>
       <FAIcon
@@ -52,6 +66,16 @@
       color: $fallback--cBlue;
       color: var(--cBlue, $fallback--cBlue);
     }
+
+    .focus-marker {
+      visibility: hidden;
+    }
+
+    &:hover, &:focus {
+      .focus-marker {
+        visibility: visible;
+      }
+    }
   }
 
 }
diff --git a/src/components/retweet_button/retweet_button.js b/src/components/retweet_button/retweet_button.js
index 2103fd0b..b7911814 100644
--- a/src/components/retweet_button/retweet_button.js
+++ b/src/components/retweet_button/retweet_button.js
@@ -1,7 +1,17 @@
 import { library } from '@fortawesome/fontawesome-svg-core'
-import { faRetweet } from '@fortawesome/free-solid-svg-icons'
+import {
+  faRetweet,
+  faPlus,
+  faMinus,
+  faCheck
+} from '@fortawesome/free-solid-svg-icons'
 
-library.add(faRetweet)
+library.add(
+  faRetweet,
+  faPlus,
+  faMinus,
+  faCheck
+)
 
 const RetweetButton = {
   props: ['status', 'loggedIn', 'visibility'],
diff --git a/src/components/retweet_button/retweet_button.vue b/src/components/retweet_button/retweet_button.vue
index 5a15d387..ed84066d 100644
--- a/src/components/retweet_button/retweet_button.vue
+++ b/src/components/retweet_button/retweet_button.vue
@@ -7,11 +7,31 @@
       :title="$t('tool_tip.repeat')"
       @click.prevent="retweet()"
     >
-      <FAIcon
-        class="fa-scale-110 fa-old-padding"
-        icon="retweet"
-        :spin="animated"
-      />
+      <FALayers class="fa-old-padding-layer">
+        <FAIcon
+          class="fa-scale-110"
+          icon="retweet"
+          :spin="animated"
+        />
+        <FAIcon
+          v-if="status.repeated"
+          class="active-marker"
+          transform="shrink-6 up-9 right-12"
+          icon="check"
+        />
+        <FAIcon
+          v-if="!status.repeated"
+          class="focus-marker"
+          transform="shrink-6 up-9 right-12"
+          icon="plus"
+        />
+        <FAIcon
+          v-else
+          class="focus-marker"
+          transform="shrink-6 up-9 right-12"
+          icon="minus"
+        />
+      </FALayers>
     </button>
     <span v-else-if="loggedIn">
       <FAIcon
@@ -64,6 +84,19 @@
       color: $fallback--cGreen;
       color: var(--cGreen, $fallback--cGreen);
     }
+
+    .focus-marker {
+      visibility: hidden;
+    }
+
+    &:hover, &:focus {
+      .focus-marker {
+        visibility: visible;
+      }
+      .active-marker {
+        visibility: hidden;
+      }
+    }
   }
 }
 </style>
-- 
cgit v1.2.3-70-g09d2


From 97951fccfdce726c350ab633acc45213d675adb9 Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Wed, 19 Jan 2022 19:45:38 -0500
Subject: Use :focus-visible instead of :focus for focus markers

In this way, after the user clicked with a pointer and moved that
pointer away, the focus marker will no longer show a focused style.

As Safari remains the only major browser engine that does not support
:focus-visible, a fallback to :focus is used if there is no browser
support for :focus-visible.

https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible
---
 src/components/extra_buttons/extra_buttons.vue     | 5 +++--
 src/components/favorite_button/favorite_button.vue | 9 +++++++--
 src/components/react_button/react_button.vue       | 5 +++--
 src/components/reply_button/reply_button.vue       | 5 +++--
 src/components/retweet_button/retweet_button.vue   | 9 +++++++--
 5 files changed, 23 insertions(+), 10 deletions(-)

(limited to 'src/components/extra_buttons/extra_buttons.vue')

diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue
index 23d74fce..d15e3102 100644
--- a/src/components/extra_buttons/extra_buttons.vue
+++ b/src/components/extra_buttons/extra_buttons.vue
@@ -168,11 +168,12 @@
       color: var(--text, $fallback--text);
     }
 
-    .focus-marker {
+    .focus-marker,
+    &:focus:not(:focus-visible):not(:hover) .focus-marker {
       visibility: hidden;
     }
 
-    &:hover, &:focus {
+    &:hover, &:focus, &:focus-visible {
       .focus-marker {
         visibility: visible;
       }
diff --git a/src/components/favorite_button/favorite_button.vue b/src/components/favorite_button/favorite_button.vue
index 4015e1e6..01db63be 100644
--- a/src/components/favorite_button/favorite_button.vue
+++ b/src/components/favorite_button/favorite_button.vue
@@ -78,11 +78,16 @@
       color: var(--cOrange, $fallback--cOrange);
     }
 
-    .focus-marker {
+    .focus-marker,
+    &:focus:not(:focus-visible):not(:hover) .focus-marker {
       visibility: hidden;
     }
 
-    &:hover, &:focus {
+    &:focus:not(:focus-visible):not(:hover) .active-marker {
+      visibility: visible;
+    }
+
+    &:hover, &:focus, &:focus-visible {
       .focus-marker {
         visibility: visible;
       }
diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue
index e30f6b68..885869f9 100644
--- a/src/components/react_button/react_button.vue
+++ b/src/components/react_button/react_button.vue
@@ -141,11 +141,12 @@
       color: var(--text, $fallback--text);
     }
 
-    .focus-marker {
+    .focus-marker,
+    &:focus:not(:focus-visible):not(:hover) .focus-marker {
       visibility: hidden;
     }
 
-    &:hover, &:focus {
+    &:hover, &:focus, &:focus-visible {
       .focus-marker {
         visibility: visible;
       }
diff --git a/src/components/reply_button/reply_button.vue b/src/components/reply_button/reply_button.vue
index d75e6fd9..0466ceb3 100644
--- a/src/components/reply_button/reply_button.vue
+++ b/src/components/reply_button/reply_button.vue
@@ -67,11 +67,12 @@
       color: var(--cBlue, $fallback--cBlue);
     }
 
-    .focus-marker {
+    .focus-marker,
+    &:focus:not(:focus-visible):not(:hover) .focus-marker {
       visibility: hidden;
     }
 
-    &:hover, &:focus {
+    &:hover, &:focus, &:focus-visible {
       .focus-marker {
         visibility: visible;
       }
diff --git a/src/components/retweet_button/retweet_button.vue b/src/components/retweet_button/retweet_button.vue
index ed84066d..d88adacc 100644
--- a/src/components/retweet_button/retweet_button.vue
+++ b/src/components/retweet_button/retweet_button.vue
@@ -85,11 +85,16 @@
       color: var(--cGreen, $fallback--cGreen);
     }
 
-    .focus-marker {
+    .focus-marker,
+    &:focus:not(:focus-visible):not(:hover) .focus-marker {
       visibility: hidden;
     }
 
-    &:hover, &:focus {
+    &:focus:not(:focus-visible):not(:hover) .active-marker {
+      visibility: visible;
+    }
+
+    &:hover, &:focus, &:focus-visible {
       .focus-marker {
         visibility: visible;
       }
-- 
cgit v1.2.3-70-g09d2


From 11672bc4907d41706ad164f1ca3c485f034061c1 Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Wed, 19 Jan 2022 21:18:02 -0500
Subject: Fix styling on Safari

---
 src/_mixins.scss                                   | 17 +++++++++++++++++
 src/components/extra_buttons/extra_buttons.vue     | 10 ++++++----
 src/components/favorite_button/favorite_button.vue | 17 ++++++++++-------
 src/components/react_button/react_button.vue       | 10 ++++++----
 src/components/reply_button/reply_button.vue       | 10 ++++++----
 src/components/retweet_button/retweet_button.vue   | 17 ++++++++++-------
 6 files changed, 55 insertions(+), 26 deletions(-)
 create mode 100644 src/_mixins.scss

(limited to 'src/components/extra_buttons/extra_buttons.vue')

diff --git a/src/_mixins.scss b/src/_mixins.scss
new file mode 100644
index 00000000..1fed16c3
--- /dev/null
+++ b/src/_mixins.scss
@@ -0,0 +1,17 @@
+@mixin unfocused-style {
+  @content;
+
+  &:focus:not(:focus-visible):not(:hover) {
+    @content;
+  }
+}
+
+@mixin focused-style {
+  &:hover, &:focus {
+    @content;
+  }
+
+  &:focus-visible {
+    @content;
+  }
+}
diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue
index d15e3102..ae50c316 100644
--- a/src/components/extra_buttons/extra_buttons.vue
+++ b/src/components/extra_buttons/extra_buttons.vue
@@ -151,6 +151,7 @@
 
 <style lang="scss">
 @import '../../_variables.scss';
+@import '../../_mixins.scss';
 
 .ExtraButtons {
   /* override of popover internal stuff */
@@ -168,12 +169,13 @@
       color: var(--text, $fallback--text);
     }
 
-    .focus-marker,
-    &:focus:not(:focus-visible):not(:hover) .focus-marker {
-      visibility: hidden;
+    @include unfocused-style {
+      .focus-marker {
+        visibility: hidden;
+      }
     }
 
-    &:hover, &:focus, &:focus-visible {
+    @include focused-style {
       .focus-marker {
         visibility: visible;
       }
diff --git a/src/components/favorite_button/favorite_button.vue b/src/components/favorite_button/favorite_button.vue
index 01db63be..74a1dfbb 100644
--- a/src/components/favorite_button/favorite_button.vue
+++ b/src/components/favorite_button/favorite_button.vue
@@ -53,6 +53,7 @@
 
 <style lang="scss">
 @import '../../_variables.scss';
+@import '../../_mixins.scss';
 
 .FavoriteButton {
   display: flex;
@@ -78,19 +79,21 @@
       color: var(--cOrange, $fallback--cOrange);
     }
 
-    .focus-marker,
-    &:focus:not(:focus-visible):not(:hover) .focus-marker {
-      visibility: hidden;
-    }
+    @include unfocused-style {
+      .focus-marker {
+        visibility: hidden;
+      }
 
-    &:focus:not(:focus-visible):not(:hover) .active-marker {
-      visibility: visible;
+      .active-marker {
+        visibility: visible;
+      }
     }
 
-    &:hover, &:focus, &:focus-visible {
+    @include focused-style {
       .focus-marker {
         visibility: visible;
       }
+
       .active-marker {
         visibility: hidden;
       }
diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue
index 885869f9..8fa4d08b 100644
--- a/src/components/react_button/react_button.vue
+++ b/src/components/react_button/react_button.vue
@@ -74,6 +74,7 @@
 
 <style lang="scss">
 @import '../../_variables.scss';
+@import '../../_mixins.scss';
 
 .ReactButton {
   .reaction-picker-filter {
@@ -141,12 +142,13 @@
       color: var(--text, $fallback--text);
     }
 
-    .focus-marker,
-    &:focus:not(:focus-visible):not(:hover) .focus-marker {
-      visibility: hidden;
+    @include unfocused-style {
+      .focus-marker {
+        visibility: hidden;
+      }
     }
 
-    &:hover, &:focus, &:focus-visible {
+    @include focused-style {
       .focus-marker {
         visibility: visible;
       }
diff --git a/src/components/reply_button/reply_button.vue b/src/components/reply_button/reply_button.vue
index 0466ceb3..ea97fbaa 100644
--- a/src/components/reply_button/reply_button.vue
+++ b/src/components/reply_button/reply_button.vue
@@ -46,6 +46,7 @@
 
 <style lang="scss">
 @import '../../_variables.scss';
+@import '../../_mixins.scss';
 
 .ReplyButton {
   display: flex;
@@ -67,12 +68,13 @@
       color: var(--cBlue, $fallback--cBlue);
     }
 
-    .focus-marker,
-    &:focus:not(:focus-visible):not(:hover) .focus-marker {
-      visibility: hidden;
+    @include unfocused-style {
+      .focus-marker {
+        visibility: hidden;
+      }
     }
 
-    &:hover, &:focus, &:focus-visible {
+    @include focused-style {
       .focus-marker {
         visibility: visible;
       }
diff --git a/src/components/retweet_button/retweet_button.vue b/src/components/retweet_button/retweet_button.vue
index d88adacc..396d1200 100644
--- a/src/components/retweet_button/retweet_button.vue
+++ b/src/components/retweet_button/retweet_button.vue
@@ -60,6 +60,7 @@
 
 <style lang="scss">
 @import '../../_variables.scss';
+@import '../../_mixins.scss';
 
 .RetweetButton {
   display: flex;
@@ -85,19 +86,21 @@
       color: var(--cGreen, $fallback--cGreen);
     }
 
-    .focus-marker,
-    &:focus:not(:focus-visible):not(:hover) .focus-marker {
-      visibility: hidden;
-    }
+    @include unfocused-style {
+      .focus-marker {
+        visibility: hidden;
+      }
 
-    &:focus:not(:focus-visible):not(:hover) .active-marker {
-      visibility: visible;
+      .active-marker {
+        visibility: visible;
+      }
     }
 
-    &:hover, &:focus, &:focus-visible {
+    @include focused-style {
       .focus-marker {
         visibility: visible;
       }
+
       .active-marker {
         visibility: hidden;
       }
-- 
cgit v1.2.3-70-g09d2


From 5676b5cfc971edd1ce70763548b03c51963e8301 Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Sat, 16 Apr 2022 15:27:35 -0400
Subject: Fix popover not popping up

Sometimes the "focus-marker" gets clicked. In this situation,
it will trigger an open, and then a close, because the original
focus-marker was removed from the dom, so $el.contains() no
longer thinks the marker is part of the trigger! We replaced the
v-if with v-show to resolve this problem.
---
 src/components/extra_buttons/extra_buttons.vue | 4 ++--
 src/components/react_button/react_button.vue   | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

(limited to 'src/components/extra_buttons/extra_buttons.vue')

diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue
index ae50c316..335aabb6 100644
--- a/src/components/extra_buttons/extra_buttons.vue
+++ b/src/components/extra_buttons/extra_buttons.vue
@@ -130,13 +130,13 @@
             icon="ellipsis-h"
           />
           <FAIcon
-            v-if="!expanded"
+            v-show="!expanded"
             class="focus-marker"
             transform="shrink-6 up-8 right-16"
             icon="plus"
           />
           <FAIcon
-            v-else
+            v-show="expanded"
             class="focus-marker"
             transform="shrink-6 up-8 right-16"
             icon="times"
diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue
index 8fa4d08b..feb95013 100644
--- a/src/components/react_button/react_button.vue
+++ b/src/components/react_button/react_button.vue
@@ -53,13 +53,13 @@
             :icon="['far', 'smile-beam']"
           />
           <FAIcon
-            v-if="!expanded"
+            v-show="!expanded"
             class="focus-marker"
             transform="shrink-6 up-9 right-17"
             icon="plus"
           />
           <FAIcon
-            v-else
+            v-show="expanded"
             class="focus-marker"
             transform="shrink-6 up-9 right-17"
             icon="times"
-- 
cgit v1.2.3-70-g09d2


From cd536157ee0e675f7a2d408043c2ddbbc30eaa3b Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Fri, 19 Aug 2022 08:45:59 -0400
Subject: Fix react & extra buttons not styled on tab-focus

---
 src/components/extra_buttons/extra_buttons.vue | 3 +++
 src/components/react_button/react_button.vue   | 3 +++
 2 files changed, 6 insertions(+)

(limited to 'src/components/extra_buttons/extra_buttons.vue')

diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue
index 335aabb6..011dff9b 100644
--- a/src/components/extra_buttons/extra_buttons.vue
+++ b/src/components/extra_buttons/extra_buttons.vue
@@ -169,6 +169,9 @@
       color: var(--text, $fallback--text);
     }
 
+  }
+
+  .popover-trigger-button {
     @include unfocused-style {
       .focus-marker {
         visibility: hidden;
diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue
index feb95013..254c49db 100644
--- a/src/components/react_button/react_button.vue
+++ b/src/components/react_button/react_button.vue
@@ -142,6 +142,9 @@
       color: var(--text, $fallback--text);
     }
 
+  }
+
+  .popover-trigger-button {
     @include unfocused-style {
       .focus-marker {
         visibility: hidden;
-- 
cgit v1.2.3-70-g09d2

