aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/App.js3
-rw-r--r--src/App.vue3
-rw-r--r--src/boot/after_store.js1
-rw-r--r--src/components/attachment/attachment.js3
-rw-r--r--src/components/attachment/attachment.vue6
-rw-r--r--src/components/conversation/conversation.js16
-rw-r--r--src/components/extra_buttons/extra_buttons.js18
-rw-r--r--src/components/extra_buttons/extra_buttons.vue13
-rw-r--r--src/components/post_status_form/post_status_form.js3
-rw-r--r--src/components/post_status_form/post_status_form.vue17
-rw-r--r--src/components/status_history_modal/status_history_modal.js60
-rw-r--r--src/components/status_history_modal/status_history_modal.vue46
-rw-r--r--src/i18n/en.json5
-rw-r--r--src/main.js2
-rw-r--r--src/modules/statusHistory.js25
-rw-r--r--src/modules/statuses.js3
-rw-r--r--src/services/api/api.service.js17
-rw-r--r--src/services/entity_normalizer/entity_normalizer.service.js6
18 files changed, 227 insertions, 20 deletions
diff --git a/src/App.js b/src/App.js
index 6e0e34a8..f87b3962 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: () => ({
@@ -92,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 9484f993..c20a4d9d 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -52,7 +52,8 @@
<MobilePostStatusButton />
<UserReportingModal />
<PostStatusModal />
- <EditStatusModal />
+ <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/attachment/attachment.js b/src/components/attachment/attachment.js
index d62a4adc..5dc50475 100644
--- a/src/components/attachment/attachment.js
+++ b/src/components/attachment/attachment.js
@@ -129,6 +129,9 @@ const Attachment = {
...mapGetters(['mergedConfig'])
},
watch: {
+ 'attachment.description' (newVal) {
+ this.localDescription = newVal
+ },
localDescription (newVal) {
this.onEdit(newVal)
}
diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index 14f6a0ae..2a89886d 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -166,7 +166,7 @@
:icon="placeholderIconClass"
/>
<p>
- {{ edit ? localDescription : attachment.description }}
+ {{ localDescription }}
</p>
</a>
@@ -244,7 +244,7 @@
</span>
</div>
<div
- v-if="size !== 'hide' && !hideDescription && (edit || (attachment.description && showDescription))"
+ v-if="size !== 'hide' && !hideDescription && (edit || (localDescription && showDescription))"
class="description-container"
:class="{ '-static': !edit }"
>
@@ -257,7 +257,7 @@
@keydown.enter.prevent=""
>
<p v-else>
- {{ attachment.description }}
+ {{ localDescription }}
</p>
</div>
</div>
diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js
index 2ef2977a..f8df9eb5 100644
--- a/src/components/conversation/conversation.js
+++ b/src/components/conversation/conversation.js
@@ -1,6 +1,8 @@
import { reduce, filter, findIndex, clone, get } from 'lodash'
import Status from '../status/status.vue'
import ThreadTree from '../thread_tree/thread_tree.vue'
+import { WSConnectionStatus } from '../../services/api/api.service.js'
+import { mapGetters, mapState } from 'vuex'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
@@ -77,6 +79,9 @@ const conversation = {
const maxDepth = this.$store.getters.mergedConfig.maxDepthInThread - 2
return maxDepth >= 1 ? maxDepth : 1
},
+ streamingEnabled () {
+ return this.mergedConfig.useStreamingApi && this.mastoUserSocketStatus === WSConnectionStatus.JOINED
+ },
displayStyle () {
return this.$store.getters.mergedConfig.conversationDisplay
},
@@ -339,7 +344,11 @@ const conversation = {
},
maybeHighlight () {
return this.isExpanded ? this.highlight : null
- }
+ },
+ ...mapGetters(['mergedConfig']),
+ ...mapState({
+ mastoUserSocketStatus: state => state.api.mastoUserSocketStatus
+ })
},
components: {
Status,
@@ -395,6 +404,11 @@ const conversation = {
setHighlight (id) {
if (!id) return
this.highlight = id
+
+ if (!this.streamingEnabled) {
+ this.$store.dispatch('fetchStatus', id)
+ }
+
this.$store.dispatch('fetchFavsAndRepeats', id)
this.$store.dispatch('fetchEmojiReactionsBy', id)
},
diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js
index 8a703953..ef040d26 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,12 @@ const ExtraButtons = {
visibility: this.status.visibility,
statusContentType: data.content_type
}))
+ },
+ showStatusHistory () {
+ const originalStatus = { ...this.status }
+ const stripFieldsList = ['attachments', 'created_at', 'emojis', 'text', 'raw_html', 'nsfw', 'poll', 'summary', 'summary_raw_html']
+ stripFieldsList.forEach(p => delete originalStatus[p])
+ this.$store.dispatch('openStatusHistoryModal', originalStatus)
}
},
computed: {
@@ -104,7 +112,11 @@ 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
+ },
+ 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 8e90ee27..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,6 +85,17 @@
/><span>{{ $t("status.edit") }}</span>
</button>
<button
+ v-if="isEdited && editingAvailable"
+ 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"
@click.prevent="deleteStatus"
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index d73219ad..ae81bfa6 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -261,6 +261,9 @@ const PostStatusForm = {
uploadFileLimitReached () {
return this.newStatus.files.length >= this.fileLimit
},
+ isEdit () {
+ return typeof this.statusId !== 'undefined' && this.statusId.trim() !== ''
+ },
...mapGetters(['mergedConfig']),
...mapState({
mobileLayout: state => state.interface.mobileLayout
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index 60cab745..f65058f4 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -67,6 +67,13 @@
<span v-else>{{ $t('post_status.direct_warning_to_all') }}</span>
</p>
<div
+ v-if="isEdit"
+ class="visibility-notice edit-warning"
+ >
+ <p>{{ $t('post_status.edit_remote_warning') }}</p>
+ <p>{{ $t('post_status.edit_unsupported_warning') }}</p>
+ </div>
+ <div
v-if="!disablePreview"
class="preview-heading faint"
>
@@ -411,6 +418,16 @@
align-items: baseline;
}
+ .visibility-notice.edit-warning {
+ > :first-child {
+ margin-top: 0;
+ }
+
+ > :last-child {
+ margin-bottom: 0;
+ }
+ }
+
.media-upload-icon, .poll-icon, .emoji-icon {
font-size: 1.85em;
line-height: 1.1;
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 fa773982..f125c249 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -216,6 +216,8 @@
"default": "Just landed in L.A.",
"direct_warning_to_all": "This post will be visible to all the mentioned users.",
"direct_warning_to_first_only": "This post will only be visible to the mentioned users at the beginning of the message.",
+ "edit_remote_warning": "Other remote instances may not support editing and unable to receive the latest version of your post.",
+ "edit_unsupported_warning": "Pleroma does not support editing mentions or polls.",
"posting": "Posting",
"post": "Post",
"preview": "Preview",
@@ -793,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
}