diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/App.js | 3 | ||||
| -rw-r--r-- | src/App.vue | 3 | ||||
| -rw-r--r-- | src/boot/after_store.js | 1 | ||||
| -rw-r--r-- | src/components/attachment/attachment.js | 3 | ||||
| -rw-r--r-- | src/components/attachment/attachment.vue | 6 | ||||
| -rw-r--r-- | src/components/conversation/conversation.js | 16 | ||||
| -rw-r--r-- | src/components/extra_buttons/extra_buttons.js | 18 | ||||
| -rw-r--r-- | src/components/extra_buttons/extra_buttons.vue | 13 | ||||
| -rw-r--r-- | src/components/post_status_form/post_status_form.js | 3 | ||||
| -rw-r--r-- | src/components/post_status_form/post_status_form.vue | 17 | ||||
| -rw-r--r-- | src/components/status_history_modal/status_history_modal.js | 60 | ||||
| -rw-r--r-- | src/components/status_history_modal/status_history_modal.vue | 46 | ||||
| -rw-r--r-- | src/i18n/en.json | 5 | ||||
| -rw-r--r-- | src/main.js | 2 | ||||
| -rw-r--r-- | src/modules/statusHistory.js | 25 | ||||
| -rw-r--r-- | src/modules/statuses.js | 3 | ||||
| -rw-r--r-- | src/services/api/api.service.js | 17 | ||||
| -rw-r--r-- | src/services/entity_normalizer/entity_normalizer.service.js | 6 |
18 files changed, 227 insertions, 20 deletions
@@ -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 } |
