diff options
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 | 75 | ||||
| -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, 274 insertions, 34 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 00d6d2b7..e5b6d9f5 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="{ 'picked-reaction': 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; + } } .picked-reaction { 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..acdf822e 100644 --- a/src/components/mrf_transparency_panel/mrf_transparency_panel.vue +++ b/src/components/mrf_transparency_panel/mrf_transparency_panel.vue @@ -6,13 +6,13 @@ <div class="panel panel-default base01-background"> <div class="panel-heading timeline-heading base02-background"> <div class="title"> - {{ $t("about.federation") }} + {{ $t("about.mrf.federation") }} </div> </div> <div class="panel-body"> <div class="mrf-section"> - <h2>{{ $t("about.mrf_policies") }}</h2> - <p>{{ $t("about.mrf_policies_desc") }}</p> + <h2>{{ $t("about.mrf.mrf_policies") }}</h2> + <p>{{ $t("about.mrf.mrf_policies_desc") }}</p> <ul> <li @@ -23,13 +23,13 @@ </ul> <h2 v-if="hasInstanceSpecificPolicies"> - {{ $t("about.mrf_policy_simple") }} + {{ $t("about.mrf.simple.simple_policies") }} </h2> <div v-if="acceptInstances.length"> - <h4>{{ $t("about.mrf_policy_simple_accept") }}</h4> + <h4>{{ $t("about.mrf.simple.accept") }}</h4> - <p>{{ $t("about.mrf_policy_simple_accept_desc") }}</p> + <p>{{ $t("about.mrf.simple.accept_desc") }}</p> <ul> <li @@ -41,9 +41,9 @@ </div> <div v-if="rejectInstances.length"> - <h4>{{ $t("about.mrf_policy_simple_reject") }}</h4> + <h4>{{ $t("about.mrf.simple.reject") }}</h4> - <p>{{ $t("about.mrf_policy_simple_reject_desc") }}</p> + <p>{{ $t("about.mrf.simple.reject_desc") }}</p> <ul> <li @@ -55,9 +55,9 @@ </div> <div v-if="quarantineInstances.length"> - <h4>{{ $t("about.mrf_policy_simple_quarantine") }}</h4> + <h4>{{ $t("about.mrf.simple.quarantine") }}</h4> - <p>{{ $t("about.mrf_policy_simple_quarantine_desc") }}</p> + <p>{{ $t("about.mrf.simple.quarantine_desc") }}</p> <ul> <li @@ -69,9 +69,9 @@ </div> <div v-if="ftlRemovalInstances.length"> - <h4>{{ $t("about.mrf_policy_simple_ftl_removal") }}</h4> + <h4>{{ $t("about.mrf.simple.ftl_removal") }}</h4> - <p>{{ $t("about.mrf_policy_simple_ftl_removal_desc") }}</p> + <p>{{ $t("about.mrf.simple.ftl_removal_desc") }}</p> <ul> <li @@ -83,9 +83,9 @@ </div> <div v-if="mediaNsfwInstances.length"> - <h4>{{ $t("about.mrf_policy_simple_media_nsfw") }}</h4> + <h4>{{ $t("about.mrf.simple.media_nsfw") }}</h4> - <p>{{ $t("about.mrf_policy_simple_media_nsfw_desc") }}</p> + <p>{{ $t("about.mrf.simple.media_nsfw_desc") }}</p> <ul> <li @@ -97,9 +97,9 @@ </div> <div v-if="mediaRemovalInstances.length"> - <h4>{{ $t("about.mrf_policy_simple_media_removal") }}</h4> + <h4>{{ $t("about.mrf.simple.media_removal") }}</h4> - <p>{{ $t("about.mrf_policy_simple_media_removal_desc") }}</p> + <p>{{ $t("about.mrf.simple.media_removal_desc") }}</p> <ul> <li @@ -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 148ac7f2..8d819a56 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -94,6 +94,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 cef492f3..60cb8a87 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 d5739304..83f07dac 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'"> |
