Resolve merge conflict,
Fix few issues (conversation page highlight, fetch conversation, ...)
This commit is contained in:
commit
6143b1ce0f
11 changed files with 69 additions and 60 deletions
|
@ -1,5 +1,4 @@
|
||||||
import Conversation from '../conversation/conversation.vue'
|
import Conversation from '../conversation/conversation.vue'
|
||||||
import { find } from 'lodash'
|
|
||||||
|
|
||||||
const conversationPage = {
|
const conversationPage = {
|
||||||
components: {
|
components: {
|
||||||
|
@ -8,8 +7,8 @@ const conversationPage = {
|
||||||
computed: {
|
computed: {
|
||||||
statusoid () {
|
statusoid () {
|
||||||
const id = this.$route.params.id
|
const id = this.$route.params.id
|
||||||
const statuses = this.$store.state.statuses.allStatuses
|
const statuses = this.$store.state.statuses.allStatusesObject
|
||||||
const status = find(statuses, {id})
|
const status = statuses[id]
|
||||||
|
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { reduce, filter, findIndex } from 'lodash'
|
import { reduce, filter, findIndex } from 'lodash'
|
||||||
|
import { set } from 'vue'
|
||||||
import Status from '../status/status.vue'
|
import Status from '../status/status.vue'
|
||||||
|
|
||||||
const sortById = (a, b) => {
|
const sortById = (a, b) => {
|
||||||
|
@ -26,7 +27,8 @@ const conversation = {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
highlight: null,
|
highlight: null,
|
||||||
expanded: false
|
expanded: false,
|
||||||
|
converationStatusIds: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: [
|
props: [
|
||||||
|
@ -38,6 +40,15 @@ const conversation = {
|
||||||
status () {
|
status () {
|
||||||
return this.statusoid
|
return this.statusoid
|
||||||
},
|
},
|
||||||
|
idsToShow () {
|
||||||
|
if (this.converationStatusIds.length > 0) {
|
||||||
|
return this.converationStatusIds
|
||||||
|
} else if (this.statusId) {
|
||||||
|
return [this.statusId]
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
},
|
||||||
statusId () {
|
statusId () {
|
||||||
if (this.statusoid.retweeted_status) {
|
if (this.statusoid.retweeted_status) {
|
||||||
return this.statusoid.retweeted_status.id
|
return this.statusoid.retweeted_status.id
|
||||||
|
@ -54,14 +65,17 @@ const conversation = {
|
||||||
return [this.status]
|
return [this.status]
|
||||||
}
|
}
|
||||||
|
|
||||||
const conversationId = this.status.statusnet_conversation_id
|
const statusesObject = this.$store.state.statuses.allStatusesObject
|
||||||
const statuses = this.$store.state.statuses.allStatuses
|
const conversation = this.idsToShow.reduce((acc, id) => {
|
||||||
const conversation = filter(statuses, { statusnet_conversation_id: conversationId })
|
acc.push(statusesObject[id])
|
||||||
|
return acc
|
||||||
|
}, [])
|
||||||
|
|
||||||
const statusIndex = findIndex(conversation, { id: this.statusId })
|
const statusIndex = findIndex(conversation, { id: this.statusId })
|
||||||
if (statusIndex !== -1) {
|
if (statusIndex !== -1) {
|
||||||
conversation[statusIndex] = this.status
|
conversation[statusIndex] = this.status
|
||||||
}
|
}
|
||||||
|
|
||||||
return sortAndFilterConversation(conversation)
|
return sortAndFilterConversation(conversation)
|
||||||
},
|
},
|
||||||
replies () {
|
replies () {
|
||||||
|
@ -99,9 +113,15 @@ const conversation = {
|
||||||
methods: {
|
methods: {
|
||||||
fetchConversation () {
|
fetchConversation () {
|
||||||
if (this.status) {
|
if (this.status) {
|
||||||
const conversationId = this.status.statusnet_conversation_id
|
this.$store.state.api.backendInteractor.fetchConversation({id: this.status.id})
|
||||||
this.$store.state.api.backendInteractor.fetchConversation({id: conversationId})
|
.then(({ancestors, descendants}) => {
|
||||||
.then((statuses) => this.$store.dispatch('addNewStatuses', { statuses }))
|
this.$store.dispatch('addNewStatuses', { statuses: ancestors })
|
||||||
|
this.$store.dispatch('addNewStatuses', { statuses: descendants })
|
||||||
|
set(this, 'converationStatusIds', [].concat(
|
||||||
|
ancestors.map(_ => _.id),
|
||||||
|
this.statusId,
|
||||||
|
descendants.map(_ => _.id)))
|
||||||
|
})
|
||||||
.then(() => this.setHighlight(this.statusId))
|
.then(() => this.setHighlight(this.statusId))
|
||||||
} else {
|
} else {
|
||||||
const id = this.$route.params.id
|
const id = this.$route.params.id
|
||||||
|
|
|
@ -20,7 +20,7 @@ const mediaUpload = {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('media', file)
|
formData.append('file', file)
|
||||||
|
|
||||||
self.$emit('uploading')
|
self.$emit('uploading')
|
||||||
self.uploading = true
|
self.uploading = true
|
||||||
|
|
|
@ -296,6 +296,8 @@ const PostStatusForm = {
|
||||||
},
|
},
|
||||||
paste (e) {
|
paste (e) {
|
||||||
if (e.clipboardData.files.length > 0) {
|
if (e.clipboardData.files.length > 0) {
|
||||||
|
// prevent pasting of file as text
|
||||||
|
e.preventDefault()
|
||||||
// Strangely, files property gets emptied after event propagation
|
// Strangely, files property gets emptied after event propagation
|
||||||
// Trying to wrap it in array doesn't work. Plus I doubt it's possible
|
// Trying to wrap it in array doesn't work. Plus I doubt it's possible
|
||||||
// to hold more than one file in clipboard.
|
// to hold more than one file in clipboard.
|
||||||
|
|
|
@ -84,10 +84,10 @@
|
||||||
<div class="media-upload-wrapper" v-for="file in newStatus.files">
|
<div class="media-upload-wrapper" v-for="file in newStatus.files">
|
||||||
<i class="fa button-icon icon-cancel" @click="removeMediaFile(file)"></i>
|
<i class="fa button-icon icon-cancel" @click="removeMediaFile(file)"></i>
|
||||||
<div class="media-upload-container attachment">
|
<div class="media-upload-container attachment">
|
||||||
<img class="thumbnail media-upload" :src="file.image" v-if="type(file) === 'image'"></img>
|
<img class="thumbnail media-upload" :src="file.url" v-if="type(file) === 'image'"></img>
|
||||||
<video v-if="type(file) === 'video'" :src="file.image" controls></video>
|
<video v-if="type(file) === 'video'" :src="file.url" controls></video>
|
||||||
<audio v-if="type(file) === 'audio'" :src="file.image" controls></audio>
|
<audio v-if="type(file) === 'audio'" :src="file.url" controls></audio>
|
||||||
<a v-if="type(file) === 'unknown'" :href="file.image">{{file.url}}</a>
|
<a v-if="type(file) === 'unknown'" :href="file.url">{{file.url}}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -287,8 +287,6 @@
|
||||||
img {
|
img {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
border-radius: $fallback--avatarRadius;
|
|
||||||
border-radius: var(--avatarRadius, $fallback--avatarRadius);
|
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,9 @@ export default function createPersistedState ({
|
||||||
merge({}, store.state, savedState)
|
merge({}, store.state, savedState)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (store.state.oauth.token) {
|
||||||
|
store.dispatch('loginUser', store.state.oauth.token)
|
||||||
|
}
|
||||||
loaded = true
|
loaded = true
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Couldn't load state")
|
console.log("Couldn't load state")
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { remove, slice, each, find, maxBy, minBy, merge, first, last, isArray, omitBy } from 'lodash'
|
import { remove, slice, each, find, maxBy, minBy, merge, first, last, isArray, omitBy } from 'lodash'
|
||||||
|
import { set } from 'vue'
|
||||||
import apiService from '../services/api/api.service.js'
|
import apiService from '../services/api/api.service.js'
|
||||||
// import parse from '../services/status_parser/status_parser.js'
|
// import parse from '../services/status_parser/status_parser.js'
|
||||||
|
|
||||||
|
@ -82,7 +83,7 @@ const mergeOrAdd = (arr, obj, item) => {
|
||||||
// This is a new item, prepare it
|
// This is a new item, prepare it
|
||||||
prepareStatus(item)
|
prepareStatus(item)
|
||||||
arr.push(item)
|
arr.push(item)
|
||||||
obj[item.id] = item
|
set(obj, item.id, item)
|
||||||
return {item, new: true}
|
return {item, new: true}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,9 @@ export const mergeOrAdd = (arr, obj, item) => {
|
||||||
} else {
|
} else {
|
||||||
// This is a new item, prepare it
|
// This is a new item, prepare it
|
||||||
arr.push(item)
|
arr.push(item)
|
||||||
obj[item.id] = item
|
set(obj, item.id, item)
|
||||||
if (item.screen_name && !item.screen_name.includes('@')) {
|
if (item.screen_name && !item.screen_name.includes('@')) {
|
||||||
obj[item.screen_name.toLowerCase()] = item
|
set(obj, item.screen_name.toLowerCase(), item)
|
||||||
}
|
}
|
||||||
return { item, new: true }
|
return { item, new: true }
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,7 @@ const FAVORITE_URL = '/api/favorites/create'
|
||||||
const UNFAVORITE_URL = '/api/favorites/destroy'
|
const UNFAVORITE_URL = '/api/favorites/destroy'
|
||||||
const RETWEET_URL = '/api/statuses/retweet'
|
const RETWEET_URL = '/api/statuses/retweet'
|
||||||
const UNRETWEET_URL = '/api/statuses/unretweet'
|
const UNRETWEET_URL = '/api/statuses/unretweet'
|
||||||
const STATUS_UPDATE_URL = '/api/statuses/update.json'
|
|
||||||
const STATUS_DELETE_URL = '/api/statuses/destroy'
|
const STATUS_DELETE_URL = '/api/statuses/destroy'
|
||||||
const STATUS_URL = '/api/statuses/show'
|
|
||||||
const MEDIA_UPLOAD_URL = '/api/statusnet/media/upload'
|
|
||||||
const CONVERSATION_URL = '/api/statusnet/conversation'
|
|
||||||
const MENTIONS_URL = '/api/statuses/mentions.json'
|
const MENTIONS_URL = '/api/statuses/mentions.json'
|
||||||
const DM_TIMELINE_URL = '/api/statuses/dm_timeline.json'
|
const DM_TIMELINE_URL = '/api/statuses/dm_timeline.json'
|
||||||
const FOLLOWERS_URL = '/api/statuses/followers.json'
|
const FOLLOWERS_URL = '/api/statuses/followers.json'
|
||||||
|
@ -37,6 +33,8 @@ const DENY_USER_URL = '/api/pleroma/friendships/deny'
|
||||||
const SUGGESTIONS_URL = '/api/v1/suggestions'
|
const SUGGESTIONS_URL = '/api/v1/suggestions'
|
||||||
|
|
||||||
const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites'
|
const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites'
|
||||||
|
const MASTODON_STATUS_URL = id => `/api/v1/statuses/${id}`
|
||||||
|
const MASTODON_STATUS_CONTEXT_URL = id => `/api/v1/statuses/${id}/context`
|
||||||
const MASTODON_USER_URL = '/api/v1/accounts'
|
const MASTODON_USER_URL = '/api/v1/accounts'
|
||||||
const MASTODON_USER_RELATIONSHIPS_URL = '/api/v1/accounts/relationships'
|
const MASTODON_USER_RELATIONSHIPS_URL = '/api/v1/accounts/relationships'
|
||||||
const MASTODON_USER_TIMELINE_URL = id => `/api/v1/accounts/${id}/statuses`
|
const MASTODON_USER_TIMELINE_URL = id => `/api/v1/accounts/${id}/statuses`
|
||||||
|
@ -46,9 +44,11 @@ const MASTODON_BLOCK_USER_URL = id => `/api/v1/accounts/${id}/block`
|
||||||
const MASTODON_UNBLOCK_USER_URL = id => `/api/v1/accounts/${id}/unblock`
|
const MASTODON_UNBLOCK_USER_URL = id => `/api/v1/accounts/${id}/unblock`
|
||||||
const MASTODON_MUTE_USER_URL = id => `/api/v1/accounts/${id}/mute`
|
const MASTODON_MUTE_USER_URL = id => `/api/v1/accounts/${id}/mute`
|
||||||
const MASTODON_UNMUTE_USER_URL = id => `/api/v1/accounts/${id}/unmute`
|
const MASTODON_UNMUTE_USER_URL = id => `/api/v1/accounts/${id}/unmute`
|
||||||
|
const MASTODON_POST_STATUS_URL = '/api/v1/statuses'
|
||||||
|
const MASTODON_MEDIA_UPLOAD_URL = '/api/v1/media'
|
||||||
|
|
||||||
import { each, map } from 'lodash'
|
import { each, map } from 'lodash'
|
||||||
import { parseStatus, parseUser, parseNotification } from '../entity_normalizer/entity_normalizer.service.js'
|
import { parseStatus, parseUser, parseNotification, parseAttachment } from '../entity_normalizer/entity_normalizer.service.js'
|
||||||
import 'whatwg-fetch'
|
import 'whatwg-fetch'
|
||||||
import { StatusCodeError } from '../errors/errors'
|
import { StatusCodeError } from '../errors/errors'
|
||||||
|
|
||||||
|
@ -317,8 +317,8 @@ const fetchFollowRequests = ({credentials}) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchConversation = ({id, credentials}) => {
|
const fetchConversation = ({id, credentials}) => {
|
||||||
let url = `${CONVERSATION_URL}/${id}.json?count=100`
|
let urlContext = MASTODON_STATUS_CONTEXT_URL(id)
|
||||||
return fetch(url, { headers: authHeaders(credentials) })
|
return fetch(urlContext, { headers: authHeaders(credentials) })
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.ok) {
|
if (data.ok) {
|
||||||
return data
|
return data
|
||||||
|
@ -326,11 +326,14 @@ const fetchConversation = ({id, credentials}) => {
|
||||||
throw new Error('Error fetching timeline', data)
|
throw new Error('Error fetching timeline', data)
|
||||||
})
|
})
|
||||||
.then((data) => data.json())
|
.then((data) => data.json())
|
||||||
.then((data) => data.map(parseStatus))
|
.then(({ancestors, descendants}) => ({
|
||||||
|
ancestors: ancestors.map(parseStatus),
|
||||||
|
descendants: descendants.map(parseStatus)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchStatus = ({id, credentials}) => {
|
const fetchStatus = ({id, credentials}) => {
|
||||||
let url = `${STATUS_URL}/${id}.json`
|
let url = MASTODON_STATUS_URL(id)
|
||||||
return fetch(url, { headers: authHeaders(credentials) })
|
return fetch(url, { headers: authHeaders(credentials) })
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.ok) {
|
if (data.ok) {
|
||||||
|
@ -439,23 +442,23 @@ const unretweet = ({ id, credentials }) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const postStatus = ({credentials, status, spoilerText, visibility, sensitive, mediaIds, inReplyToStatusId, contentType, noAttachmentLinks}) => {
|
const postStatus = ({credentials, status, spoilerText, visibility, sensitive, mediaIds = [], inReplyToStatusId, contentType}) => {
|
||||||
const idsText = mediaIds.join(',')
|
|
||||||
const form = new FormData()
|
const form = new FormData()
|
||||||
|
|
||||||
form.append('status', status)
|
form.append('status', status)
|
||||||
form.append('source', 'Pleroma FE')
|
form.append('source', 'Pleroma FE')
|
||||||
if (noAttachmentLinks) form.append('no_attachment_links', noAttachmentLinks)
|
|
||||||
if (spoilerText) form.append('spoiler_text', spoilerText)
|
if (spoilerText) form.append('spoiler_text', spoilerText)
|
||||||
if (visibility) form.append('visibility', visibility)
|
if (visibility) form.append('visibility', visibility)
|
||||||
if (sensitive) form.append('sensitive', sensitive)
|
if (sensitive) form.append('sensitive', sensitive)
|
||||||
if (contentType) form.append('content_type', contentType)
|
if (contentType) form.append('content_type', contentType)
|
||||||
form.append('media_ids', idsText)
|
mediaIds.forEach(val => {
|
||||||
|
form.append('media_ids[]', val)
|
||||||
|
})
|
||||||
if (inReplyToStatusId) {
|
if (inReplyToStatusId) {
|
||||||
form.append('in_reply_to_status_id', inReplyToStatusId)
|
form.append('in_reply_to_id', inReplyToStatusId)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetch(STATUS_UPDATE_URL, {
|
return fetch(MASTODON_POST_STATUS_URL, {
|
||||||
body: form,
|
body: form,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: authHeaders(credentials)
|
headers: authHeaders(credentials)
|
||||||
|
@ -480,13 +483,13 @@ const deleteStatus = ({ id, credentials }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadMedia = ({formData, credentials}) => {
|
const uploadMedia = ({formData, credentials}) => {
|
||||||
return fetch(MEDIA_UPLOAD_URL, {
|
return fetch(MASTODON_MEDIA_UPLOAD_URL, {
|
||||||
body: formData,
|
body: formData,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: authHeaders(credentials)
|
headers: authHeaders(credentials)
|
||||||
})
|
})
|
||||||
.then((response) => response.text())
|
.then((data) => data.json())
|
||||||
.then((text) => (new DOMParser()).parseFromString(text, 'application/xml'))
|
.then((data) => parseAttachment(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
const followImport = ({params, credentials}) => {
|
const followImport = ({params, credentials}) => {
|
||||||
|
|
|
@ -128,14 +128,15 @@ export const parseUser = (data) => {
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseAttachment = (data) => {
|
export const parseAttachment = (data) => {
|
||||||
const output = {}
|
const output = {}
|
||||||
const masto = !data.hasOwnProperty('oembed')
|
const masto = !data.hasOwnProperty('oembed')
|
||||||
|
|
||||||
if (masto) {
|
if (masto) {
|
||||||
// Not exactly same...
|
// Not exactly same...
|
||||||
output.mimetype = data.type
|
output.mimetype = data.pleroma ? data.pleroma.mime_type : data.type
|
||||||
output.meta = data.meta // not present in BE yet
|
output.meta = data.meta // not present in BE yet
|
||||||
|
output.id = data.id
|
||||||
} else {
|
} else {
|
||||||
output.mimetype = data.mimetype
|
output.mimetype = data.mimetype
|
||||||
// output.meta = ??? missing
|
// output.meta = ??? missing
|
||||||
|
|
|
@ -4,7 +4,7 @@ import apiService from '../api/api.service.js'
|
||||||
const postStatus = ({ store, status, spoilerText, visibility, sensitive, media = [], inReplyToStatusId = undefined, contentType = 'text/plain' }) => {
|
const postStatus = ({ store, status, spoilerText, visibility, sensitive, media = [], inReplyToStatusId = undefined, contentType = 'text/plain' }) => {
|
||||||
const mediaIds = map(media, 'id')
|
const mediaIds = map(media, 'id')
|
||||||
|
|
||||||
return apiService.postStatus({credentials: store.state.users.currentUser.credentials, status, spoilerText, visibility, sensitive, mediaIds, inReplyToStatusId, contentType, noAttachmentLinks: store.state.instance.noAttachmentLinks})
|
return apiService.postStatus({credentials: store.state.users.currentUser.credentials, status, spoilerText, visibility, sensitive, mediaIds, inReplyToStatusId, contentType})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (!data.error) {
|
if (!data.error) {
|
||||||
store.dispatch('addNewStatuses', {
|
store.dispatch('addNewStatuses', {
|
||||||
|
@ -26,25 +26,7 @@ const postStatus = ({ store, status, spoilerText, visibility, sensitive, media =
|
||||||
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 }).then((xml) => {
|
return apiService.uploadMedia({ credentials, formData })
|
||||||
// Firefox and Chrome treat method differently...
|
|
||||||
let link = xml.getElementsByTagName('link')
|
|
||||||
|
|
||||||
if (link.length === 0) {
|
|
||||||
link = xml.getElementsByTagName('atom:link')
|
|
||||||
}
|
|
||||||
|
|
||||||
link = link[0]
|
|
||||||
|
|
||||||
const mediaData = {
|
|
||||||
id: xml.getElementsByTagName('media_id')[0].textContent,
|
|
||||||
url: xml.getElementsByTagName('media_url')[0].textContent,
|
|
||||||
image: link.getAttribute('href'),
|
|
||||||
mimetype: link.getAttribute('type')
|
|
||||||
}
|
|
||||||
|
|
||||||
return mediaData
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusPosterService = {
|
const statusPosterService = {
|
||||||
|
|
Loading…
Reference in a new issue