Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
22 KB
Referenced Files
None
Subscribers
None
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

Mime Type
text/x-diff
Expires
Fri, Oct 30, 7:46 AM (22 h, 23 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1799814
Default Alt Text
(22 KB)

Event Timeline