diff options
| author | HJ <30-hj@users.noreply.git.pleroma.social> | 2020-02-11 23:09:15 +0000 |
|---|---|---|
| committer | HJ <30-hj@users.noreply.git.pleroma.social> | 2020-02-11 23:09:15 +0000 |
| commit | 84ebae8ed36390f62ccda653d4fb9d8aad1482a2 (patch) | |
| tree | 70fb486e4b31b029c8dbcb89f87acf154275f905 /src/components | |
| parent | 60446c56a5c8d4b4d669fbd4803e77ce686daac9 (diff) | |
| parent | 96dc297babd5797b9307af8676719c2eb8cd80e2 (diff) | |
Merge branch 'develop' into 'themes-accent'
# Conflicts:
# src/components/emoji_reactions/emoji_reactions.vue
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/emoji_reactions/emoji_reactions.js | 48 | ||||
| -rw-r--r-- | src/components/emoji_reactions/emoji_reactions.vue | 103 | ||||
| -rw-r--r-- | src/components/interactions/interactions.js | 1 | ||||
| -rw-r--r-- | src/components/interactions/interactions.vue | 1 | ||||
| -rw-r--r-- | src/components/mrf_transparency_panel/mrf_transparency_panel.js | 10 | ||||
| -rw-r--r-- | src/components/mrf_transparency_panel/mrf_transparency_panel.vue | 43 | ||||
| -rw-r--r-- | src/components/notification/notification.vue | 7 | ||||
| -rw-r--r-- | src/components/notifications/notifications.scss | 4 | ||||
| -rw-r--r-- | src/components/react_button/react_button.js | 7 | ||||
| -rw-r--r-- | src/components/react_button/react_button.vue | 4 | ||||
| -rw-r--r-- | src/components/settings/settings.vue | 10 | ||||
| -rw-r--r-- | src/components/status/status.js | 10 | ||||
| -rw-r--r-- | src/components/status/status.vue | 17 | ||||
| -rw-r--r-- | src/components/user_settings/user_settings.js | 2 | ||||
| -rw-r--r-- | src/components/user_settings/user_settings.vue | 9 |
15 files changed, 258 insertions, 18 deletions
diff --git a/src/components/emoji_reactions/emoji_reactions.js b/src/components/emoji_reactions/emoji_reactions.js index 95d52cb6..b799ac9a 100644 --- a/src/components/emoji_reactions/emoji_reactions.js +++ b/src/components/emoji_reactions/emoji_reactions.js @@ -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 { diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue index f5d2e79a..290b25b3 100644 --- a/src/components/emoji_reactions/emoji_reactions.vue +++ b/src/components/emoji_reactions/emoji_reactions.vue @@ -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> diff --git a/src/components/interactions/interactions.js b/src/components/interactions/interactions.js index cc31ff20..7fe5e76d 100644 --- a/src/components/interactions/interactions.js +++ b/src/components/interactions/interactions.js @@ -10,6 +10,7 @@ const tabModeDict = { const Interactions = { data () { return { + allowFollowingMove: this.$store.state.users.currentUser.allow_following_move, filterMode: tabModeDict['mentions'] } }, diff --git a/src/components/interactions/interactions.vue b/src/components/interactions/interactions.vue index a2e252ab..57d5d87c 100644 --- a/src/components/interactions/interactions.vue +++ b/src/components/interactions/interactions.vue @@ -22,6 +22,7 @@ :label="$t('interactions.follows')" /> <span + v-if="!allowFollowingMove" key="moves" :label="$t('interactions.moves')" /> diff --git a/src/components/mrf_transparency_panel/mrf_transparency_panel.js b/src/components/mrf_transparency_panel/mrf_transparency_panel.js index 6a1baec8..a0b600d2 100644 --- a/src/components/mrf_transparency_panel/mrf_transparency_panel.js +++ b/src/components/mrf_transparency_panel/mrf_transparency_panel.js @@ -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 } } } diff --git a/src/components/mrf_transparency_panel/mrf_transparency_panel.vue b/src/components/mrf_transparency_panel/mrf_transparency_panel.vue index d6495dc6..8038e587 100644 --- a/src/components/mrf_transparency_panel/mrf_transparency_panel.vue +++ b/src/components/mrf_transparency_panel/mrf_transparency_panel.vue @@ -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> diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index 16124e50..411c0271 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -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'" diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 64ded936..f5b13322 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -97,6 +97,10 @@ min-width: 0; } + .emoji-reaction-emoji { + font-size: 16px; + } + .notification-details { min-width: 0px; word-wrap: break-word; diff --git a/src/components/react_button/react_button.js b/src/components/react_button/react_button.js index 6fb2a780..a6cf5b94 100644 --- a/src/components/react_button/react_button.js +++ b/src/components/react_button/react_button.js @@ -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() } }, diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue index c925dd71..fb43ebaf 100644 --- a/src/components/react_button/react_button.vue +++ b/src/components/react_button/react_button.vue @@ -54,6 +54,10 @@ .reaction-picker-filter { padding: 0.5em; + display: flex; + input { + flex: 1; + } } .reaction-picker-divider { diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue index 22e14a89..9e14b449 100644 --- a/src/components/settings/settings.vue +++ b/src/components/settings/settings.vue @@ -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> diff --git a/src/components/status/status.js b/src/components/status/status.js index 81b57667..fc5956ec 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -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 }, diff --git a/src/components/status/status.vue b/src/components/status/status.vue index a326874e..76b038d9 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -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" /> diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js index 38373056..eca6f9b1 100644 --- a/src/components/user_settings/user_settings.js +++ b/src/components/user_settings/user_settings.js @@ -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 diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue index 2222c293..8b2336b4 100644 --- a/src/components/user_settings/user_settings.vue +++ b/src/components/user_settings/user_settings.vue @@ -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'"> |
