Merge branch 'develop' into 'themes-accent'
# Conflicts: # src/components/emoji_reactions/emoji_reactions.vue
This commit is contained in:
		
						commit
						84ebae8ed3
					
				
					 28 changed files with 352 additions and 55 deletions
				
			
		| 
						 | 
				
			
			@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 | 
			
		|||
- Pleroma AMOLED dark theme
 | 
			
		||||
- User level domain mutes, under User Settings -> Mutes
 | 
			
		||||
- Emoji reactions for statuses
 | 
			
		||||
- MRF keyword policy disclosure
 | 
			
		||||
### Changed
 | 
			
		||||
- theme engine update to 3 (themes v2.1 introduction)
 | 
			
		||||
- massive internal changes in theme engine - slowly away from "generate things separately with spaghetti code" towards "feed all data into single 'generateTheme' function and declare slot inheritance and all in a separate file"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -78,7 +78,7 @@ button {
 | 
			
		|||
  border-radius: $fallback--btnRadius;
 | 
			
		||||
  border-radius: var(--btnRadius, $fallback--btnRadius);
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  box-shadow: 0px 0px 2px 0px rgba(0, 0, 0, 1), 0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset, 0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
 | 
			
		||||
  box-shadow: $fallback--buttonShadow;
 | 
			
		||||
  box-shadow: var(--buttonShadow);
 | 
			
		||||
  font-size: 14px;
 | 
			
		||||
  font-family: sans-serif;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,3 +27,5 @@ $fallback--tooltipRadius: 5px;
 | 
			
		|||
$fallback--avatarRadius: 4px;
 | 
			
		||||
$fallback--avatarAltRadius: 10px;
 | 
			
		||||
$fallback--attachmentRadius: 10px;
 | 
			
		||||
 | 
			
		||||
$fallback--buttonShadow: 0px 0px 2px 0px rgba(0, 0, 0, 1), 0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset, 0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,17 +1,55 @@
 | 
			
		|||
import UserAvatar from '../user_avatar/user_avatar.vue'
 | 
			
		||||
 | 
			
		||||
const EMOJI_REACTION_COUNT_CUTOFF = 12
 | 
			
		||||
 | 
			
		||||
const EmojiReactions = {
 | 
			
		||||
  name: 'EmojiReactions',
 | 
			
		||||
  components: {
 | 
			
		||||
    UserAvatar
 | 
			
		||||
  },
 | 
			
		||||
  props: ['status'],
 | 
			
		||||
  data: () => ({
 | 
			
		||||
    showAll: false,
 | 
			
		||||
    popperOptions: {
 | 
			
		||||
      modifiers: {
 | 
			
		||||
        preventOverflow: { padding: { top: 50 }, boundariesElement: 'viewport' }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }),
 | 
			
		||||
  computed: {
 | 
			
		||||
    tooManyReactions () {
 | 
			
		||||
      return this.status.emoji_reactions.length > EMOJI_REACTION_COUNT_CUTOFF
 | 
			
		||||
    },
 | 
			
		||||
    emojiReactions () {
 | 
			
		||||
      return this.status.emoji_reactions
 | 
			
		||||
      return this.showAll
 | 
			
		||||
        ? this.status.emoji_reactions
 | 
			
		||||
        : this.status.emoji_reactions.slice(0, EMOJI_REACTION_COUNT_CUTOFF)
 | 
			
		||||
    },
 | 
			
		||||
    showMoreString () {
 | 
			
		||||
      return `+${this.status.emoji_reactions.length - EMOJI_REACTION_COUNT_CUTOFF}`
 | 
			
		||||
    },
 | 
			
		||||
    accountsForEmoji () {
 | 
			
		||||
      return this.status.emoji_reactions.reduce((acc, reaction) => {
 | 
			
		||||
        acc[reaction.name] = reaction.accounts || []
 | 
			
		||||
        return acc
 | 
			
		||||
      }, {})
 | 
			
		||||
    },
 | 
			
		||||
    loggedIn () {
 | 
			
		||||
      return !!this.$store.state.users.currentUser
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    toggleShowAll () {
 | 
			
		||||
      this.showAll = !this.showAll
 | 
			
		||||
    },
 | 
			
		||||
    reactedWith (emoji) {
 | 
			
		||||
      const user = this.$store.state.users.currentUser
 | 
			
		||||
      const reaction = this.status.emoji_reactions.find(r => r.emoji === emoji)
 | 
			
		||||
      return reaction.accounts && reaction.accounts.find(u => u.id === user.id)
 | 
			
		||||
      return this.status.emoji_reactions.find(r => r.name === emoji).me
 | 
			
		||||
    },
 | 
			
		||||
    fetchEmojiReactionsByIfMissing () {
 | 
			
		||||
      const hasNoAccounts = this.status.emoji_reactions.find(r => !r.accounts)
 | 
			
		||||
      if (hasNoAccounts) {
 | 
			
		||||
        this.$store.dispatch('fetchEmojiReactionsBy', this.status.id)
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    reactWith (emoji) {
 | 
			
		||||
      this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +58,8 @@ const EmojiReactions = {
 | 
			
		|||
      this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji })
 | 
			
		||||
    },
 | 
			
		||||
    emojiOnClick (emoji, event) {
 | 
			
		||||
      if (!this.loggedIn) return
 | 
			
		||||
 | 
			
		||||
      if (this.reactedWith(emoji)) {
 | 
			
		||||
        this.unreact(emoji)
 | 
			
		||||
      } else {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,16 +1,58 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <div class="emoji-reactions">
 | 
			
		||||
    <button
 | 
			
		||||
    <v-popover
 | 
			
		||||
      v-for="(reaction) in emojiReactions"
 | 
			
		||||
      :key="reaction.emoji"
 | 
			
		||||
      class="emoji-reaction btn btn-default"
 | 
			
		||||
      :class="{ 'toggled': reactedWith(reaction.emoji) }"
 | 
			
		||||
      @click="emojiOnClick(reaction.emoji, $event)"
 | 
			
		||||
      :key="reaction.name"
 | 
			
		||||
      :popper-options="popperOptions"
 | 
			
		||||
      trigger="hover"
 | 
			
		||||
      placement="top"
 | 
			
		||||
    >
 | 
			
		||||
      <span class="reaction-emoji">{{ reaction.emoji }}</span>
 | 
			
		||||
      <span>{{ reaction.count }}</span>
 | 
			
		||||
    </button>
 | 
			
		||||
 | 
			
		||||
      <div
 | 
			
		||||
        slot="popover"
 | 
			
		||||
        class="reacted-users"
 | 
			
		||||
      >
 | 
			
		||||
        <div v-if="accountsForEmoji[reaction.name].length">
 | 
			
		||||
          <div
 | 
			
		||||
            v-for="(account) in accountsForEmoji[reaction.name]"
 | 
			
		||||
            :key="account.id"
 | 
			
		||||
            class="reacted-user"
 | 
			
		||||
          >
 | 
			
		||||
            <UserAvatar
 | 
			
		||||
              :user="account"
 | 
			
		||||
              class="avatar-small"
 | 
			
		||||
              :compact="true"
 | 
			
		||||
            />
 | 
			
		||||
            <div class="reacted-user-names">
 | 
			
		||||
              <span class="reacted-user-name" v-html="account.name_html" />
 | 
			
		||||
              <span class="reacted-user-screen-name">{{ account.screen_name }}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div v-else>
 | 
			
		||||
          <i class="icon-spin4 animate-spin" />
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <button
 | 
			
		||||
        class="emoji-reaction btn btn-default"
 | 
			
		||||
        :class="{ 'picked-reaction': reactedWith(reaction.name), 'not-clickable': !loggedIn }"
 | 
			
		||||
        @click="emojiOnClick(reaction.name, $event)"
 | 
			
		||||
        @mouseenter="fetchEmojiReactionsByIfMissing()"
 | 
			
		||||
      >
 | 
			
		||||
        <span class="reaction-emoji">{{ reaction.name }}</span>
 | 
			
		||||
        <span>{{ reaction.count }}</span>
 | 
			
		||||
      </button>
 | 
			
		||||
    </v-popover>
 | 
			
		||||
    <a
 | 
			
		||||
        v-if="tooManyReactions"
 | 
			
		||||
        @click="toggleShowAll"
 | 
			
		||||
        class="emoji-reaction-expand faint"
 | 
			
		||||
        href='javascript:void(0)'
 | 
			
		||||
      >
 | 
			
		||||
        {{ showAll ? $t('general.show_less') : showMoreString }}
 | 
			
		||||
      </a>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script src="./emoji_reactions.js" ></script>
 | 
			
		||||
| 
						 | 
				
			
			@ -23,6 +65,31 @@
 | 
			
		|||
  flex-wrap: wrap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.reacted-users {
 | 
			
		||||
  padding: 0.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.reacted-user {
 | 
			
		||||
  padding: 0.25em;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
 | 
			
		||||
  .reacted-user-names {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    margin-left: 0.5em;
 | 
			
		||||
 | 
			
		||||
    img {
 | 
			
		||||
      width: 1em;
 | 
			
		||||
      height: 1em;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .reacted-user-screen-name {
 | 
			
		||||
    font-size: 9px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.emoji-reaction {
 | 
			
		||||
  padding: 0 0.5em;
 | 
			
		||||
  margin-right: 0.5em;
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +105,26 @@
 | 
			
		|||
  &:focus {
 | 
			
		||||
    outline: none;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &.not-clickable {
 | 
			
		||||
    cursor: default;
 | 
			
		||||
    &:hover {
 | 
			
		||||
      box-shadow: $fallback--buttonShadow;
 | 
			
		||||
      box-shadow: var(--buttonShadow);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.emoji-reaction-expand {
 | 
			
		||||
  padding: 0 0.5em;
 | 
			
		||||
  margin-right: 0.5em;
 | 
			
		||||
  margin-top: 0.5em;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  &:hover {
 | 
			
		||||
    text-decoration: underline;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,6 +10,7 @@ const tabModeDict = {
 | 
			
		|||
const Interactions = {
 | 
			
		||||
  data () {
 | 
			
		||||
    return {
 | 
			
		||||
      allowFollowingMove: this.$store.state.users.currentUser.allow_following_move,
 | 
			
		||||
      filterMode: tabModeDict['mentions']
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,6 +22,7 @@
 | 
			
		|||
        :label="$t('interactions.follows')"
 | 
			
		||||
      />
 | 
			
		||||
      <span
 | 
			
		||||
        v-if="!allowFollowingMove"
 | 
			
		||||
        key="moves"
 | 
			
		||||
        :label="$t('interactions.moves')"
 | 
			
		||||
      />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,10 @@ const MRFTransparencyPanel = {
 | 
			
		|||
      rejectInstances: state => get(state, 'instance.federationPolicy.mrf_simple.reject', []),
 | 
			
		||||
      ftlRemovalInstances: state => get(state, 'instance.federationPolicy.mrf_simple.federated_timeline_removal', []),
 | 
			
		||||
      mediaNsfwInstances: state => get(state, 'instance.federationPolicy.mrf_simple.media_nsfw', []),
 | 
			
		||||
      mediaRemovalInstances: state => get(state, 'instance.federationPolicy.mrf_simple.media_removal', [])
 | 
			
		||||
      mediaRemovalInstances: state => get(state, 'instance.federationPolicy.mrf_simple.media_removal', []),
 | 
			
		||||
      keywordsFtlRemoval: state => get(state, 'instance.federationPolicy.mrf_keyword.federated_timeline_removal', []),
 | 
			
		||||
      keywordsReject: state => get(state, 'instance.federationPolicy.mrf_keyword.reject', []),
 | 
			
		||||
      keywordsReplace: state => get(state, 'instance.federationPolicy.mrf_keyword.replace', [])
 | 
			
		||||
    }),
 | 
			
		||||
    hasInstanceSpecificPolicies () {
 | 
			
		||||
      return this.quarantineInstances.length ||
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +23,11 @@ const MRFTransparencyPanel = {
 | 
			
		|||
        this.ftlRemovalInstances.length ||
 | 
			
		||||
        this.mediaNsfwInstances.length ||
 | 
			
		||||
        this.mediaRemovalInstances.length
 | 
			
		||||
    },
 | 
			
		||||
    hasKeywordPolicies () {
 | 
			
		||||
      return this.keywordsFtlRemoval.length ||
 | 
			
		||||
        this.keywordsReject.length ||
 | 
			
		||||
        this.keywordsReplace.length
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -109,6 +109,49 @@
 | 
			
		|||
              />
 | 
			
		||||
            </ul>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <h2 v-if="hasKeywordPolicies">
 | 
			
		||||
            {{ $t("about.mrf.keyword.keyword_policies") }}
 | 
			
		||||
          </h2>
 | 
			
		||||
 | 
			
		||||
          <div v-if="keywordsFtlRemoval.length">
 | 
			
		||||
            <h4>{{ $t("about.mrf.keyword.ftl_removal") }}</h4>
 | 
			
		||||
 | 
			
		||||
            <ul>
 | 
			
		||||
              <li
 | 
			
		||||
                v-for="keyword in keywordsFtlRemoval"
 | 
			
		||||
                :key="keyword"
 | 
			
		||||
                v-text="keyword"
 | 
			
		||||
              />
 | 
			
		||||
            </ul>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div v-if="keywordsReject.length">
 | 
			
		||||
            <h4>{{ $t("about.mrf.keyword.reject") }}</h4>
 | 
			
		||||
 | 
			
		||||
            <ul>
 | 
			
		||||
              <li
 | 
			
		||||
                v-for="keyword in keywordsReject"
 | 
			
		||||
                :key="keyword"
 | 
			
		||||
                v-text="keyword"
 | 
			
		||||
              />
 | 
			
		||||
            </ul>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div v-if="keywordsReplace.length">
 | 
			
		||||
            <h4>{{ $t("about.mrf.keyword.replace") }}</h4>
 | 
			
		||||
 | 
			
		||||
            <ul>
 | 
			
		||||
              <li
 | 
			
		||||
                v-for="keyword in keywordsReplace"
 | 
			
		||||
                :key="keyword"
 | 
			
		||||
              >
 | 
			
		||||
                {{ keyword.pattern }}
 | 
			
		||||
                {{ $t("about.mrf.keyword.is_replaced_by") }}
 | 
			
		||||
                {{ keyword.replacement }}
 | 
			
		||||
              </li>
 | 
			
		||||
            </ul>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -78,6 +78,13 @@
 | 
			
		|||
              <i class="fa icon-arrow-curved lit" />
 | 
			
		||||
              <small>{{ $t('notifications.migrated_to') }}</small>
 | 
			
		||||
            </span>
 | 
			
		||||
            <span v-if="notification.type === 'pleroma:emoji_reaction'">
 | 
			
		||||
              <small>
 | 
			
		||||
                <i18n path="notifications.reacted_with">
 | 
			
		||||
                  <span class="emoji-reaction-emoji">{{ notification.emoji }}</span>
 | 
			
		||||
                </i18n>
 | 
			
		||||
              </small>
 | 
			
		||||
            </span>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div
 | 
			
		||||
            v-if="notification.type === 'follow' || notification.type === 'move'"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -97,6 +97,10 @@
 | 
			
		|||
    min-width: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .emoji-reaction-emoji {
 | 
			
		||||
    font-size: 16px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .notification-details {
 | 
			
		||||
    min-width: 0px;
 | 
			
		||||
    word-wrap: break-word;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,12 @@ const ReactButton = {
 | 
			
		|||
      this.showTooltip = false
 | 
			
		||||
    },
 | 
			
		||||
    addReaction (event, emoji) {
 | 
			
		||||
      this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
 | 
			
		||||
      const existingReaction = this.status.emoji_reactions.find(r => r.name === emoji)
 | 
			
		||||
      if (existingReaction && existingReaction.me) {
 | 
			
		||||
        this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji })
 | 
			
		||||
      } else {
 | 
			
		||||
        this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
 | 
			
		||||
      }
 | 
			
		||||
      this.closeReactionSelect()
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,6 +54,10 @@
 | 
			
		|||
 | 
			
		||||
.reaction-picker-filter {
 | 
			
		||||
  padding: 0.5em;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  input {
 | 
			
		||||
    flex: 1;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.reaction-picker-divider {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -92,6 +92,11 @@
 | 
			
		|||
                    {{ $t('settings.reply_link_preview') }}
 | 
			
		||||
                  </Checkbox>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li>
 | 
			
		||||
                  <Checkbox v-model="emojiReactionsOnTimeline">
 | 
			
		||||
                    {{ $t('settings.emoji_reactions_on_timeline') }}
 | 
			
		||||
                  </Checkbox>
 | 
			
		||||
                </li>
 | 
			
		||||
              </ul>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -328,6 +333,11 @@
 | 
			
		|||
                      {{ $t('settings.notification_visibility_moves') }}
 | 
			
		||||
                    </Checkbox>
 | 
			
		||||
                  </li>
 | 
			
		||||
                  <li>
 | 
			
		||||
                    <Checkbox v-model="notificationVisibility.emojiReactions">
 | 
			
		||||
                      {{ $t('settings.notification_visibility_emoji_reactions') }}
 | 
			
		||||
                    </Checkbox>
 | 
			
		||||
                  </li>
 | 
			
		||||
                </ul>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -256,6 +256,16 @@ const Status = {
 | 
			
		|||
        file => !fileType.fileMatchesSomeType(this.galleryTypes, file)
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
    hasImageAttachments () {
 | 
			
		||||
      return this.status.attachments.some(
 | 
			
		||||
        file => fileType.fileType(file.mimetype) === 'image'
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
    hasVideoAttachments () {
 | 
			
		||||
      return this.status.attachments.some(
 | 
			
		||||
        file => fileType.fileType(file.mimetype) === 'video'
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
    maxThumbnails () {
 | 
			
		||||
      return this.mergedConfig.maxThumbnails
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -277,7 +277,21 @@
 | 
			
		|||
              href="#"
 | 
			
		||||
              class="cw-status-hider"
 | 
			
		||||
              @click.prevent="toggleShowMore"
 | 
			
		||||
            >{{ $t("general.show_more") }}</a>
 | 
			
		||||
            >
 | 
			
		||||
              {{ $t("general.show_more") }}
 | 
			
		||||
              <span
 | 
			
		||||
                v-if="hasImageAttachments"
 | 
			
		||||
                class="icon-picture"
 | 
			
		||||
              />
 | 
			
		||||
              <span
 | 
			
		||||
                v-if="hasVideoAttachments"
 | 
			
		||||
                class="icon-video"
 | 
			
		||||
              />
 | 
			
		||||
              <span
 | 
			
		||||
                v-if="status.card"
 | 
			
		||||
                class="icon-link"
 | 
			
		||||
              />
 | 
			
		||||
            </a>
 | 
			
		||||
            <a
 | 
			
		||||
              v-if="showingMore"
 | 
			
		||||
              href="#"
 | 
			
		||||
| 
						 | 
				
			
			@ -355,6 +369,7 @@
 | 
			
		|||
          </transition>
 | 
			
		||||
 | 
			
		||||
          <EmojiReactions
 | 
			
		||||
            v-if="(mergedConfig.emojiReactionsOnTimeline || isFocused) && (!noHeading && !isPreview)"
 | 
			
		||||
            :status="status"
 | 
			
		||||
          />
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,6 +55,7 @@ const UserSettings = {
 | 
			
		|||
      showRole: this.$store.state.users.currentUser.show_role,
 | 
			
		||||
      role: this.$store.state.users.currentUser.role,
 | 
			
		||||
      discoverable: this.$store.state.users.currentUser.discoverable,
 | 
			
		||||
      allowFollowingMove: this.$store.state.users.currentUser.allow_following_move,
 | 
			
		||||
      pickAvatarBtnVisible: true,
 | 
			
		||||
      bannerUploading: false,
 | 
			
		||||
      backgroundUploading: false,
 | 
			
		||||
| 
						 | 
				
			
			@ -162,6 +163,7 @@ const UserSettings = {
 | 
			
		|||
            hide_follows: this.hideFollows,
 | 
			
		||||
            hide_followers: this.hideFollowers,
 | 
			
		||||
            discoverable: this.discoverable,
 | 
			
		||||
            allow_following_move: this.allowFollowingMove,
 | 
			
		||||
            hide_follows_count: this.hideFollowsCount,
 | 
			
		||||
            hide_followers_count: this.hideFollowersCount,
 | 
			
		||||
            show_role: this.showRole
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -90,9 +90,7 @@
 | 
			
		|||
              </Checkbox>
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
              <Checkbox
 | 
			
		||||
                v-model="hideFollowers"
 | 
			
		||||
              >
 | 
			
		||||
              <Checkbox v-model="hideFollowers">
 | 
			
		||||
                {{ $t('settings.hide_followers_description') }}
 | 
			
		||||
              </Checkbox>
 | 
			
		||||
            </p>
 | 
			
		||||
| 
						 | 
				
			
			@ -104,6 +102,11 @@
 | 
			
		|||
                {{ $t('settings.hide_followers_count_description') }}
 | 
			
		||||
              </Checkbox>
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
              <Checkbox v-model="allowFollowingMove">
 | 
			
		||||
                {{ $t('settings.allow_following_move') }}
 | 
			
		||||
              </Checkbox>
 | 
			
		||||
            </p>
 | 
			
		||||
            <p v-if="role === 'admin' || role === 'moderator'">
 | 
			
		||||
              <Checkbox v-model="showRole">
 | 
			
		||||
                <template v-if="role === 'admin'">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,7 +16,16 @@
 | 
			
		|||
    "mrf_policy_simple_media_removal": "Media Removal",
 | 
			
		||||
    "mrf_policy_simple_media_removal_desc": "This instance removes media from posts on the following instances:",
 | 
			
		||||
    "mrf_policy_simple_media_nsfw": "Media Force-set As Sensitive",
 | 
			
		||||
    "mrf_policy_simple_media_nsfw_desc": "This instance forces media to be set sensitive in posts on the following instances:"
 | 
			
		||||
    "mrf_policy_simple_media_nsfw_desc": "This instance forces media to be set sensitive in posts on the following instances:",
 | 
			
		||||
    "mrf": {
 | 
			
		||||
      "keyword": {
 | 
			
		||||
        "keyword_policies": "Keyword Policies",
 | 
			
		||||
        "ftl_removal": "Removal from \"The Whole Known Network\" Timeline",
 | 
			
		||||
        "reject": "Reject",
 | 
			
		||||
        "replace": "Replace",
 | 
			
		||||
        "is_replaced_by": "→"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "chat": {
 | 
			
		||||
    "title": "Chat"
 | 
			
		||||
| 
						 | 
				
			
			@ -118,7 +127,8 @@
 | 
			
		|||
    "read": "Read!",
 | 
			
		||||
    "repeated_you": "repeated your status",
 | 
			
		||||
    "no_more_notifications": "No more notifications",
 | 
			
		||||
    "migrated_to": "migrated to"
 | 
			
		||||
    "migrated_to": "migrated to",
 | 
			
		||||
    "reacted_with": "reacted with {0}"
 | 
			
		||||
  },
 | 
			
		||||
  "polls": {
 | 
			
		||||
    "add_poll": "Add Poll",
 | 
			
		||||
| 
						 | 
				
			
			@ -233,6 +243,7 @@
 | 
			
		|||
        "desc": "To enable two-factor authentication, enter the code from your two-factor app:"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "allow_following_move": "Allow auto-follow when following account moves",
 | 
			
		||||
    "attachmentRadius": "Attachments",
 | 
			
		||||
    "attachments": "Attachments",
 | 
			
		||||
    "autoload": "Enable automatic loading when scrolled to the bottom",
 | 
			
		||||
| 
						 | 
				
			
			@ -274,6 +285,7 @@
 | 
			
		|||
    "domain_mutes": "Domains",
 | 
			
		||||
    "avatar_size_instruction": "The recommended minimum size for avatar images is 150x150 pixels.",
 | 
			
		||||
    "pad_emoji": "Pad emoji with spaces when adding from picker",
 | 
			
		||||
    "emoji_reactions_on_timeline": "Show emoji reactions on timeline",
 | 
			
		||||
    "export_theme": "Save preset",
 | 
			
		||||
    "filtering": "Filtering",
 | 
			
		||||
    "filtering_explanation": "All statuses containing these words will be muted, one per line",
 | 
			
		||||
| 
						 | 
				
			
			@ -323,6 +335,7 @@
 | 
			
		|||
    "notification_visibility_mentions": "Mentions",
 | 
			
		||||
    "notification_visibility_repeats": "Repeats",
 | 
			
		||||
    "notification_visibility_moves": "User Migrates",
 | 
			
		||||
    "notification_visibility_emoji_reactions": "Reactions",
 | 
			
		||||
    "no_rich_text_description": "Strip rich text formatting from all posts",
 | 
			
		||||
    "no_blocks": "No blocks",
 | 
			
		||||
    "no_mutes": "No mutes",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,7 +53,8 @@
 | 
			
		|||
    "notifications": "Ilmoitukset",
 | 
			
		||||
    "read": "Lue!",
 | 
			
		||||
    "repeated_you": "toisti viestisi",
 | 
			
		||||
    "no_more_notifications": "Ei enempää ilmoituksia"
 | 
			
		||||
    "no_more_notifications": "Ei enempää ilmoituksia",
 | 
			
		||||
    "reacted_with": "lisäsi reaktion {0}"
 | 
			
		||||
  },
 | 
			
		||||
  "polls": {
 | 
			
		||||
    "add_poll": "Lisää äänestys",
 | 
			
		||||
| 
						 | 
				
			
			@ -140,6 +141,7 @@
 | 
			
		|||
    "delete_account_description": "Poista tilisi ja viestisi pysyvästi.",
 | 
			
		||||
    "delete_account_error": "Virhe poistaessa tiliäsi. Jos virhe jatkuu, ota yhteyttä palvelimesi ylläpitoon.",
 | 
			
		||||
    "delete_account_instructions": "Syötä salasanasi vahvistaaksesi tilin poiston.",
 | 
			
		||||
    "emoji_reactions_on_timeline": "Näytä emojireaktiot aikajanalla",
 | 
			
		||||
    "export_theme": "Tallenna teema",
 | 
			
		||||
    "filtering": "Suodatus",
 | 
			
		||||
    "filtering_explanation": "Kaikki viestit, jotka sisältävät näitä sanoja, suodatetaan. Yksi sana per rivi.",
 | 
			
		||||
| 
						 | 
				
			
			@ -183,6 +185,7 @@
 | 
			
		|||
    "notification_visibility_likes": "Tykkäykset",
 | 
			
		||||
    "notification_visibility_mentions": "Maininnat",
 | 
			
		||||
    "notification_visibility_repeats": "Toistot",
 | 
			
		||||
    "notification_visibility_emoji_reactions": "Reaktiot",
 | 
			
		||||
    "no_rich_text_description": "Älä näytä tekstin muotoilua.",
 | 
			
		||||
    "hide_network_description": "Älä näytä seurauksiani tai seuraajiani",
 | 
			
		||||
    "nsfw_clickthrough": "Piilota NSFW liitteet klikkauksen taakse",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ export const defaultState = {
 | 
			
		|||
  autoLoad: true,
 | 
			
		||||
  streaming: false,
 | 
			
		||||
  hoverPreview: true,
 | 
			
		||||
  emojiReactionsOnTimeline: true,
 | 
			
		||||
  autohideFloatingPostButton: false,
 | 
			
		||||
  pauseOnUnfocused: true,
 | 
			
		||||
  stopGifs: false,
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +33,8 @@ export const defaultState = {
 | 
			
		|||
    mentions: true,
 | 
			
		||||
    likes: true,
 | 
			
		||||
    repeats: true,
 | 
			
		||||
    moves: true
 | 
			
		||||
    moves: true,
 | 
			
		||||
    emojiReactions: false
 | 
			
		||||
  },
 | 
			
		||||
  webPushNotifications: false,
 | 
			
		||||
  muteWords: [],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -81,7 +81,8 @@ const visibleNotificationTypes = (rootState) => {
 | 
			
		|||
    rootState.config.notificationVisibility.mentions && 'mention',
 | 
			
		||||
    rootState.config.notificationVisibility.repeats && 'repeat',
 | 
			
		||||
    rootState.config.notificationVisibility.follows && 'follow',
 | 
			
		||||
    rootState.config.notificationVisibility.moves && 'move'
 | 
			
		||||
    rootState.config.notificationVisibility.moves && 'move',
 | 
			
		||||
    rootState.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reactions'
 | 
			
		||||
  ].filter(_ => _)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -325,6 +326,10 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot
 | 
			
		|||
      notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (notification.type === 'pleroma:emoji_reaction') {
 | 
			
		||||
      dispatch('fetchEmojiReactionsBy', notification.status.id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Only add a new notification if we don't have one for the same action
 | 
			
		||||
    if (!state.notifications.idStore.hasOwnProperty(notification.id)) {
 | 
			
		||||
      state.notifications.maxId = notification.id > state.notifications.maxId
 | 
			
		||||
| 
						 | 
				
			
			@ -358,7 +363,9 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot
 | 
			
		|||
            break
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (i18nString) {
 | 
			
		||||
        if (notification.type === 'pleroma:emoji_reaction') {
 | 
			
		||||
          notifObj.body = rootGetters.i18n.t('notifications.reacted_with', [notification.emoji])
 | 
			
		||||
        } else if (i18nString) {
 | 
			
		||||
          notifObj.body = rootGetters.i18n.t('notifications.' + i18nString)
 | 
			
		||||
        } else {
 | 
			
		||||
          notifObj.body = notification.status.text
 | 
			
		||||
| 
						 | 
				
			
			@ -371,10 +378,10 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        if (!notification.seen && !state.notifications.desktopNotificationSilence && visibleNotificationTypes.includes(notification.type)) {
 | 
			
		||||
          let notification = new window.Notification(title, notifObj)
 | 
			
		||||
          let desktopNotification = new window.Notification(title, notifObj)
 | 
			
		||||
          // Chrome is known for not closing notifications automatically
 | 
			
		||||
          // according to MDN, anyway.
 | 
			
		||||
          setTimeout(notification.close.bind(notification), 5000)
 | 
			
		||||
          setTimeout(desktopNotification.close.bind(desktopNotification), 5000)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } else if (notification.seen) {
 | 
			
		||||
| 
						 | 
				
			
			@ -537,12 +544,13 @@ export const mutations = {
 | 
			
		|||
  },
 | 
			
		||||
  addOwnReaction (state, { id, emoji, currentUser }) {
 | 
			
		||||
    const status = state.allStatusesObject[id]
 | 
			
		||||
    const reactionIndex = findIndex(status.emoji_reactions, { emoji })
 | 
			
		||||
    const reaction = status.emoji_reactions[reactionIndex] || { emoji, count: 0, accounts: [] }
 | 
			
		||||
    const reactionIndex = findIndex(status.emoji_reactions, { name: emoji })
 | 
			
		||||
    const reaction = status.emoji_reactions[reactionIndex] || { name: emoji, count: 0, accounts: [] }
 | 
			
		||||
 | 
			
		||||
    const newReaction = {
 | 
			
		||||
      ...reaction,
 | 
			
		||||
      count: reaction.count + 1,
 | 
			
		||||
      me: true,
 | 
			
		||||
      accounts: [
 | 
			
		||||
        ...reaction.accounts,
 | 
			
		||||
        currentUser
 | 
			
		||||
| 
						 | 
				
			
			@ -558,21 +566,23 @@ export const mutations = {
 | 
			
		|||
  },
 | 
			
		||||
  removeOwnReaction (state, { id, emoji, currentUser }) {
 | 
			
		||||
    const status = state.allStatusesObject[id]
 | 
			
		||||
    const reactionIndex = findIndex(status.emoji_reactions, { emoji })
 | 
			
		||||
    const reactionIndex = findIndex(status.emoji_reactions, { name: emoji })
 | 
			
		||||
    if (reactionIndex < 0) return
 | 
			
		||||
 | 
			
		||||
    const reaction = status.emoji_reactions[reactionIndex]
 | 
			
		||||
    const accounts = reaction.accounts || []
 | 
			
		||||
 | 
			
		||||
    const newReaction = {
 | 
			
		||||
      ...reaction,
 | 
			
		||||
      count: reaction.count - 1,
 | 
			
		||||
      accounts: reaction.accounts.filter(acc => acc.id === currentUser.id)
 | 
			
		||||
      me: false,
 | 
			
		||||
      accounts: accounts.filter(acc => acc.id !== currentUser.id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (newReaction.count > 0) {
 | 
			
		||||
      set(status.emoji_reactions, reactionIndex, newReaction)
 | 
			
		||||
    } else {
 | 
			
		||||
      set(status, 'emoji_reactions', status.emoji_reactions.filter(r => r.emoji !== emoji))
 | 
			
		||||
      set(status, 'emoji_reactions', status.emoji_reactions.filter(r => r.name !== emoji))
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  updateStatusWithPoll (state, { id, poll }) {
 | 
			
		||||
| 
						 | 
				
			
			@ -681,18 +691,22 @@ const statuses = {
 | 
			
		|||
    },
 | 
			
		||||
    reactWithEmoji ({ rootState, dispatch, commit }, { id, emoji }) {
 | 
			
		||||
      const currentUser = rootState.users.currentUser
 | 
			
		||||
      if (!currentUser) return
 | 
			
		||||
 | 
			
		||||
      commit('addOwnReaction', { id, emoji, currentUser })
 | 
			
		||||
      rootState.api.backendInteractor.reactWithEmoji({ id, emoji }).then(
 | 
			
		||||
        status => {
 | 
			
		||||
        ok => {
 | 
			
		||||
          dispatch('fetchEmojiReactionsBy', id)
 | 
			
		||||
        }
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
    unreactWithEmoji ({ rootState, dispatch, commit }, { id, emoji }) {
 | 
			
		||||
      const currentUser = rootState.users.currentUser
 | 
			
		||||
      if (!currentUser) return
 | 
			
		||||
 | 
			
		||||
      commit('removeOwnReaction', { id, emoji, currentUser })
 | 
			
		||||
      rootState.api.backendInteractor.unreactWithEmoji({ id, emoji }).then(
 | 
			
		||||
        status => {
 | 
			
		||||
        ok => {
 | 
			
		||||
          dispatch('fetchEmojiReactionsBy', id)
 | 
			
		||||
        }
 | 
			
		||||
      )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -74,9 +74,9 @@ const MASTODON_SEARCH_2 = `/api/v2/search`
 | 
			
		|||
const MASTODON_USER_SEARCH_URL = '/api/v1/accounts/search'
 | 
			
		||||
const MASTODON_DOMAIN_BLOCKS_URL = '/api/v1/domain_blocks'
 | 
			
		||||
const MASTODON_STREAMING = '/api/v1/streaming'
 | 
			
		||||
const PLEROMA_EMOJI_REACTIONS_URL = id => `/api/v1/pleroma/statuses/${id}/emoji_reactions_by`
 | 
			
		||||
const PLEROMA_EMOJI_REACT_URL = id => `/api/v1/pleroma/statuses/${id}/react_with_emoji`
 | 
			
		||||
const PLEROMA_EMOJI_UNREACT_URL = id => `/api/v1/pleroma/statuses/${id}/unreact_with_emoji`
 | 
			
		||||
const PLEROMA_EMOJI_REACTIONS_URL = id => `/api/v1/pleroma/statuses/${id}/reactions`
 | 
			
		||||
const PLEROMA_EMOJI_REACT_URL = (id, emoji) => `/api/v1/pleroma/statuses/${id}/reactions/${emoji}`
 | 
			
		||||
const PLEROMA_EMOJI_UNREACT_URL = (id, emoji) => `/api/v1/pleroma/statuses/${id}/reactions/${emoji}`
 | 
			
		||||
 | 
			
		||||
const oldfetch = window.fetch
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -495,7 +495,8 @@ const fetchTimeline = ({
 | 
			
		|||
  until = false,
 | 
			
		||||
  userId = false,
 | 
			
		||||
  tag = false,
 | 
			
		||||
  withMuted = false
 | 
			
		||||
  withMuted = false,
 | 
			
		||||
  withMove = false
 | 
			
		||||
}) => {
 | 
			
		||||
  const timelineUrls = {
 | 
			
		||||
    public: MASTODON_PUBLIC_TIMELINE,
 | 
			
		||||
| 
						 | 
				
			
			@ -535,6 +536,9 @@ const fetchTimeline = ({
 | 
			
		|||
  if (timeline === 'public' || timeline === 'publicAndExternal') {
 | 
			
		||||
    params.push(['only_media', false])
 | 
			
		||||
  }
 | 
			
		||||
  if (timeline === 'notifications') {
 | 
			
		||||
    params.push(['with_move', withMove])
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  params.push(['count', 20])
 | 
			
		||||
  params.push(['with_muted', withMuted])
 | 
			
		||||
| 
						 | 
				
			
			@ -884,25 +888,27 @@ const fetchRebloggedByUsers = ({ id }) => {
 | 
			
		|||
  return promisedRequest({ url: MASTODON_STATUS_REBLOGGEDBY_URL(id) }).then((users) => users.map(parseUser))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const fetchEmojiReactions = ({ id }) => {
 | 
			
		||||
  return promisedRequest({ url: PLEROMA_EMOJI_REACTIONS_URL(id) })
 | 
			
		||||
const fetchEmojiReactions = ({ id, credentials }) => {
 | 
			
		||||
  return promisedRequest({ url: PLEROMA_EMOJI_REACTIONS_URL(id), credentials })
 | 
			
		||||
    .then((reactions) => reactions.map(r => {
 | 
			
		||||
      r.accounts = r.accounts.map(parseUser)
 | 
			
		||||
      return r
 | 
			
		||||
    }))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const reactWithEmoji = ({ id, emoji, credentials }) => {
 | 
			
		||||
  return promisedRequest({
 | 
			
		||||
    url: PLEROMA_EMOJI_REACT_URL(id),
 | 
			
		||||
    method: 'POST',
 | 
			
		||||
    credentials,
 | 
			
		||||
    payload: { emoji }
 | 
			
		||||
    url: PLEROMA_EMOJI_REACT_URL(id, emoji),
 | 
			
		||||
    method: 'PUT',
 | 
			
		||||
    credentials
 | 
			
		||||
  }).then(parseStatus)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const unreactWithEmoji = ({ id, emoji, credentials }) => {
 | 
			
		||||
  return promisedRequest({
 | 
			
		||||
    url: PLEROMA_EMOJI_UNREACT_URL(id),
 | 
			
		||||
    method: 'POST',
 | 
			
		||||
    credentials,
 | 
			
		||||
    payload: { emoji }
 | 
			
		||||
    url: PLEROMA_EMOJI_UNREACT_URL(id, emoji),
 | 
			
		||||
    method: 'DELETE',
 | 
			
		||||
    credentials
 | 
			
		||||
  }).then(parseStatus)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -83,6 +83,8 @@ export const parseUser = (data) => {
 | 
			
		|||
        output.subscribed = relationship.subscribing
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      output.allow_following_move = data.pleroma.allow_following_move
 | 
			
		||||
 | 
			
		||||
      output.hide_follows = data.pleroma.hide_follows
 | 
			
		||||
      output.hide_followers = data.pleroma.hide_followers
 | 
			
		||||
      output.hide_follows_count = data.pleroma.hide_follows_count
 | 
			
		||||
| 
						 | 
				
			
			@ -352,6 +354,7 @@ export const parseNotification = (data) => {
 | 
			
		|||
      ? null
 | 
			
		||||
      : parseUser(data.target)
 | 
			
		||||
    output.from_profile = parseUser(data.account)
 | 
			
		||||
    output.emoji = data.emoji
 | 
			
		||||
  } else {
 | 
			
		||||
    const parsedNotice = parseStatus(data.notice)
 | 
			
		||||
    output.type = data.ntype
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,8 @@ export const visibleTypes = store => ([
 | 
			
		|||
  store.state.config.notificationVisibility.mentions && 'mention',
 | 
			
		||||
  store.state.config.notificationVisibility.repeats && 'repeat',
 | 
			
		||||
  store.state.config.notificationVisibility.follows && 'follow',
 | 
			
		||||
  store.state.config.notificationVisibility.moves && 'move'
 | 
			
		||||
  store.state.config.notificationVisibility.moves && 'move',
 | 
			
		||||
  store.state.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reaction'
 | 
			
		||||
].filter(_ => _))
 | 
			
		||||
 | 
			
		||||
const sortById = (a, b) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,9 +11,12 @@ const fetchAndUpdate = ({ store, credentials, older = false }) => {
 | 
			
		|||
  const rootState = store.rootState || store.state
 | 
			
		||||
  const timelineData = rootState.statuses.notifications
 | 
			
		||||
  const hideMutedPosts = getters.mergedConfig.hideMutedPosts
 | 
			
		||||
  const allowFollowingMove = rootState.users.currentUser.allow_following_move
 | 
			
		||||
 | 
			
		||||
  args['withMuted'] = !hideMutedPosts
 | 
			
		||||
 | 
			
		||||
  args['withMove'] = !allowFollowingMove
 | 
			
		||||
 | 
			
		||||
  args['timeline'] = 'notifications'
 | 
			
		||||
  if (older) {
 | 
			
		||||
    if (timelineData.minId !== Number.POSITIVE_INFINITY) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -339,6 +339,12 @@
 | 
			
		|||
      "css": "arrow-curved",
 | 
			
		||||
      "code": 59426,
 | 
			
		||||
      "src": "iconic"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "0ddd3e8201ccc7d41f7b7c9d27eca6c1",
 | 
			
		||||
      "css": "link",
 | 
			
		||||
      "code": 59427,
 | 
			
		||||
      "src": "fontawesome"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -245,11 +245,12 @@ describe('Statuses module', () => {
 | 
			
		|||
    it('increments count in existing reaction', () => {
 | 
			
		||||
      const state = defaultState()
 | 
			
		||||
      const status = makeMockStatus({ id: '1' })
 | 
			
		||||
      status.emoji_reactions = [ { emoji: '😂', count: 1, accounts: [] } ]
 | 
			
		||||
      status.emoji_reactions = [ { name: '😂', count: 1, accounts: [] } ]
 | 
			
		||||
 | 
			
		||||
      mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
 | 
			
		||||
      mutations.addOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } })
 | 
			
		||||
      expect(state.allStatusesObject['1'].emoji_reactions[0].count).to.eql(2)
 | 
			
		||||
      expect(state.allStatusesObject['1'].emoji_reactions[0].me).to.eql(true)
 | 
			
		||||
      expect(state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id).to.eql('me')
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -261,27 +262,29 @@ describe('Statuses module', () => {
 | 
			
		|||
      mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
 | 
			
		||||
      mutations.addOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } })
 | 
			
		||||
      expect(state.allStatusesObject['1'].emoji_reactions[0].count).to.eql(1)
 | 
			
		||||
      expect(state.allStatusesObject['1'].emoji_reactions[0].me).to.eql(true)
 | 
			
		||||
      expect(state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id).to.eql('me')
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    it('decreases count in existing reaction', () => {
 | 
			
		||||
      const state = defaultState()
 | 
			
		||||
      const status = makeMockStatus({ id: '1' })
 | 
			
		||||
      status.emoji_reactions = [ { emoji: '😂', count: 2, accounts: [{ id: 'me' }] } ]
 | 
			
		||||
      status.emoji_reactions = [ { name: '😂', count: 2, accounts: [{ id: 'me' }] } ]
 | 
			
		||||
 | 
			
		||||
      mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
 | 
			
		||||
      mutations.removeOwnReaction(state, { id: '1', emoji: '😂', currentUser: {} })
 | 
			
		||||
      mutations.removeOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } })
 | 
			
		||||
      expect(state.allStatusesObject['1'].emoji_reactions[0].count).to.eql(1)
 | 
			
		||||
      expect(state.allStatusesObject['1'].emoji_reactions[0].me).to.eql(false)
 | 
			
		||||
      expect(state.allStatusesObject['1'].emoji_reactions[0].accounts).to.eql([])
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    it('removes a reaction', () => {
 | 
			
		||||
      const state = defaultState()
 | 
			
		||||
      const status = makeMockStatus({ id: '1' })
 | 
			
		||||
      status.emoji_reactions = [{ emoji: '😂', count: 1, accounts: [{ id: 'me' }] }]
 | 
			
		||||
      status.emoji_reactions = [{ name: '😂', count: 1, accounts: [{ id: 'me' }] }]
 | 
			
		||||
 | 
			
		||||
      mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
 | 
			
		||||
      mutations.removeOwnReaction(state, { id: '1', emoji: '😂', currentUser: {} })
 | 
			
		||||
      mutations.removeOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } })
 | 
			
		||||
      expect(state.allStatusesObject['1'].emoji_reactions.length).to.eql(0)
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue