Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85826501
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
22 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/components/emoji_reactions/emoji_reactions.js b/src/components/emoji_reactions/emoji_reactions.js
index e1a28a8f18..e22336138e 100644
--- a/src/components/emoji_reactions/emoji_reactions.js
+++ b/src/components/emoji_reactions/emoji_reactions.js
@@ -1,103 +1,107 @@
import StillImage from 'src/components/still-image/still-image.vue'
import UserAvatar from '../user_avatar/user_avatar.vue'
import UserListPopover from '../user_list_popover/user_list_popover.vue'
import { useInstanceStore } from 'src/stores/instance.js'
+import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faCheck, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons'
library.add(faPlus, faMinus, faCheck)
const EMOJI_REACTION_COUNT_CUTOFF = 12
const EmojiReactions = {
name: 'EmojiReactions',
components: {
UserAvatar,
UserListPopover,
StillImage,
},
props: ['status'],
data: () => ({
showAll: false,
}),
computed: {
tooManyReactions() {
return this.status.emoji_reactions.length > EMOJI_REACTION_COUNT_CUTOFF
},
emojiReactions() {
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
},
remoteInteractionLink() {
return useInstanceStore().getRemoteInteractionLink({
statusId: this.status.id,
})
},
+ allowNonSquareEmoji() {
+ return useMergedConfigStore().mergedConfig.nonSquareEmoji
+ },
},
methods: {
toggleShowAll() {
this.showAll = !this.showAll
},
reactedWith(emoji) {
return this.status.emoji_reactions.find((r) => r.name === emoji).me
},
async fetchEmojiReactionsByIfMissing() {
const hasNoAccounts = this.status.emoji_reactions.find((r) => !r.accounts)
if (hasNoAccounts) {
return await this.$store.dispatch(
'fetchEmojiReactionsBy',
this.status.id,
)
}
},
reactWith(emoji) {
this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
},
unreact(emoji) {
this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji })
},
async emojiOnClick(emoji) {
if (!this.loggedIn) return
await this.fetchEmojiReactionsByIfMissing()
if (this.reactedWith(emoji)) {
this.unreact(emoji)
} else {
this.reactWith(emoji)
}
},
counterTriggerAttrs(reaction) {
return {
class: [
'emoji-reaction-count-button',
{
'-picked-reaction': this.reactedWith(reaction.name),
toggled: this.reactedWith(reaction.name),
},
],
'aria-label': this.$t(
'status.reaction_count_label',
{ num: reaction.count },
reaction.count,
),
}
},
},
}
export default EmojiReactions
diff --git a/src/components/emoji_reactions/emoji_reactions.scss b/src/components/emoji_reactions/emoji_reactions.scss
index 8ea45e1b4f..88ff1cee6d 100644
--- a/src/components/emoji_reactions/emoji_reactions.scss
+++ b/src/components/emoji_reactions/emoji_reactions.scss
@@ -1,121 +1,133 @@
@use "../../mixins";
.EmojiReactions {
display: flex;
margin-top: 0.25em;
flex-wrap: wrap;
--emoji-size: calc(var(--emojiSize, 1.25em) * var(--emojiReactionsScale, 1));
.emoji-reaction-container {
display: flex;
align-items: stretch;
margin-top: 0.5em;
margin-right: 0.5em;
.emoji-reaction-popover {
padding: 0;
.emoji-reaction-count-button {
margin: 0;
height: 100%;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
box-sizing: border-box;
min-width: 2em;
display: inline-flex;
justify-content: center;
align-items: center;
}
}
}
.emoji-reaction {
padding-left: 0.5em;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
margin: 0;
.reaction-emoji {
width: var(--emoji-size);
height: var(--emoji-size);
margin-right: 0.25em;
line-height: var(--emoji-size);
display: flex;
justify-content: center;
align-items: center;
+ &.-wide {
+ width: auto;
+ min-width: var(--emoji-size);
+ max-width: calc(var(--emoji-size) * 3);
+ }
+
--_still_image-label-scale: 0.3;
}
.reaction-emoji-content {
max-width: 100%;
max-height: 100%;
width: var(--emoji-size);
height: var(--emoji-size);
line-height: inherit;
overflow: hidden;
font-size: calc(var(--emoji-size) * 0.8);
margin: 0;
+ &.-wide {
+ width: auto;
+ min-width: var(--emoji-size);
+ max-width: calc(var(--emoji-size) * 3);
+ }
+
img {
object-fit: contain;
}
}
&:focus {
outline: none;
}
.svg-inline--fa {
color: var(--text);
}
&.-picked-reaction {
.svg-inline--fa {
color: var(--accent);
}
}
@include mixins.unfocused-style {
.focus-marker {
visibility: hidden;
}
.active-marker {
visibility: visible;
}
}
@include mixins.focused-style {
.svg-inline--fa {
color: var(--accent);
}
.focus-marker {
visibility: visible;
}
.active-marker {
visibility: hidden;
}
}
}
.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;
}
}
}
diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue
index e2361885a9..16c69fa40e 100644
--- a/src/components/emoji_reactions/emoji_reactions.vue
+++ b/src/components/emoji_reactions/emoji_reactions.vue
@@ -1,75 +1,77 @@
<template>
<div class="EmojiReactions">
<span
v-for="(reaction) in emojiReactions"
:key="reaction.url || reaction.name"
class="emoji-reaction-container btn-group"
>
<component
:is="loggedIn ? 'button' : 'a'"
v-bind="!loggedIn ? { href: remoteInteractionLink } : {}"
role="button"
class="emoji-reaction btn button-default"
:class="{ '-picked-reaction': reactedWith(reaction.name), toggled: reactedWith(reaction.name) }"
:title="reaction.url ? reaction.name : undefined"
:aria-pressed="reactedWith(reaction.name)"
@click="emojiOnClick(reaction.name, $event)"
>
<span
class="reaction-emoji"
+ :class="{ ['-wide']: allowNonSquareEmoji }"
>
<StillImage
v-if="reaction.url"
:src="reaction.url"
class="reaction-emoji-content"
+ :class="{ ['-wide']: allowNonSquareEmoji }"
/>
<span
v-else
class="reaction-emoji reaction-emoji-content"
>{{ reaction.name }}</span>
</span>
<FALayers>
<FAIcon
v-if="reactedWith(reaction.name)"
class="active-marker"
transform="shrink-6 up-9"
icon="check"
/>
<FAIcon
v-if="!reactedWith(reaction.name)"
class="focus-marker"
transform="shrink-6 up-9"
icon="plus"
/>
<FAIcon
v-else
class="focus-marker"
transform="shrink-6 up-9"
icon="minus"
/>
</FALayers>
</component>
<UserListPopover
:users="accountsForEmoji[reaction.name]"
class="emoji-reaction-popover"
:normal-button="true"
:trigger-attrs="counterTriggerAttrs(reaction)"
@show="fetchEmojiReactionsByIfMissing()"
>
<span class="emoji-reaction-counts">{{ reaction.count }}</span>
</UserListPopover>
</span>
<a
v-if="tooManyReactions"
class="emoji-reaction-expand faint"
href="javascript:void(0)"
@click="toggleShowAll"
>
{{ showAll ? $t('general.show_less') : showMoreString }}
</a>
</div>
</template>
<script src="./emoji_reactions.js"></script>
<style src="./emoji_reactions.scss" lang="scss" />
diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue
index d0accc3897..fbe45eceb1 100644
--- a/src/components/notification/notification.vue
+++ b/src/components/notification/notification.vue
@@ -1,294 +1,295 @@
<template>
<article
v-if="notification.type === 'mention' || notification.type === 'status'"
ref="root"
>
<Status
class="Notification"
:compact="true"
:statusoid="notification.status"
@click="interacted"
/>
</article>
<article
v-else
ref="root"
class="NotificationParent"
:class="{ '-expandable': expandable }"
>
<div
v-if="needMute && !unmuted"
:id="'notif-' +notification.id"
:aria-expanded="statusExpanded"
:aria-controls="'notif-' +notification.id"
class="Notification container -muted"
>
<small>
<user-link
:user="notification.from_profile"
:at="false"
/>
</small>
<button
class="button-unstyled unmute"
@click.prevent="toggleMute"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="eye-slash"
/>
</button>
</div>
<div
v-else
class="Notification non-mention"
:class="[userClass, { highlighted: userStyle }, '-type--' + notification.type]"
:style="[ userStyle ]"
>
<a
class="avatar-container"
:href="$router.resolve(userProfileLink).href"
@click.prevent
>
<UserPopover
:user-id="notification.from_profile.id"
:overlay-centers="true"
>
<UserAvatar
class="post-avatar"
:compact="true"
:user="notification.from_profile"
/>
</UserPopover>
</a>
<div class="notification-right">
<span class="notification-details">
<div class="name-and-action">
<!-- eslint-disable vue/no-v-html -->
<bdi v-if="!!notification.from_profile.name_html">
<RichContent
class="username"
:title="'@'+notification.from_profile.screen_name_ui"
:html="notification.from_profile.name_html"
:emoji="notification.from_profile.emoji"
:allow-non-square-emoji="allowNonSquareEmoji"
:is-local="notification.from_profile.is_local"
/>
</bdi>
<!-- eslint-enable vue/no-v-html -->
<span
v-else
class="username"
:title="'@'+notification.from_profile.screen_name_ui"
>
{{ notification.from_profile.name }}
</span>
{{ ' ' }}
<span v-if="notification.type === 'like'">
<FAIcon
class="type-icon"
icon="star"
/>
{{ ' ' }}
<small>{{ $t('notifications.favorited_you') }}</small>
</span>
<span v-if="notification.type === 'repeat'">
<FAIcon
class="type-icon"
icon="retweet"
:title="$t('tool_tip.repeat')"
/>
{{ ' ' }}
<small>{{ $t('notifications.repeated_you') }}</small>
</span>
<span v-if="notification.type === 'follow'">
<FAIcon
class="type-icon"
icon="user-plus"
/>
{{ ' ' }}
<small>{{ $t('notifications.followed_you') }}</small>
</span>
<span v-if="notification.type === 'follow_request'">
<FAIcon
class="type-icon"
icon="user"
/>
{{ ' ' }}
<small>{{ $t('notifications.follow_request') }}</small>
</span>
<span v-if="notification.type === 'move'">
<FAIcon
class="type-icon"
icon="suitcase-rolling"
/>
{{ ' ' }}
<small>{{ $t('notifications.migrated_to') }}</small>
</span>
<span v-if="notification.type === 'pleroma:emoji_reaction'">
<small>
<i18n-t
scope="global"
keypath="notifications.reacted_with"
>
<img
v-if="notification.emoji_url"
class="emoji-reaction-emoji emoji-reaction-emoji-image"
:src="notification.emoji_url"
:alt="notification.emoji"
:title="notification.emoji"
+ :class="{ ['-wide']: allowNonSquareEmoji }"
>
<span
v-else
class="emoji-reaction-emoji"
>{{ notification.emoji }}</span>
</i18n-t>
</small>
</span>
<span v-if="notification.type === 'pleroma:report'">
<small>{{ $t('notifications.submitted_report') }}</small>
</span>
<span v-if="notification.type === 'poll'">
<FAIcon
class="type-icon"
icon="poll-h"
/>
{{ ' ' }}
<small>{{ $t('notifications.poll_ended') }}</small>
</span>
</div>
<div
v-if="isStatusNotification"
class="timeago"
>
<router-link
v-if="notification.status"
:to="{ name: 'conversation', params: { id: notification.status.id } }"
class="timeago-link faint"
>
<Timeago
:time="notification.created_at"
:auto-update="240"
/>
</router-link>
<button
class="button-unstyled expand-icon"
:title="$t('tool_tip.toggle_expand')"
:aria-expanded="statusExpanded"
@click.prevent="toggleStatusExpanded"
>
<FAIcon
class="fa-scale-110"
fixed-width
:icon="statusExpanded ? 'compress-alt' : 'expand-alt'"
/>
</button>
</div>
<div
v-else
class="timeago"
>
<span class="faint">
<Timeago
:time="notification.created_at"
:auto-update="240"
/>
</span>
</div>
<button
v-if="needMute"
class="button-unstyled"
:title="$t('tool_tip.toggle_mute')"
:aria-expanded="!unmuted"
@click.prevent="toggleMute"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="eye-slash"
/>
</button>
</span>
<div
v-if="notification.type === 'follow' || notification.type === 'follow_request'"
class="follow-text"
>
<user-link
class="follow-name"
:user="notification.from_profile"
/>
<div
v-if="notification.type === 'follow_request'"
style="white-space: nowrap;"
>
<button
class="button-unstyled"
:title="$t('tool_tip.accept_follow_request')"
@click="approveUser()"
>
<FAIcon
icon="check"
class="fa-scale-110 fa-old-padding follow-request-accept"
/>
</button>
<button
class="button-unstyled"
:title="$t('tool_tip.reject_follow_request')"
@click="denyUser()"
>
<FAIcon
icon="times"
class="fa-scale-110 fa-old-padding follow-request-reject"
/>
</button>
</div>
</div>
<div
v-else-if="notification.type === 'move'"
class="move-text"
>
<user-link
:user="notification.target"
/>
</div>
<Report
v-else-if="notification.type === 'pleroma:report'"
:report-id="notification.report.id"
/>
<template v-else>
<StatusContent
class="status-content"
:compact="!statusExpanded"
:status="notification.status"
:collapse="!statusExpanded"
@click="onContentClick"
/>
</template>
</div>
</div>
<teleport to="#modal">
<confirm-modal
v-if="showingApproveConfirmDialog"
:title="$t('user_card.approve_confirm_title')"
:confirm-text="$t('user_card.approve_confirm_accept_button')"
:cancel-text="$t('user_card.approve_confirm_cancel_button')"
@accepted="doApprove"
@cancelled="hideApproveConfirmDialog"
>
{{ $t('user_card.approve_confirm', { user: user.screen_name_ui }) }}
</confirm-modal>
<confirm-modal
v-if="showingDenyConfirmDialog"
:title="$t('user_card.deny_confirm_title')"
:confirm-text="$t('user_card.deny_confirm_accept_button')"
:cancel-text="$t('user_card.deny_confirm_cancel_button')"
@accepted="doDeny"
@cancelled="hideDenyConfirmDialog"
>
{{ $t('user_card.deny_confirm', { user: user.screen_name_ui }) }}
</confirm-modal>
</teleport>
</article>
</template>
<script src="./notification.js"></script>
<style src="./notification.scss" lang="scss"></style>
diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss
index 79ef5b3009..32839ebbf4 100644
--- a/src/components/notifications/notifications.scss
+++ b/src/components/notifications/notifications.scss
@@ -1,181 +1,185 @@
.Notifications {
&:not(.minimal) {
// a bit of a hack to allow scrolling below notifications
padding-bottom: 15em;
}
.loadmore-error {
color: var(--text);
}
.notification {
position: relative;
.notification-overlay {
position: absolute;
inset: 0;
pointer-events: none;
}
&.unseen {
.notification-overlay {
background-image: linear-gradient(135deg, var(--badgeNotification) 4px, transparent 10px);
}
}
}
}
/* stylelint-disable-next-line no-descending-specificity */
.notification {
box-sizing: border-box;
/* TODO cleanup this */
.Status {
flex: 1;
}
&:hover .animated.Avatar {
canvas {
display: none;
}
img {
visibility: visible;
}
}
&:last-child .Notification {
border-bottom: none;
}
.non-mention {
display: flex;
flex: 1;
flex-wrap: nowrap;
padding: 0.6em;
min-width: 0;
.avatar-container {
width: 32px;
height: 32px;
}
}
.follow-request-accept {
&:hover {
color: var(--text);
}
}
.follow-request-reject {
&:hover {
color: var(--cRed);
}
}
.follow-text,
.move-text {
padding: 0.5em 0;
overflow-wrap: break-word;
display: flex;
justify-content: space-between;
.follow-name {
display: block;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
time {
white-space: nowrap;
}
.notification-right {
flex: 1;
padding-left: 0.8em;
min-width: 0;
.timeago {
min-width: 3em;
text-align: right;
}
.timeago-link {
margin-right: 0.2em;
}
.expand-icon {
.svg-inline--fa {
margin-left: 0.25em;
}
}
}
.emoji-reaction-emoji {
font-size: 1.3em;
max-width: 1.25em;
height: 1.25em;
width: auto;
+
+ &.-wide {
+ max-width: 3.75em;
+ }
}
.emoji-reaction-emoji-image {
vertical-align: middle;
object-fit: contain;
}
.notification-details {
min-width: 0;
line-height: var(--post-line-height);
position: relative;
overflow: hidden;
width: 100%;
flex: 1 1 0;
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
.name-and-action {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
}
.username {
font-weight: bolder;
max-width: 100%;
text-overflow: ellipsis;
white-space: nowrap;
}
.timeago {
margin-right: 0.2em;
}
.status-content {
margin: 0;
max-height: 300px;
}
h1 {
overflow-wrap: break-word;
text-wrap: pretty;
margin: 0 0 0.3em;
padding: 0;
font-size: 1em;
line-height: 1.5;
small {
font-weight: lighter;
}
}
p {
margin: 0;
margin-top: 0;
margin-bottom: 0.3em;
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Oct 30, 7:46 AM (23 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1799814
Default Alt Text
(22 KB)
Attached To
Mode
rPUFE pleroma-fe-upstream
Attached
Detach File
Event Timeline