Merge branch 'issue-433-status-reply-form' into 'develop'
Issue 433 status reply form Closes #433 See merge request pleroma/pleroma-fe!674
This commit is contained in:
		
						commit
						7e8c2acc98
					
				
					 9 changed files with 102 additions and 73 deletions
				
			
		| 
						 | 
				
			
			@ -1,5 +1,9 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <conversation :collapsable="false" :statusoid="statusoid"></conversation>
 | 
			
		||||
  <conversation
 | 
			
		||||
    :collapsable="false"
 | 
			
		||||
    isPage="true"
 | 
			
		||||
    :statusoid="statusoid"
 | 
			
		||||
  ></conversation>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script src="./conversation-page.js"></script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,12 @@
 | 
			
		|||
import { reduce, filter } from 'lodash'
 | 
			
		||||
import { reduce, filter, findIndex } from 'lodash'
 | 
			
		||||
import { set } from 'vue'
 | 
			
		||||
import Status from '../status/status.vue'
 | 
			
		||||
 | 
			
		||||
const sortById = (a, b) => {
 | 
			
		||||
  const seqA = Number(a.id)
 | 
			
		||||
  const seqB = Number(b.id)
 | 
			
		||||
  const idA = a.type === 'retweet' ? a.retweeted_status.id : a.id
 | 
			
		||||
  const idB = b.type === 'retweet' ? b.retweeted_status.id : b.id
 | 
			
		||||
  const seqA = Number(idA)
 | 
			
		||||
  const seqB = Number(idB)
 | 
			
		||||
  const isSeqA = !Number.isNaN(seqA)
 | 
			
		||||
  const isSeqB = !Number.isNaN(seqB)
 | 
			
		||||
  if (isSeqA && isSeqB) {
 | 
			
		||||
| 
						 | 
				
			
			@ -14,12 +16,19 @@ const sortById = (a, b) => {
 | 
			
		|||
  } else if (!isSeqA && isSeqB) {
 | 
			
		||||
    return 1
 | 
			
		||||
  } else {
 | 
			
		||||
    return a.id < b.id ? -1 : 1
 | 
			
		||||
    return idA < idB ? -1 : 1
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const sortAndFilterConversation = (conversation) => {
 | 
			
		||||
const sortAndFilterConversation = (conversation, statusoid) => {
 | 
			
		||||
  if (statusoid.type === 'retweet') {
 | 
			
		||||
    conversation = filter(
 | 
			
		||||
      conversation,
 | 
			
		||||
      (status) => (status.type === 'retweet' || status.id !== statusoid.retweeted_status.id)
 | 
			
		||||
    )
 | 
			
		||||
  } else {
 | 
			
		||||
    conversation = filter(conversation, (status) => status.type !== 'retweet')
 | 
			
		||||
  }
 | 
			
		||||
  return conversation.filter(_ => _).sort(sortById)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -27,13 +36,20 @@ const conversation = {
 | 
			
		|||
  data () {
 | 
			
		||||
    return {
 | 
			
		||||
      highlight: null,
 | 
			
		||||
      expanded: false,
 | 
			
		||||
      converationStatusIds: []
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  props: [
 | 
			
		||||
    'statusoid',
 | 
			
		||||
    'collapsable'
 | 
			
		||||
    'collapsable',
 | 
			
		||||
    'isPage'
 | 
			
		||||
  ],
 | 
			
		||||
  created () {
 | 
			
		||||
    if (this.isPage) {
 | 
			
		||||
      this.fetchConversation()
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    status () {
 | 
			
		||||
      return this.statusoid
 | 
			
		||||
| 
						 | 
				
			
			@ -59,12 +75,22 @@ const conversation = {
 | 
			
		|||
        return []
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!this.isExpanded) {
 | 
			
		||||
        return [this.status]
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const statusesObject = this.$store.state.statuses.allStatusesObject
 | 
			
		||||
      const conversation = this.idsToShow.reduce((acc, id) => {
 | 
			
		||||
        acc.push(statusesObject[id])
 | 
			
		||||
        return acc
 | 
			
		||||
      }, [])
 | 
			
		||||
      return sortAndFilterConversation(conversation)
 | 
			
		||||
 | 
			
		||||
      const statusIndex = findIndex(conversation, { id: this.statusId })
 | 
			
		||||
      if (statusIndex !== -1) {
 | 
			
		||||
        conversation[statusIndex] = this.status
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return sortAndFilterConversation(conversation, this.status)
 | 
			
		||||
    },
 | 
			
		||||
    replies () {
 | 
			
		||||
      let i = 1
 | 
			
		||||
| 
						 | 
				
			
			@ -82,16 +108,21 @@ const conversation = {
 | 
			
		|||
        i++
 | 
			
		||||
        return result
 | 
			
		||||
      }, {})
 | 
			
		||||
    },
 | 
			
		||||
    isExpanded () {
 | 
			
		||||
      return this.expanded || this.isPage
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  components: {
 | 
			
		||||
    Status
 | 
			
		||||
  },
 | 
			
		||||
  created () {
 | 
			
		||||
    this.fetchConversation()
 | 
			
		||||
  },
 | 
			
		||||
  watch: {
 | 
			
		||||
    '$route': 'fetchConversation'
 | 
			
		||||
    '$route': 'fetchConversation',
 | 
			
		||||
    expanded (value) {
 | 
			
		||||
      if (value) {
 | 
			
		||||
        this.fetchConversation()
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    fetchConversation () {
 | 
			
		||||
| 
						 | 
				
			
			@ -117,10 +148,19 @@ const conversation = {
 | 
			
		|||
      return this.replies[id] || []
 | 
			
		||||
    },
 | 
			
		||||
    focused (id) {
 | 
			
		||||
      return id === this.statusId
 | 
			
		||||
      return (this.isExpanded) && id === this.status.id
 | 
			
		||||
    },
 | 
			
		||||
    setHighlight (id) {
 | 
			
		||||
      this.highlight = id
 | 
			
		||||
    },
 | 
			
		||||
    getHighlight () {
 | 
			
		||||
      return this.isExpanded ? this.highlight : null
 | 
			
		||||
    },
 | 
			
		||||
    toggleExpanded () {
 | 
			
		||||
      this.expanded = !this.expanded
 | 
			
		||||
      if (!this.expanded) {
 | 
			
		||||
        this.setHighlight(null)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,26 +1,42 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <div class="timeline panel panel-default">
 | 
			
		||||
    <div class="panel-heading conversation-heading">
 | 
			
		||||
  <div class="timeline panel-default" :class="[isExpanded ? 'panel' : 'panel-disabled']">
 | 
			
		||||
    <div v-if="isExpanded" class="panel-heading conversation-heading">
 | 
			
		||||
      <span class="title"> {{ $t('timeline.conversation') }} </span>
 | 
			
		||||
      <span v-if="collapsable">
 | 
			
		||||
        <a href="#" @click.prevent="$emit('toggleExpanded')">{{ $t('timeline.collapse') }}</a>
 | 
			
		||||
        <a href="#" @click.prevent="toggleExpanded">{{ $t('timeline.collapse') }}</a>
 | 
			
		||||
      </span>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="panel-body">
 | 
			
		||||
      <div class="timeline">
 | 
			
		||||
    <status
 | 
			
		||||
      v-for="status in conversation"
 | 
			
		||||
          @goto="setHighlight" :key="status.id"
 | 
			
		||||
          :inlineExpanded="collapsable" :statusoid="status"
 | 
			
		||||
          :expandable='false' :focused="focused(status.id)"
 | 
			
		||||
          :inConversation='true'
 | 
			
		||||
          :highlight="highlight"
 | 
			
		||||
      @goto="setHighlight"
 | 
			
		||||
      @toggleExpanded="toggleExpanded"
 | 
			
		||||
      :key="status.id"
 | 
			
		||||
      :inlineExpanded="collapsable"
 | 
			
		||||
      :statusoid="status"
 | 
			
		||||
      :expandable='!expanded'
 | 
			
		||||
      :focused="focused(status.id)"
 | 
			
		||||
      :inConversation="isExpanded"
 | 
			
		||||
      :highlight="getHighlight()"
 | 
			
		||||
      :replies="getReplies(status.id)"
 | 
			
		||||
          class="status-fadein">
 | 
			
		||||
        </status>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
      class="status-fadein panel-body"
 | 
			
		||||
    />
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script src="./conversation.js"></script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss">
 | 
			
		||||
@import '../../_variables.scss';
 | 
			
		||||
 | 
			
		||||
.timeline {
 | 
			
		||||
  .panel-disabled {
 | 
			
		||||
    .status-el {
 | 
			
		||||
      border-left: none;
 | 
			
		||||
      border-bottom-width: 1px;
 | 
			
		||||
      border-bottom-style: solid;
 | 
			
		||||
      border-color: var(--border, $fallback--border);
 | 
			
		||||
      border-radius: 0;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -310,7 +310,6 @@ const Status = {
 | 
			
		|||
      this.replying = !this.replying
 | 
			
		||||
    },
 | 
			
		||||
    gotoOriginal (id) {
 | 
			
		||||
      // only handled by conversation, not status_or_conversation
 | 
			
		||||
      if (this.inConversation) {
 | 
			
		||||
        this.$emit('goto', id)
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,7 +12,7 @@
 | 
			
		|||
      </div>
 | 
			
		||||
    </template>
 | 
			
		||||
    <template v-else>
 | 
			
		||||
      <div v-if="retweet && !noHeading" :class="[repeaterClass, { highlighted: repeaterStyle }]" :style="[repeaterStyle]" class="media container retweet-info">
 | 
			
		||||
      <div v-if="retweet && !noHeading && !inConversation" :class="[repeaterClass, { highlighted: repeaterStyle }]" :style="[repeaterStyle]" class="media container retweet-info">
 | 
			
		||||
        <UserAvatar class="media-left" v-if="retweet" :betterShadow="betterShadow" :src="statusoid.user.profile_image_url_original"/>
 | 
			
		||||
        <div class="media-body faint">
 | 
			
		||||
          <span class="user-name">
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +24,7 @@
 | 
			
		|||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div :class="[userClass, { highlighted: userStyle, 'is-retweet': retweet }]" :style="[ userStyle ]" class="media status">
 | 
			
		||||
      <div :class="[userClass, { highlighted: userStyle, 'is-retweet': retweet && !inConversation }]" :style="[ userStyle ]" class="media status">
 | 
			
		||||
        <div v-if="!noHeading" class="media-left">
 | 
			
		||||
          <router-link :to="userProfileLink" @click.stop.prevent.capture.native="toggleUserExpanded">
 | 
			
		||||
            <UserAvatar :compact="compact" :betterShadow="betterShadow" :src="status.user.profile_image_url_original"/>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,22 +0,0 @@
 | 
			
		|||
import Status from '../status/status.vue'
 | 
			
		||||
import Conversation from '../conversation/conversation.vue'
 | 
			
		||||
 | 
			
		||||
const statusOrConversation = {
 | 
			
		||||
  props: ['statusoid'],
 | 
			
		||||
  data () {
 | 
			
		||||
    return {
 | 
			
		||||
      expanded: false
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  components: {
 | 
			
		||||
    Status,
 | 
			
		||||
    Conversation
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    toggleExpanded () {
 | 
			
		||||
      this.expanded = !this.expanded
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default statusOrConversation
 | 
			
		||||
| 
						 | 
				
			
			@ -1,14 +0,0 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <conversation v-if="expanded" @toggleExpanded="toggleExpanded" :collapsable="true" :statusoid="statusoid"></conversation>
 | 
			
		||||
    <status v-if="!expanded" @toggleExpanded="toggleExpanded" :expandable="true" :inConversation="false" :focused="false" :statusoid="statusoid"></status>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script src="./status_or_conversation.js"></script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss">
 | 
			
		||||
 .spacer {
 | 
			
		||||
   height: 1em
 | 
			
		||||
 }
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
import Status from '../status/status.vue'
 | 
			
		||||
import timelineFetcher from '../../services/timeline_fetcher/timeline_fetcher.service.js'
 | 
			
		||||
import StatusOrConversation from '../status_or_conversation/status_or_conversation.vue'
 | 
			
		||||
import Conversation from '../conversation/conversation.vue'
 | 
			
		||||
import { throttle } from 'lodash'
 | 
			
		||||
 | 
			
		||||
const Timeline = {
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +43,7 @@ const Timeline = {
 | 
			
		|||
  },
 | 
			
		||||
  components: {
 | 
			
		||||
    Status,
 | 
			
		||||
    StatusOrConversation
 | 
			
		||||
    Conversation
 | 
			
		||||
  },
 | 
			
		||||
  created () {
 | 
			
		||||
    const store = this.$store
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,7 +16,13 @@
 | 
			
		|||
    </div>
 | 
			
		||||
    <div :class="classes.body">
 | 
			
		||||
      <div class="timeline">
 | 
			
		||||
        <status-or-conversation v-for="status in timeline.visibleStatuses" :key="status.id" v-bind:statusoid="status" class="status-fadein"></status-or-conversation>
 | 
			
		||||
        <conversation 
 | 
			
		||||
          v-for="status in timeline.visibleStatuses"
 | 
			
		||||
          class="status-fadein"
 | 
			
		||||
          :key="status.id"
 | 
			
		||||
          :statusoid="status"
 | 
			
		||||
          :collapsable="true"
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div :class="classes.footer">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue