Merge branch 'add/edit-status' into 'develop'
Add edit status functionality See merge request pleroma/pleroma-fe!1537
This commit is contained in:
commit
2bea5d8128
|
@ -10,3 +10,5 @@ Contributors of this project.
|
||||||
- shpuld (shpuld@shitposter.club): CSS and styling
|
- shpuld (shpuld@shitposter.club): CSS and styling
|
||||||
- Vincent Guth (https://unsplash.com/photos/XrwVIFy6rTw): Background images.
|
- Vincent Guth (https://unsplash.com/photos/XrwVIFy6rTw): Background images.
|
||||||
- hj (hj@shigusegubu.club): Code
|
- hj (hj@shigusegubu.club): Code
|
||||||
|
- Sean King (seanking@freespeechextremist.com): Code
|
||||||
|
- Tusooa Zhu (tusooa@kazv.moe): Code
|
||||||
|
|
|
@ -10,7 +10,9 @@ import MobilePostStatusButton from './components/mobile_post_status_button/mobil
|
||||||
import MobileNav from './components/mobile_nav/mobile_nav.vue'
|
import MobileNav from './components/mobile_nav/mobile_nav.vue'
|
||||||
import DesktopNav from './components/desktop_nav/desktop_nav.vue'
|
import DesktopNav from './components/desktop_nav/desktop_nav.vue'
|
||||||
import UserReportingModal from './components/user_reporting_modal/user_reporting_modal.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 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 GlobalNoticeList from './components/global_notice_list/global_notice_list.vue'
|
||||||
import { windowWidth, windowHeight } from './services/window_utils/window_utils'
|
import { windowWidth, windowHeight } from './services/window_utils/window_utils'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
@ -35,6 +37,8 @@ export default {
|
||||||
UpdateNotification: defineAsyncComponent(() => import('./components/update_notification/update_notification.vue')),
|
UpdateNotification: defineAsyncComponent(() => import('./components/update_notification/update_notification.vue')),
|
||||||
UserReportingModal,
|
UserReportingModal,
|
||||||
PostStatusModal,
|
PostStatusModal,
|
||||||
|
EditStatusModal,
|
||||||
|
StatusHistoryModal,
|
||||||
GlobalNoticeList
|
GlobalNoticeList
|
||||||
},
|
},
|
||||||
data: () => ({
|
data: () => ({
|
||||||
|
@ -101,6 +105,7 @@ export default {
|
||||||
return this.$store.getters.mergedConfig.alwaysShowNewPostButton || this.layoutType === 'mobile'
|
return this.$store.getters.mergedConfig.alwaysShowNewPostButton || this.layoutType === 'mobile'
|
||||||
},
|
},
|
||||||
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
|
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
|
||||||
|
editingAvailable () { return this.$store.state.instance.editingAvailable },
|
||||||
shoutboxPosition () {
|
shoutboxPosition () {
|
||||||
return this.$store.getters.mergedConfig.alwaysShowNewPostButton || false
|
return this.$store.getters.mergedConfig.alwaysShowNewPostButton || false
|
||||||
},
|
},
|
||||||
|
|
|
@ -67,6 +67,8 @@
|
||||||
<MobilePostStatusButton />
|
<MobilePostStatusButton />
|
||||||
<UserReportingModal />
|
<UserReportingModal />
|
||||||
<PostStatusModal />
|
<PostStatusModal />
|
||||||
|
<EditStatusModal v-if="editingAvailable" />
|
||||||
|
<StatusHistoryModal v-if="editingAvailable" />
|
||||||
<SettingsModal />
|
<SettingsModal />
|
||||||
<UpdateNotification />
|
<UpdateNotification />
|
||||||
<div id="modal" />
|
<div id="modal" />
|
||||||
|
|
|
@ -251,6 +251,7 @@ const getNodeInfo = async ({ store }) => {
|
||||||
store.dispatch('setInstanceOption', { name: 'pleromaChatMessagesAvailable', value: features.includes('pleroma_chat_messages') })
|
store.dispatch('setInstanceOption', { name: 'pleromaChatMessagesAvailable', value: features.includes('pleroma_chat_messages') })
|
||||||
store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') })
|
store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') })
|
||||||
store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: features.includes('polls') })
|
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: 'pollLimits', value: metadata.pollLimits })
|
||||||
store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled })
|
store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled })
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,9 @@ const Attachment = {
|
||||||
...mapGetters(['mergedConfig'])
|
...mapGetters(['mergedConfig'])
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
'attachment.description' (newVal) {
|
||||||
|
this.localDescription = newVal
|
||||||
|
},
|
||||||
localDescription (newVal) {
|
localDescription (newVal) {
|
||||||
this.onEdit(newVal)
|
this.onEdit(newVal)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { reduce, filter, findIndex, clone, get } from 'lodash'
|
import { reduce, filter, findIndex, clone, get } from 'lodash'
|
||||||
import Status from '../status/status.vue'
|
import Status from '../status/status.vue'
|
||||||
import ThreadTree from '../thread_tree/thread_tree.vue'
|
import ThreadTree from '../thread_tree/thread_tree.vue'
|
||||||
|
import { WSConnectionStatus } from '../../services/api/api.service.js'
|
||||||
|
import { mapGetters, mapState } from 'vuex'
|
||||||
import QuickFilterSettings from '../quick_filter_settings/quick_filter_settings.vue'
|
import QuickFilterSettings from '../quick_filter_settings/quick_filter_settings.vue'
|
||||||
import QuickViewSettings from '../quick_view_settings/quick_view_settings.vue'
|
import QuickViewSettings from '../quick_view_settings/quick_view_settings.vue'
|
||||||
|
|
||||||
|
@ -79,6 +81,9 @@ const conversation = {
|
||||||
const maxDepth = this.$store.getters.mergedConfig.maxDepthInThread - 2
|
const maxDepth = this.$store.getters.mergedConfig.maxDepthInThread - 2
|
||||||
return maxDepth >= 1 ? maxDepth : 1
|
return maxDepth >= 1 ? maxDepth : 1
|
||||||
},
|
},
|
||||||
|
streamingEnabled () {
|
||||||
|
return this.mergedConfig.useStreamingApi && this.mastoUserSocketStatus === WSConnectionStatus.JOINED
|
||||||
|
},
|
||||||
displayStyle () {
|
displayStyle () {
|
||||||
return this.$store.getters.mergedConfig.conversationDisplay
|
return this.$store.getters.mergedConfig.conversationDisplay
|
||||||
},
|
},
|
||||||
|
@ -341,7 +346,11 @@ const conversation = {
|
||||||
},
|
},
|
||||||
maybeHighlight () {
|
maybeHighlight () {
|
||||||
return this.isExpanded ? this.highlight : null
|
return this.isExpanded ? this.highlight : null
|
||||||
}
|
},
|
||||||
|
...mapGetters(['mergedConfig']),
|
||||||
|
...mapState({
|
||||||
|
mastoUserSocketStatus: state => state.api.mastoUserSocketStatus
|
||||||
|
})
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Status,
|
Status,
|
||||||
|
@ -399,6 +408,11 @@ const conversation = {
|
||||||
setHighlight (id) {
|
setHighlight (id) {
|
||||||
if (!id) return
|
if (!id) return
|
||||||
this.highlight = id
|
this.highlight = id
|
||||||
|
|
||||||
|
if (!this.streamingEnabled) {
|
||||||
|
this.$store.dispatch('fetchStatus', id)
|
||||||
|
}
|
||||||
|
|
||||||
this.$store.dispatch('fetchFavsAndRepeats', id)
|
this.$store.dispatch('fetchFavsAndRepeats', id)
|
||||||
this.$store.dispatch('fetchEmojiReactionsBy', id)
|
this.$store.dispatch('fetchEmojiReactionsBy', id)
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
||||||
|
import Modal from '../modal/modal.vue'
|
||||||
|
import statusPosterService from '../../services/status_poster/status_poster.service.js'
|
||||||
|
import get from 'lodash/get'
|
||||||
|
|
||||||
|
const EditStatusModal = {
|
||||||
|
components: {
|
||||||
|
PostStatusForm,
|
||||||
|
Modal
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
resettingForm: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isLoggedIn () {
|
||||||
|
return !!this.$store.state.users.currentUser
|
||||||
|
},
|
||||||
|
modalActivated () {
|
||||||
|
return this.$store.state.editStatus.modalActivated
|
||||||
|
},
|
||||||
|
isFormVisible () {
|
||||||
|
return this.isLoggedIn && !this.resettingForm && this.modalActivated
|
||||||
|
},
|
||||||
|
params () {
|
||||||
|
return this.$store.state.editStatus.params || {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
params (newVal, oldVal) {
|
||||||
|
if (get(newVal, 'statusId') !== get(oldVal, 'statusId')) {
|
||||||
|
this.resettingForm = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.resettingForm = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isFormVisible (val) {
|
||||||
|
if (val) {
|
||||||
|
this.$nextTick(() => this.$el && this.$el.querySelector('textarea').focus())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
doEditStatus ({ status, spoilerText, sensitive, media, contentType, poll }) {
|
||||||
|
const params = {
|
||||||
|
store: this.$store,
|
||||||
|
statusId: this.$store.state.editStatus.params.statusId,
|
||||||
|
status,
|
||||||
|
spoilerText,
|
||||||
|
sensitive,
|
||||||
|
poll,
|
||||||
|
media,
|
||||||
|
contentType
|
||||||
|
}
|
||||||
|
|
||||||
|
return statusPosterService.editStatus(params)
|
||||||
|
.then((data) => {
|
||||||
|
return data
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error('Error editing status', err)
|
||||||
|
return {
|
||||||
|
error: err.message
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
closeModal () {
|
||||||
|
this.$store.dispatch('closeEditStatusModal')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditStatusModal
|
|
@ -0,0 +1,48 @@
|
||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
v-if="isFormVisible"
|
||||||
|
class="edit-form-modal-view"
|
||||||
|
@backdropClicked="closeModal"
|
||||||
|
>
|
||||||
|
<div class="edit-form-modal-panel panel">
|
||||||
|
<div class="panel-heading">
|
||||||
|
{{ $t('post_status.edit_status') }}
|
||||||
|
</div>
|
||||||
|
<PostStatusForm
|
||||||
|
class="panel-body"
|
||||||
|
v-bind="params"
|
||||||
|
:post-handler="doEditStatus"
|
||||||
|
:disable-polls="true"
|
||||||
|
:disable-visibility-selector="true"
|
||||||
|
@posted="closeModal"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./edit_status_modal.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.modal-view.edit-form-modal-view {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.edit-form-modal-panel {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 25%;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 700px;
|
||||||
|
|
||||||
|
@media (orientation: landscape) {
|
||||||
|
margin-top: 8%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-bottom-left {
|
||||||
|
max-width: 6.5em;
|
||||||
|
|
||||||
|
.emoji-icon {
|
||||||
|
justify-content: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -7,6 +7,7 @@ import {
|
||||||
faThumbtack,
|
faThumbtack,
|
||||||
faShareAlt,
|
faShareAlt,
|
||||||
faExternalLinkAlt,
|
faExternalLinkAlt,
|
||||||
|
faHistory,
|
||||||
faPlus,
|
faPlus,
|
||||||
faTimes
|
faTimes
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
@ -24,6 +25,7 @@ library.add(
|
||||||
faShareAlt,
|
faShareAlt,
|
||||||
faExternalLinkAlt,
|
faExternalLinkAlt,
|
||||||
faFlag,
|
faFlag,
|
||||||
|
faHistory,
|
||||||
faPlus,
|
faPlus,
|
||||||
faTimes
|
faTimes
|
||||||
)
|
)
|
||||||
|
@ -86,6 +88,25 @@ const ExtraButtons = {
|
||||||
},
|
},
|
||||||
reportStatus () {
|
reportStatus () {
|
||||||
this.$store.dispatch('openUserReportingModal', { userId: this.status.user.id, statusIds: [this.status.id] })
|
this.$store.dispatch('openUserReportingModal', { userId: this.status.user.id, statusIds: [this.status.id] })
|
||||||
|
},
|
||||||
|
editStatus () {
|
||||||
|
this.$store.dispatch('fetchStatusSource', { id: this.status.id })
|
||||||
|
.then(data => this.$store.dispatch('openEditStatusModal', {
|
||||||
|
statusId: this.status.id,
|
||||||
|
subject: data.spoiler_text,
|
||||||
|
statusText: data.text,
|
||||||
|
statusIsSensitive: this.status.nsfw,
|
||||||
|
statusPoll: this.status.poll,
|
||||||
|
statusFiles: [...this.status.attachments],
|
||||||
|
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: {
|
computed: {
|
||||||
|
@ -109,7 +130,11 @@ const ExtraButtons = {
|
||||||
},
|
},
|
||||||
statusLink () {
|
statusLink () {
|
||||||
return `${this.$store.state.instance.server}${this.$router.resolve({ name: 'conversation', params: { id: this.status.id } }).href}`
|
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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,28 @@
|
||||||
/><span>{{ $t("status.unbookmark") }}</span>
|
/><span>{{ $t("status.unbookmark") }}</span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
<button
|
||||||
|
v-if="ownStatus && editingAvailable"
|
||||||
|
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="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
|
<button
|
||||||
v-if="canDelete"
|
v-if="canDelete"
|
||||||
class="button-default dropdown-item dropdown-item-icon"
|
class="button-default dropdown-item dropdown-item-icon"
|
||||||
|
|
|
@ -55,6 +55,14 @@ const pxStringToNumber = (str) => {
|
||||||
|
|
||||||
const PostStatusForm = {
|
const PostStatusForm = {
|
||||||
props: [
|
props: [
|
||||||
|
'statusId',
|
||||||
|
'statusText',
|
||||||
|
'statusIsSensitive',
|
||||||
|
'statusPoll',
|
||||||
|
'statusFiles',
|
||||||
|
'statusMediaDescriptions',
|
||||||
|
'statusScope',
|
||||||
|
'statusContentType',
|
||||||
'replyTo',
|
'replyTo',
|
||||||
'repliedUser',
|
'repliedUser',
|
||||||
'attentions',
|
'attentions',
|
||||||
|
@ -62,6 +70,7 @@ const PostStatusForm = {
|
||||||
'subject',
|
'subject',
|
||||||
'disableSubject',
|
'disableSubject',
|
||||||
'disableScopeSelector',
|
'disableScopeSelector',
|
||||||
|
'disableVisibilitySelector',
|
||||||
'disableNotice',
|
'disableNotice',
|
||||||
'disableLockWarning',
|
'disableLockWarning',
|
||||||
'disablePolls',
|
'disablePolls',
|
||||||
|
@ -125,22 +134,38 @@ const PostStatusForm = {
|
||||||
|
|
||||||
const { postContentType: contentType, sensitiveByDefault } = this.$store.getters.mergedConfig
|
const { postContentType: contentType, sensitiveByDefault } = this.$store.getters.mergedConfig
|
||||||
|
|
||||||
|
let statusParams = {
|
||||||
|
spoilerText: this.subject || '',
|
||||||
|
status: statusText,
|
||||||
|
nsfw: !!sensitiveByDefault,
|
||||||
|
files: [],
|
||||||
|
poll: {},
|
||||||
|
mediaDescriptions: {},
|
||||||
|
visibility: scope,
|
||||||
|
contentType
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.statusId) {
|
||||||
|
const statusContentType = this.statusContentType || contentType
|
||||||
|
statusParams = {
|
||||||
|
spoilerText: this.subject || '',
|
||||||
|
status: this.statusText || '',
|
||||||
|
nsfw: this.statusIsSensitive || !!sensitiveByDefault,
|
||||||
|
files: this.statusFiles || [],
|
||||||
|
poll: this.statusPoll || {},
|
||||||
|
mediaDescriptions: this.statusMediaDescriptions || {},
|
||||||
|
visibility: this.statusScope || scope,
|
||||||
|
contentType: statusContentType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dropFiles: [],
|
dropFiles: [],
|
||||||
uploadingFiles: false,
|
uploadingFiles: false,
|
||||||
error: null,
|
error: null,
|
||||||
posting: false,
|
posting: false,
|
||||||
highlighted: 0,
|
highlighted: 0,
|
||||||
newStatus: {
|
newStatus: statusParams,
|
||||||
spoilerText: this.subject || '',
|
|
||||||
status: statusText,
|
|
||||||
nsfw: !!sensitiveByDefault,
|
|
||||||
files: [],
|
|
||||||
poll: {},
|
|
||||||
mediaDescriptions: {},
|
|
||||||
visibility: scope,
|
|
||||||
contentType
|
|
||||||
},
|
|
||||||
caret: 0,
|
caret: 0,
|
||||||
pollFormVisible: false,
|
pollFormVisible: false,
|
||||||
showDropIcon: 'hide',
|
showDropIcon: 'hide',
|
||||||
|
@ -236,6 +261,9 @@ const PostStatusForm = {
|
||||||
uploadFileLimitReached () {
|
uploadFileLimitReached () {
|
||||||
return this.newStatus.files.length >= this.fileLimit
|
return this.newStatus.files.length >= this.fileLimit
|
||||||
},
|
},
|
||||||
|
isEdit () {
|
||||||
|
return typeof this.statusId !== 'undefined' && this.statusId.trim() !== ''
|
||||||
|
},
|
||||||
...mapGetters(['mergedConfig']),
|
...mapGetters(['mergedConfig']),
|
||||||
...mapState({
|
...mapState({
|
||||||
mobileLayout: state => state.interface.mobileLayout
|
mobileLayout: state => state.interface.mobileLayout
|
||||||
|
|
|
@ -66,6 +66,13 @@
|
||||||
<span v-if="safeDMEnabled">{{ $t('post_status.direct_warning_to_first_only') }}</span>
|
<span v-if="safeDMEnabled">{{ $t('post_status.direct_warning_to_first_only') }}</span>
|
||||||
<span v-else>{{ $t('post_status.direct_warning_to_all') }}</span>
|
<span v-else>{{ $t('post_status.direct_warning_to_all') }}</span>
|
||||||
</p>
|
</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
|
<div
|
||||||
v-if="!disablePreview"
|
v-if="!disablePreview"
|
||||||
class="preview-heading faint"
|
class="preview-heading faint"
|
||||||
|
@ -170,6 +177,7 @@
|
||||||
class="visibility-tray"
|
class="visibility-tray"
|
||||||
>
|
>
|
||||||
<scope-selector
|
<scope-selector
|
||||||
|
v-if="!disableVisibilitySelector"
|
||||||
:show-all="showAllScopes"
|
:show-all="showAllScopes"
|
||||||
:user-default="userDefaultScope"
|
:user-default="userDefaultScope"
|
||||||
:original-scope="copyMessageScope"
|
:original-scope="copyMessageScope"
|
||||||
|
@ -410,6 +418,16 @@
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.visibility-notice.edit-warning {
|
||||||
|
> :first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> :last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.media-upload-icon, .poll-icon, .emoji-icon {
|
.media-upload-icon, .poll-icon, .emoji-icon {
|
||||||
font-size: 1.85em;
|
font-size: 1.85em;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
|
|
|
@ -395,6 +395,12 @@ const Status = {
|
||||||
},
|
},
|
||||||
visibilityLocalized () {
|
visibilityLocalized () {
|
||||||
return this.$i18n.t('general.scope_in_timeline.' + this.status.visibility)
|
return this.$i18n.t('general.scope_in_timeline.' + this.status.visibility)
|
||||||
|
},
|
||||||
|
isEdited () {
|
||||||
|
return this.status.edited_at !== null
|
||||||
|
},
|
||||||
|
editingAvailable () {
|
||||||
|
return this.$store.state.instance.editingAvailable
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -156,7 +156,8 @@
|
||||||
margin-right: 0.2em;
|
margin-right: 0.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
& .heading-reply-row {
|
& .heading-reply-row,
|
||||||
|
& .heading-edited-row {
|
||||||
position: relative;
|
position: relative;
|
||||||
align-content: baseline;
|
align-content: baseline;
|
||||||
font-size: 0.85em;
|
font-size: 0.85em;
|
||||||
|
|
|
@ -327,6 +327,24 @@
|
||||||
class="mentions-line"
|
class="mentions-line"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="isEdited && editingAvailable && !isPreview"
|
||||||
|
class="heading-edited-row"
|
||||||
|
>
|
||||||
|
<i18n-t
|
||||||
|
keypath="status.edited_at"
|
||||||
|
tag="span"
|
||||||
|
>
|
||||||
|
<template #time>
|
||||||
|
<Timeago
|
||||||
|
template-key="time.in_past"
|
||||||
|
:time="status.edited_at"
|
||||||
|
:auto-update="60"
|
||||||
|
:long-format="true"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<StatusContent
|
<StatusContent
|
||||||
|
|
|
@ -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
|
|
@ -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"
|
||||||
|
:is-preview="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>
|
|
@ -3,7 +3,7 @@
|
||||||
:datetime="time"
|
:datetime="time"
|
||||||
:title="localeDateString"
|
:title="localeDateString"
|
||||||
>
|
>
|
||||||
{{ $tc(relativeTime.key, relativeTime.num, [relativeTime.num]) }}
|
{{ relativeTimeString }}
|
||||||
</time>
|
</time>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import localeService from 'src/services/locale/locale.service.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Timeago',
|
name: 'Timeago',
|
||||||
props: ['time', 'autoUpdate', 'longFormat', 'nowThreshold'],
|
props: ['time', 'autoUpdate', 'longFormat', 'nowThreshold', 'templateKey'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
relativeTime: { key: 'time.now', num: 0 },
|
relativeTime: { key: 'time.now', num: 0 },
|
||||||
|
@ -26,6 +26,23 @@ export default {
|
||||||
return typeof this.time === 'string'
|
return typeof this.time === 'string'
|
||||||
? new Date(Date.parse(this.time)).toLocaleString(browserLocale)
|
? new Date(Date.parse(this.time)).toLocaleString(browserLocale)
|
||||||
: this.time.toLocaleString(browserLocale)
|
: this.time.toLocaleString(browserLocale)
|
||||||
|
},
|
||||||
|
relativeTimeString () {
|
||||||
|
const timeString = this.$i18n.tc(this.relativeTime.key, this.relativeTime.num, [this.relativeTime.num])
|
||||||
|
|
||||||
|
if (typeof this.templateKey === 'string' && this.relativeTime.key !== 'time.now') {
|
||||||
|
return this.$i18n.t(this.templateKey, [timeString])
|
||||||
|
}
|
||||||
|
|
||||||
|
return timeString
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
time (newVal, oldVal) {
|
||||||
|
if (oldVal !== newVal) {
|
||||||
|
clearTimeout(this.interval)
|
||||||
|
this.refreshRelativeTimeObject()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
|
|
|
@ -214,6 +214,7 @@
|
||||||
"load_older": "Load older interactions"
|
"load_older": "Load older interactions"
|
||||||
},
|
},
|
||||||
"post_status": {
|
"post_status": {
|
||||||
|
"edit_status": "Edit status",
|
||||||
"new_status": "Post new status",
|
"new_status": "Post new status",
|
||||||
"account_not_locked_warning": "Your account is not {0}. Anyone can follow you to view your follower-only posts.",
|
"account_not_locked_warning": "Your account is not {0}. Anyone can follow you to view your follower-only posts.",
|
||||||
"account_not_locked_warning_link": "locked",
|
"account_not_locked_warning_link": "locked",
|
||||||
|
@ -229,6 +230,8 @@
|
||||||
"default": "Just landed in L.A.",
|
"default": "Just landed in L.A.",
|
||||||
"direct_warning_to_all": "This post will be visible to all the mentioned users.",
|
"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.",
|
"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",
|
"posting": "Posting",
|
||||||
"post": "Post",
|
"post": "Post",
|
||||||
"preview": "Preview",
|
"preview": "Preview",
|
||||||
|
@ -797,6 +800,8 @@
|
||||||
"favorites": "Favorites",
|
"favorites": "Favorites",
|
||||||
"repeats": "Repeats",
|
"repeats": "Repeats",
|
||||||
"delete": "Delete status",
|
"delete": "Delete status",
|
||||||
|
"edit": "Edit status",
|
||||||
|
"edited_at": "(last edited {time})",
|
||||||
"pin": "Pin on profile",
|
"pin": "Pin on profile",
|
||||||
"unpin": "Unpin from profile",
|
"unpin": "Unpin from profile",
|
||||||
"pinned": "Pinned",
|
"pinned": "Pinned",
|
||||||
|
@ -844,7 +849,8 @@
|
||||||
"ancestor_follow_with_icon": "{icon} {text}",
|
"ancestor_follow_with_icon": "{icon} {text}",
|
||||||
"show_all_conversation_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_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": {
|
"user_card": {
|
||||||
"approve": "Approve",
|
"approve": "Approve",
|
||||||
|
|
|
@ -20,6 +20,9 @@ import oauthTokensModule from './modules/oauth_tokens.js'
|
||||||
import reportsModule from './modules/reports.js'
|
import reportsModule from './modules/reports.js'
|
||||||
import pollsModule from './modules/polls.js'
|
import pollsModule from './modules/polls.js'
|
||||||
import postStatusModule from './modules/postStatus.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'
|
import chatsModule from './modules/chats.js'
|
||||||
|
|
||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
|
@ -86,6 +89,8 @@ const persistedStateOptions = {
|
||||||
reports: reportsModule,
|
reports: reportsModule,
|
||||||
polls: pollsModule,
|
polls: pollsModule,
|
||||||
postStatus: postStatusModule,
|
postStatus: postStatusModule,
|
||||||
|
editStatus: editStatusModule,
|
||||||
|
statusHistory: statusHistoryModule,
|
||||||
chats: chatsModule
|
chats: chatsModule
|
||||||
},
|
},
|
||||||
plugins,
|
plugins,
|
||||||
|
|
|
@ -103,6 +103,13 @@ const api = {
|
||||||
showImmediately: timelineData.visibleStatuses.length === 0,
|
showImmediately: timelineData.visibleStatuses.length === 0,
|
||||||
timeline: 'friends'
|
timeline: 'friends'
|
||||||
})
|
})
|
||||||
|
} else if (message.event === 'status.update') {
|
||||||
|
dispatch('addNewStatuses', {
|
||||||
|
statuses: [message.status],
|
||||||
|
userId: false,
|
||||||
|
showImmediately: message.status.id in timelineData.visibleStatusesObject,
|
||||||
|
timeline: 'friends'
|
||||||
|
})
|
||||||
} else if (message.event === 'delete') {
|
} else if (message.event === 'delete') {
|
||||||
dispatch('deleteStatusById', message.id)
|
dispatch('deleteStatusById', message.id)
|
||||||
} else if (message.event === 'pleroma:chat_update') {
|
} else if (message.event === 'pleroma:chat_update') {
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
const editStatus = {
|
||||||
|
state: {
|
||||||
|
params: null,
|
||||||
|
modalActivated: false
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
openEditStatusModal (state, params) {
|
||||||
|
state.params = params
|
||||||
|
state.modalActivated = true
|
||||||
|
},
|
||||||
|
closeEditStatusModal (state) {
|
||||||
|
state.modalActivated = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
openEditStatusModal ({ commit }, params) {
|
||||||
|
commit('openEditStatusModal', params)
|
||||||
|
},
|
||||||
|
closeEditStatusModal ({ commit }) {
|
||||||
|
commit('closeEditStatusModal')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default editStatus
|
|
@ -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
|
|
@ -249,6 +249,9 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
|
||||||
status: (status) => {
|
status: (status) => {
|
||||||
addStatus(status, showImmediately)
|
addStatus(status, showImmediately)
|
||||||
},
|
},
|
||||||
|
edit: (status) => {
|
||||||
|
addStatus(status, showImmediately)
|
||||||
|
},
|
||||||
retweet: (status) => {
|
retweet: (status) => {
|
||||||
// RetweetedStatuses are never shown immediately
|
// RetweetedStatuses are never shown immediately
|
||||||
const retweetedStatus = addStatus(status.retweeted_status, false, false)
|
const retweetedStatus = addStatus(status.retweeted_status, false, false)
|
||||||
|
@ -606,6 +609,12 @@ const statuses = {
|
||||||
return rootState.api.backendInteractor.fetchStatus({ id })
|
return rootState.api.backendInteractor.fetchStatus({ id })
|
||||||
.then((status) => dispatch('addNewStatuses', { statuses: [status] }))
|
.then((status) => dispatch('addNewStatuses', { statuses: [status] }))
|
||||||
},
|
},
|
||||||
|
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) {
|
deleteStatus ({ rootState, commit }, status) {
|
||||||
commit('setDeleted', { status })
|
commit('setDeleted', { status })
|
||||||
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
|
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { each, map, concat, last, get } from 'lodash'
|
import { each, map, concat, last, get } from 'lodash'
|
||||||
import { parseStatus, parseUser, parseNotification, parseAttachment, parseChat, parseLinkHeaderPagination } from '../entity_normalizer/entity_normalizer.service.js'
|
import { parseStatus, parseSource, parseUser, parseNotification, parseAttachment, parseChat, parseLinkHeaderPagination } from '../entity_normalizer/entity_normalizer.service.js'
|
||||||
import { RegistrationError, StatusCodeError } from '../errors/errors'
|
import { RegistrationError, StatusCodeError } from '../errors/errors'
|
||||||
|
|
||||||
/* eslint-env browser */
|
/* eslint-env browser */
|
||||||
|
@ -49,6 +49,8 @@ const MASTODON_PUBLIC_TIMELINE = '/api/v1/timelines/public'
|
||||||
const MASTODON_USER_HOME_TIMELINE_URL = '/api/v1/timelines/home'
|
const MASTODON_USER_HOME_TIMELINE_URL = '/api/v1/timelines/home'
|
||||||
const MASTODON_STATUS_URL = id => `/api/v1/statuses/${id}`
|
const MASTODON_STATUS_URL = id => `/api/v1/statuses/${id}`
|
||||||
const MASTODON_STATUS_CONTEXT_URL = id => `/api/v1/statuses/${id}/context`
|
const MASTODON_STATUS_CONTEXT_URL = id => `/api/v1/statuses/${id}/context`
|
||||||
|
const MASTODON_STATUS_SOURCE_URL = id => `/api/v1/statuses/${id}/source`
|
||||||
|
const MASTODON_STATUS_HISTORY_URL = id => `/api/v1/statuses/${id}/history`
|
||||||
const MASTODON_USER_URL = '/api/v1/accounts'
|
const MASTODON_USER_URL = '/api/v1/accounts'
|
||||||
const MASTODON_USER_LOOKUP_URL = '/api/v1/accounts/lookup'
|
const MASTODON_USER_LOOKUP_URL = '/api/v1/accounts/lookup'
|
||||||
const MASTODON_USER_RELATIONSHIPS_URL = '/api/v1/accounts/relationships'
|
const MASTODON_USER_RELATIONSHIPS_URL = '/api/v1/accounts/relationships'
|
||||||
|
@ -522,6 +524,31 @@ const fetchStatus = ({ id, credentials }) => {
|
||||||
.then((data) => parseStatus(data))
|
.then((data) => parseStatus(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchStatusSource = ({ id, credentials }) => {
|
||||||
|
const url = MASTODON_STATUS_SOURCE_URL(id)
|
||||||
|
return fetch(url, { headers: authHeaders(credentials) })
|
||||||
|
.then((data) => {
|
||||||
|
if (data.ok) {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
throw new Error('Error fetching source', data)
|
||||||
|
})
|
||||||
|
.then((data) => data.json())
|
||||||
|
.then((data) => parseSource(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchStatusHistory = ({ status, credentials }) => {
|
||||||
|
const url = MASTODON_STATUS_HISTORY_URL(status.id)
|
||||||
|
return promisedRequest({ url, credentials })
|
||||||
|
.then((data) => {
|
||||||
|
data.reverse()
|
||||||
|
return data.map((item) => {
|
||||||
|
item.originalStatus = status
|
||||||
|
return parseStatus(item)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const tagUser = ({ tag, credentials, user }) => {
|
const tagUser = ({ tag, credentials, user }) => {
|
||||||
const screenName = user.screen_name
|
const screenName = user.screen_name
|
||||||
const form = {
|
const form = {
|
||||||
|
@ -825,6 +852,54 @@ const postStatus = ({
|
||||||
.then((data) => data.error ? data : parseStatus(data))
|
.then((data) => data.error ? data : parseStatus(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const editStatus = ({
|
||||||
|
id,
|
||||||
|
credentials,
|
||||||
|
status,
|
||||||
|
spoilerText,
|
||||||
|
sensitive,
|
||||||
|
poll,
|
||||||
|
mediaIds = [],
|
||||||
|
contentType
|
||||||
|
}) => {
|
||||||
|
const form = new FormData()
|
||||||
|
const pollOptions = poll.options || []
|
||||||
|
|
||||||
|
form.append('status', status)
|
||||||
|
if (spoilerText) form.append('spoiler_text', spoilerText)
|
||||||
|
if (sensitive) form.append('sensitive', sensitive)
|
||||||
|
if (contentType) form.append('content_type', contentType)
|
||||||
|
mediaIds.forEach(val => {
|
||||||
|
form.append('media_ids[]', val)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (pollOptions.some(option => option !== '')) {
|
||||||
|
const normalizedPoll = {
|
||||||
|
expires_in: poll.expiresIn,
|
||||||
|
multiple: poll.multiple
|
||||||
|
}
|
||||||
|
Object.keys(normalizedPoll).forEach(key => {
|
||||||
|
form.append(`poll[${key}]`, normalizedPoll[key])
|
||||||
|
})
|
||||||
|
|
||||||
|
pollOptions.forEach(option => {
|
||||||
|
form.append('poll[options][]', option)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const putHeaders = authHeaders(credentials)
|
||||||
|
|
||||||
|
return fetch(MASTODON_STATUS_URL(id), {
|
||||||
|
body: form,
|
||||||
|
method: 'PUT',
|
||||||
|
headers: putHeaders
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
return response.json()
|
||||||
|
})
|
||||||
|
.then((data) => data.error ? data : parseStatus(data))
|
||||||
|
}
|
||||||
|
|
||||||
const deleteStatus = ({ id, credentials }) => {
|
const deleteStatus = ({ id, credentials }) => {
|
||||||
return fetch(MASTODON_DELETE_URL(id), {
|
return fetch(MASTODON_DELETE_URL(id), {
|
||||||
headers: authHeaders(credentials),
|
headers: authHeaders(credentials),
|
||||||
|
@ -1291,7 +1366,8 @@ const MASTODON_STREAMING_EVENTS = new Set([
|
||||||
'update',
|
'update',
|
||||||
'notification',
|
'notification',
|
||||||
'delete',
|
'delete',
|
||||||
'filters_changed'
|
'filters_changed',
|
||||||
|
'status.update'
|
||||||
])
|
])
|
||||||
|
|
||||||
const PLEROMA_STREAMING_EVENTS = new Set([
|
const PLEROMA_STREAMING_EVENTS = new Set([
|
||||||
|
@ -1363,6 +1439,8 @@ export const handleMastoWS = (wsEvent) => {
|
||||||
const data = payload ? JSON.parse(payload) : null
|
const data = payload ? JSON.parse(payload) : null
|
||||||
if (event === 'update') {
|
if (event === 'update') {
|
||||||
return { event, status: parseStatus(data) }
|
return { event, status: parseStatus(data) }
|
||||||
|
} else if (event === 'status.update') {
|
||||||
|
return { event, status: parseStatus(data) }
|
||||||
} else if (event === 'notification') {
|
} else if (event === 'notification') {
|
||||||
return { event, notification: parseNotification(data) }
|
return { event, notification: parseNotification(data) }
|
||||||
} else if (event === 'pleroma:chat_update') {
|
} else if (event === 'pleroma:chat_update') {
|
||||||
|
@ -1497,6 +1575,8 @@ const apiService = {
|
||||||
fetchPinnedStatuses,
|
fetchPinnedStatuses,
|
||||||
fetchConversation,
|
fetchConversation,
|
||||||
fetchStatus,
|
fetchStatus,
|
||||||
|
fetchStatusSource,
|
||||||
|
fetchStatusHistory,
|
||||||
fetchFriends,
|
fetchFriends,
|
||||||
exportFriends,
|
exportFriends,
|
||||||
fetchFollowers,
|
fetchFollowers,
|
||||||
|
@ -1518,6 +1598,7 @@ const apiService = {
|
||||||
bookmarkStatus,
|
bookmarkStatus,
|
||||||
unbookmarkStatus,
|
unbookmarkStatus,
|
||||||
postStatus,
|
postStatus,
|
||||||
|
editStatus,
|
||||||
deleteStatus,
|
deleteStatus,
|
||||||
uploadMedia,
|
uploadMedia,
|
||||||
setMediaDescription,
|
setMediaDescription,
|
||||||
|
|
|
@ -251,6 +251,16 @@ export const parseAttachment = (data) => {
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const parseSource = (data) => {
|
||||||
|
const output = {}
|
||||||
|
|
||||||
|
output.text = data.text
|
||||||
|
output.spoiler_text = data.spoiler_text
|
||||||
|
output.content_type = data.content_type
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
export const parseStatus = (data) => {
|
export const parseStatus = (data) => {
|
||||||
const output = {}
|
const output = {}
|
||||||
const masto = Object.prototype.hasOwnProperty.call(data, 'account')
|
const masto = Object.prototype.hasOwnProperty.call(data, 'account')
|
||||||
|
@ -272,6 +282,8 @@ export const parseStatus = (data) => {
|
||||||
|
|
||||||
output.tags = data.tags
|
output.tags = data.tags
|
||||||
|
|
||||||
|
output.edited_at = data.edited_at
|
||||||
|
|
||||||
if (data.pleroma) {
|
if (data.pleroma) {
|
||||||
const { pleroma } = data
|
const { pleroma } = data
|
||||||
output.text = pleroma.content ? data.pleroma.content['text/plain'] : data.content
|
output.text = pleroma.content ? data.pleroma.content['text/plain'] : data.content
|
||||||
|
@ -373,6 +385,10 @@ export const parseStatus = (data) => {
|
||||||
output.favoritedBy = []
|
output.favoritedBy = []
|
||||||
output.rebloggedBy = []
|
output.rebloggedBy = []
|
||||||
|
|
||||||
|
if (Object.prototype.hasOwnProperty.call(data, 'originalStatus')) {
|
||||||
|
Object.assign(output, data.originalStatus)
|
||||||
|
}
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,47 @@ const postStatus = ({
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const editStatus = ({
|
||||||
|
store,
|
||||||
|
statusId,
|
||||||
|
status,
|
||||||
|
spoilerText,
|
||||||
|
sensitive,
|
||||||
|
poll,
|
||||||
|
media = [],
|
||||||
|
contentType = 'text/plain'
|
||||||
|
}) => {
|
||||||
|
const mediaIds = map(media, 'id')
|
||||||
|
|
||||||
|
return apiService.editStatus({
|
||||||
|
id: statusId,
|
||||||
|
credentials: store.state.users.currentUser.credentials,
|
||||||
|
status,
|
||||||
|
spoilerText,
|
||||||
|
sensitive,
|
||||||
|
poll,
|
||||||
|
mediaIds,
|
||||||
|
contentType
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
if (!data.error) {
|
||||||
|
store.dispatch('addNewStatuses', {
|
||||||
|
statuses: [data],
|
||||||
|
timeline: 'friends',
|
||||||
|
showImmediately: true,
|
||||||
|
noIdUpdate: true // To prevent missing notices on next pull.
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error('Error editing status', err)
|
||||||
|
return {
|
||||||
|
error: err.message
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const uploadMedia = ({ store, formData }) => {
|
const uploadMedia = ({ store, formData }) => {
|
||||||
const credentials = store.state.users.currentUser.credentials
|
const credentials = store.state.users.currentUser.credentials
|
||||||
return apiService.uploadMedia({ credentials, formData })
|
return apiService.uploadMedia({ credentials, formData })
|
||||||
|
@ -59,6 +100,7 @@ const setMediaDescription = ({ store, id, description }) => {
|
||||||
|
|
||||||
const statusPosterService = {
|
const statusPosterService = {
|
||||||
postStatus,
|
postStatus,
|
||||||
|
editStatus,
|
||||||
uploadMedia,
|
uploadMedia,
|
||||||
setMediaDescription
|
setMediaDescription
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue