Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85806394
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
33 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/components/activity_header/activity_header.scss b/src/components/activity_header/activity_header.scss
index 6459e1b9cf..713effe4a8 100644
--- a/src/components/activity_header/activity_header.scss
+++ b/src/components/activity_header/activity_header.scss
@@ -1,81 +1,82 @@
.ActivityHeader {
display: grid;
- line-height: 1.1;
+ line-height: var(--__header-line-height);
grid-template-columns: auto auto minmax(min-content, 1fr);
- grid-template-rows: 1.1em;
+ grid-template-rows: var(--__header-height);
grid-template-areas:
"title handle misc";
- gap: calc(var(--status-margin) / 4) calc(var(--status-margin) / 2);
+ gap: 0 calc(var(--status-margin) / 2);
&.-multiline {
grid-template-columns: max-content minmax(min-content, 1fr);
- grid-template-rows: 1.1em 1.1em;
+ grid-template-rows: var(--__header-height) var(--__header-height);
grid-template-areas:
"title misc"
"handle handle";
}
.user-title,
.user-handle {
white-space: nowrap;
}
.user-title {
grid-area: title;
font-weight: bold;
- overflow-x: hidden;
- text-overflow: ellipsis;
+ overflow-x: clip;
+ text-overflow: clip;
+ min-width: 3em;
.emoji {
vertical-align: top;
}
--_still_image-label-scale: 0.25;
--emoji-size: 1em;
}
.user-handle {
grid-area: handle;
display: flex;
min-width: 1em;
.user-profile-link {
flex: 0 1 auto;
overflow-x: hidden;
text-overflow: ellipsis;
&::after {
content: ' ';
display: inline-block;
width: 0.25em;
}
}
.instance-favicon {
flex: 0 0 auto;
height: 1.1em;
width: 1.1em;
min-width: 1.1em;
object-fit: contain;
vertical-align: middle;
}
}
.misc {
grid-area: misc;
flex: 0 0 auto;
display: flex;
word-break: keep-all;
justify-self: end;
.visibility-icon,
.button-unstyled {
margin: calc(var(--status-margin) / -2) 0;
padding: calc(var(--status-margin) / 2);
&:last-child {
margin-right: calc(var(--status-margin) / -2);
}
}
}
}
diff --git a/src/components/status/activity-layout.scss b/src/components/status/activity-layout.scss
index c7d5f37011..6d95946be2 100644
--- a/src/components/status/activity-layout.scss
+++ b/src/components/status/activity-layout.scss
@@ -1,75 +1,83 @@
// This code is shared between Status and Notification
.activity-layout {
display: grid;
grid-template-columns: auto 1fr;
grid-template-rows: auto auto auto auto;
grid-template-areas:
"avatar header"
"avatar subheader"
"avatar content"
"avatar content"
"actions actions";
gap: calc(var(--status-margin) / 2) var(--status-margin);
overflow: hidden;
padding-top: var(--status-margin);
padding-left: var(--status-margin);
padding-right: var(--status-margin);
+ --__header-line-height: 1.2;
+ --__header-height: calc(var(--__header-line-height) * 1em);
+ --_avatar-size: calc(var(--__header-height) * 3);
+
&.-width-efficient {
grid-template-areas:
"avatar header"
"avatar subheader"
"content content"
"content content"
"actions actions";
&.-compact {
+ --_avatar-size: calc(var(--__header-height) * 2);
+
grid-template-areas:
"avatar header"
"avatar header"
"subheader subheader"
"content content"
"actions actions";
}
}
/* action buttons have HUGE height so we don't need as much margin at bottom */
padding-bottom: calc(var(--status-margin) / 2);
&:hover .animated.Avatar {
canvas {
display: none;
}
img {
visibility: visible;
}
}
.activity-avatar {
grid-area: avatar;
+ align-self: start;
+ display: flex;
min-width: 0;
}
.activity-header {
grid-area: header;
min-width: 0;
}
.activity-subheader {
grid-area: subheader;
display: grid;
min-width: 0;
}
.activity-content {
grid-area: content;
padding: calc(var(--status-margin) / 2) 0;
min-width: 0;
}
.activity-actions {
grid-area: actions;
min-width: 0;
}
}
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
index 03983add6b..342ae94609 100644
--- a/src/components/status/status.vue
+++ b/src/components/status/status.vue
@@ -1,472 +1,468 @@
<template>
<article
v-if="!hideStatus"
ref="root"
class="Status"
:class="rootClasses"
:aria-label="mainStatus.summary || $t('status.unsubjected')"
>
<div
v-if="error"
class="alert error"
>
{{ error }}
<button
class="fa-scale-110 fa-old-padding"
type="button"
@click="clearError"
>
<FAIcon icon="times" />
</button>
</div>
<template v-if="muted && !isPreview">
<div class="muted">
<small class="status-username">
<FAIcon
v-if="muted && isRepeat"
class="fa-scale-110 fa-old-padding repeat-icon"
icon="retweet"
/>
<UserLink
:user="repeater"
:at="false"
/>
</small>
<small class="mute-reason">
{{ muteLocalized }}
</small>
<button
class="unmute button-unstyled"
@click.prevent="toggleMute"
:title="$t('status.show_muted_status')"
>
<FAIcon
icon="eye-slash"
class="fa-scale-110 fa-old-padding"
/>
</button>
</div>
</template>
<template v-else>
<div
v-if="isRepeat && !inConversation"
:class="[repeaterClass, { highlighted: repeaterStyle }]"
:style="[repeaterStyle]"
class="repeat-info"
>
<UserAvatar
class="repeater-avatar"
:user-id="repeater.id"
/>
<bdi
class="faint status-username repeater-name"
:title="repeaterName"
>
<router-link
v-if="repeaterHtml"
:to="repeaterProfileLink"
>
<RichContent
:html="repeaterHtml"
:emoji="repeater.emoji"
:allow-non-square-emoji="allowNonSquareEmoji"
:pause-mfm="pauseMfm"
:scale-mfm="scaleMfm"
:is-local="repeater.is_local"
/>
</router-link>
<router-link
v-else
:to="repeaterProfileLink"
>{{ repeaterName }}</router-link>
</bdi>
{{ ' ' }}
<span class="repeat-label">
<FAIcon
icon="retweet"
class="repeat-icon"
:title="$t('tool_tip.repeat')"
/>
{{ $t('timeline.repeated') }}
</span>
</div>
<div
v-if="!isDeleted"
:class="[userClass, { highlighted: userStyle, '-repeat': isRepeat && !inConversation, '-width-efficient': shouldBeWidthEfficient, '-compact': compact }]"
:style="[ userStyle ]"
class="activity-layout"
:data-tags="tags"
>
- <a
+ <UserPopover
v-if="user.name"
- :href="$router.resolve(userProfileLink).href"
class="activity-avatar"
- @click.prevent
+ :user-id="user.id"
+ overlay-centers
+ link
>
- <UserPopover
+ <UserAvatar
+ class="post-avatar"
+ :compact="compact"
:user-id="user.id"
- :overlay-centers="true"
- >
- <UserAvatar
- class="post-avatar"
- :compact="compact"
- :user-id="user.id"
- />
- </UserPopover>
- </a>
+ />
+ </UserPopover>
<UserAvatar
v-else
:user-id="user.id"
class="activity-avatar"
:compact="compact"
:title="$t('status.unknown_user_info')"
/>
<ActivityHeader
class="activity-header"
:status-id="mainStatus.id"
:user-id="user.id"
:unmuted="unmuted"
:time="mainStatus.created_at"
:replies="replies"
:multiline="shouldBeWidthEfficient"
:compact="compact"
:expandable="expandable"
:expanded="isExpanded"
:thread-display-state="threadDisplayState"
@toggle-muted="toggleMute"
@toggle-expanded="$emit('toggleExpanded')"
@toggle-thread-display="$emit('toggleThreadDisplay')"
@dive="$emit('dive')"
/>
<div class="activity-subheader">
<div
v-if="scrobblePresent"
class="status-rich-presence"
>
<a
v-if="scrobble.externalLink"
:href="scrobble.externalLink"
target="_blank"
>
{{ scrobble.artist }} — {{ scrobble.title }}
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="play"
/>
<span class="status-rich-presence-time">
<Timeago
template-key="time.in_past"
:time="scrobble.created_at"
:auto-update="60"
/>
</span>
</a>
<span v-if="!scrobble.externalLink">
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="music"
/>
{{ scrobble.artist }} — {{ scrobble.title }}
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="play"
/>
<span class="status-rich-presence-time">
<Timeago
template-key="time.in_past"
:time="scrobble.created_at"
:auto-update="60"
/>
</span>
</span>
</div>
<div
v-if="isReply || hasMentionsLine"
class="heading-reply-row"
>
<span
v-if="isReply"
class="glued-label reply-glued-label"
>
<i18n-t
keypath="status.reply_to_with_arg"
scope="global"
>
<template #replyToWithIcon>
<StatusPopover
v-if="!isPreview"
:status-id="mainStatus.parent_visible && mainStatus.in_reply_to_status_id"
class="reply-to-popover"
style="min-width: 0;"
:class="{ '-strikethrough': !mainStatus.parent_visible }"
:aria-label="$t('tool_tip.reply')"
:link="isExpanded"
>
<i18n-t
keypath="status.reply_to_with_icon"
scope="global"
>
<template #icon>
<FAIcon
- class="fa-scale-110 fa-old-padding"
+ class="fa-scale-110"
icon="reply"
flip="horizontal"
/>
</template>
<template #replyTo>
<span
class="reply-to-text"
>
{{ $t('status.reply_to') }}
</span>
</template>
</i18n-t>
</StatusPopover>
<span
v-else
class="reply-to-no-popover"
>
<span class="reply-to-text">{{ $t('status.reply_to') }}</span>
</span>
</template>
<template #user>
<MentionLink
:content="replyToName"
:url="replyProfileLink"
:user-id="status.in_reply_to_user_id"
:user-screen-name="status.in_reply_to_screen_name"
/>
</template>
</i18n-t>
</span>
<!-- This little wrapper is made for sole purpose of "gluing" -->
<!-- "Mentions" label to the first mention -->
<span
v-if="hasMentionsLine"
class="glued-label"
>
<span
class="mentions"
:aria-label="$t('tool_tip.mentions')"
@click.prevent="gotoOriginal(status.in_reply_to_status_id)"
>
<span
class="mentions-text"
>
{{ $t('status.mentions') }}
</span>
</span>
<MentionsLine
v-if="hasMentionsLine"
:mentions="mentionsLine.slice(0, 1)"
class="mentions-line-first"
/>
</span>
{{ ' ' }}
<MentionsLine
v-if="hasMentionsLine"
:mentions="mentionsLine.slice(1)"
class="mentions-line"
/>
</div>
<div
v-if="isEdited && editingAvailable && !isPreview"
class="heading-edited-row"
>
<i18n-t
scope="global"
keypath="status.edited_at"
tag="span"
>
<template #time>
<Timeago
template-key="time.in_past"
:time="mainStatus.edited_at"
:auto-update="60"
:long-format="true"
/>
</template>
</i18n-t>
</div>
</div>
<div
class="activity-content"
>
<StatusContent
ref="content"
:status="mainStatus"
:focused="focused"
:in-conversation="inConversation"
@mediaplay="addMediaPlaying($event)"
@mediapause="removeMediaPlaying($event)"
@parse-ready="setHeadTailLinks"
/>
<Quote
:status-id="quoteId"
:status-url="quoteUrl"
:status-visible="quoteVisible"
:initially-expanded="quoteExpanded"
/>
<div
v-if="inConversation && !isPreview && replies?.size"
class="replies"
>
<button
v-if="showOtherRepliesInside && replies.size > 1"
class="button-unstyled -link"
:title="$t('status.ancestor_follow', { numReplies: replies.size - 1 }, replies.size - 1)"
@click.prevent="$emit('dive')"
>
{{ $t('status.replies_list_with_others', { numReplies: replies.size - 1 }, replies.size - 1) }}
</button>
<span
v-else
role="header"
:aria-level="headerLevel"
class="faint"
>
{{ $t('status.replies_list') }}
</span>
<span role="list" :aria-label="$t('status.replies_list')">
<span
v-for="reply in replies.values()"
:key="reply.id"
role="listitem"
:aria-label="reply.name"
>
<StatusPopover
:status-id="reply.id"
class="reply-link"
link
>
{{ reply.name }}
</StatusPopover>
{{ ' ' }}
</span>
</span>
</div>
<Transition name="fade">
<div
v-if="shouldDisplayFavsAndRepeats"
class="favs-repeated-users"
>
<div class="stats">
<UserListPopover
v-if="repeatedBy.size > 0"
:user-ids="repeatedBy"
>
<div class="stat-count">
<a class="stat-title">{{ $t('status.repeats') }}</a>
<div class="stat-number">
{{ repeatedBy.size }}
</div>
</div>
</UserListPopover>
<UserListPopover
v-if="favoritedBy.size > 0"
:user-ids="favoritedBy"
>
<div
class="stat-count"
>
<a class="stat-title">{{ $t('status.favorites') }}</a>
<div class="stat-number">
{{ favoritedBy.size }}
</div>
</div>
</UserListPopover>
<router-link
v-if="mainStatus.quotes_count > 0"
:to="{ name: 'quotes', params: { id: status.id } }"
>
<div
class="stat-count"
>
<a class="stat-title">{{ $t('status.quotes') }}</a>
<div class="stat-number">
{{ mainStatus.quotes_count }}
</div>
</div>
</router-link>
<div class="avatar-row">
<AvatarList :user-ids="combinedFavsAndRepeatsUsers" />
</div>
</div>
</div>
</Transition>
<EmojiReactions
v-if="(mergedConfig.emojiReactionsOnTimeline || focused) && (!isPreview)"
:status="mainStatus"
/>
</div>
<StatusActionButtons
v-if="!isPreview"
class="activity-actions"
:status="mainStatus"
:replying="replying"
@toggle-replying="toggleReplyForm"
/>
</div>
<div
v-else
class="gravestone"
>
<UserAvatar
class="post-avatar"
:compact="compact"
/>
<div class="deleted-text">
{{ $t('status.status_deleted') }}
</div>
</div>
<div
v-if="replying"
class="reply-form"
>
<PostStatusForm
ref="postStatusForm"
class="reply-body"
:closeable="true"
:replied-status="mainStatus"
@posted="closeReplyForm"
@draft-done="closeReplyForm"
@close-accepted="closeReplyForm"
/>
</div>
<i18n-t
v-if="inConversation && conversationRank === 'ancestor' && !isPreview && showOtherRepliesBelow && replies?.size > 1"
tag="button"
scope="global"
keypath="status.ancestor_follow_with_icon"
class="button-unstyled -link thread-tree-show-replies-button"
@click.prevent="$emit('dive')"
>
<template #icon>
<FAIcon
icon="angle-double-right"
/>
</template>
<template #text>
<span>
{{ $t('status.ancestor_follow', { numReplies: replies.size - 1 }) }}
</span>
</template>
</i18n-t>
</template>
</article>
</template>
<script src="./status.js"></script>
<style src="./status.scss" lang="scss"></style>
<style src="./activity-layout.scss" lang="scss"></style>
diff --git a/src/components/status_body/status_body.scss b/src/components/status_body/status_body.scss
index a475c0e776..a896d1c53c 100644
--- a/src/components/status_body/status_body.scss
+++ b/src/components/status_body/status_body.scss
@@ -1,216 +1,217 @@
.StatusBody {
display: flex;
flex-direction: column;
.emoji {
--_still_image-label-scale: 0.5;
}
.attachments {
margin-top: 0.5em;
}
& .text,
& .summary {
white-space: pre-wrap;
overflow-wrap: break-word;
text-wrap: pretty;
line-height: var(--post-line-height);
}
.summary {
display: block;
font-style: italic;
margin: 0;
font-size: 1rem;
font-weight: normal;
}
.text {
&.-single-line {
white-space-collapse: collapse;
text-overflow: ellipsis;
overflow: hidden;
height: 1.4em;
}
}
.summary-wrapper {
margin-top: 0;
margin-bottom: 0.5em;
border-style: solid;
border-width: 0 0 1px;
border-color: var(--border);
flex-grow: 0;
&.-tall {
position: relative;
.summary {
max-height: 2em;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
.text-wrapper {
position: relative;
text-overflow: ellipsis;
overflow-wrap: break-word;
overflow: hidden;
display: flex;
flex-flow: column nowrap;
+ padding: 0.1em 0;
&.-tall-status {
z-index: 1;
&:not(.-hidden) {
height: 16em;
}
.media-body {
min-height: 0;
mask:
linear-gradient(to top, white, transparent) bottom/100% 8em no-repeat,
linear-gradient(to top, white, white);
/* Autoprefixed seem to ignore this one, and also syntax is different */
/* stylelint-disable mask-composite */
/* stylelint-disable declaration-property-value-no-unknown */
/* stylelint-disable scss/declaration-property-value-no-unknown */
/* TODO check if this is still needed */
mask-composite: xor;
/* stylelint-enable scss/declaration-property-value-no-unknown */
/* stylelint-enable declaration-property-value-no-unknown */
/* stylelint-enable mask-composite */
mask-composite: exclude;
}
}
&.-expanded {
overflow: visible;
}
}
&.-single-line {
.summary-wrapper {
display: inline-block;
margin: 0;
padding: 0;
border: none;
.summary {
padding: 0;
}
}
.text-wrapper {
display: inline-flex;
}
}
& .tall-status-hider,
& .tall-subject-hider,
& .status-unhider,
& .cw-status-hider {
display: inline-block;
overflow-wrap: break-word;
text-wrap: pretty;
width: 100%;
text-align: center;
margin: 0.1em 0;
}
.status-unhider {
margin-top: auto;
position: sticky;
bottom: 0;
padding-bottom: 1em;
}
.tall-subject-hider {
// position: absolute;
padding-bottom: 0.5em;
&:not(.cw-status-hider) {
position: absolute;
margin-top: 10em;
height: 5em;
line-height: 8em;
z-index: 2;
}
}
& .status-unhider,
& .cw-status-hider {
overflow-wrap: break-word;
text-wrap: pretty;
svg {
color: inherit;
}
}
.toggle-button {
padding: 0.5em;
}
&.-compact {
align-items: start;
flex-direction: row;
& .body,
& .attachments {
max-height: 3.25em;
}
.body {
overflow: hidden;
white-space: normal;
min-width: 5em;
flex: 5 1 auto;
mask-size: auto 3.5em, auto auto;
mask-position: 0 0, 0 0;
mask-repeat: repeat-x, repeat;
mask-image: linear-gradient(to bottom, white 2em, transparent 3em);
/* Autoprefixed seem to ignore this one, and also syntax is different */
/* stylelint-disable mask-composite */
/* stylelint-disable declaration-property-value-no-unknown */
/* stylelint-disable scss/declaration-property-value-no-unknown */
/* TODO check if this is still needed */
mask-composite: xor;
/* stylelint-enable scss/declaration-property-value-no-unknown */
/* stylelint-enable declaration-property-value-no-unknown */
/* stylelint-enable mask-composite */
mask-composite: exclude;
}
.attachments {
margin-top: 0;
flex: 1 1 0;
min-width: 5em;
height: 100%;
margin-left: 0.5em;
}
.summary-wrapper {
.summary::after {
content: ": ";
}
line-height: inherit;
margin: 0;
border: none;
}
.text-wrapper {
display: inline-block;
width: 100%;
}
}
}
diff --git a/src/components/still-image/still-image-emoji-popover.vue b/src/components/still-image/still-image-emoji-popover.vue
index f80f111f75..19b69aedad 100644
--- a/src/components/still-image/still-image-emoji-popover.vue
+++ b/src/components/still-image/still-image-emoji-popover.vue
@@ -1,97 +1,107 @@
<template>
<Popover
ref="emojiPopover"
+ class="emoji-trigger"
trigger="click"
placement="top"
:bound-to="{ x: 'container' }"
:offset="{ y: 10 }"
@show="fetchEmojiPacksIfAdmin"
>
<template #trigger>
<StillImage v-bind="$attrs" />
</template>
<template #content>
<div class="emoji-popover">
<h3>{{ $attrs.title }}</h3>
<div class="emoji-popover-centered">
<StillImage
class="emoji"
v-bind="$attrs"
/>
</div>
<template v-if="isUserAdmin && !isLocal">
<span
v-if="adminPacksLocalLoading"
class="loading-spinner"
>
<FAIcon
class="fa-old-padding"
spin
icon="circle-notch"
/>
</span>
<div v-else>
<button
class="button button-default btn emoji-popover-button"
type="button"
:disabled="packName == ''"
@click="copyToLocalPack"
>
{{ $t('admin_dash.emoji.copy_to_pack') }}
</button>
<SelectComponent
v-model="packName"
>
<option
value=""
disabled
hidden
>
{{ $t('admin_dash.emoji.emoji_pack') }}
</option>
<option
v-for="(pack, listPackName) in adminPacksLocal"
:key="listPackName"
:label="listPackName"
>
{{ listPackName }}
</option>
</SelectComponent>
</div>
</template>
</div>
</template>
</Popover>
</template>
<script src="./still-image-emoji-popover" />
<style>
- .emoji-popover {
- margin: 0 0.5em 0.5em;
- text-align: center;
+.emoji-trigger {
+ border-radius: var(--roundness);
- .emoji {
- width: calc(var(--emoji-size) * 3);
- height: calc(var(--emoji-size) * 3);
- }
+ &:focus,
+ &:focus-visible {
+ box-shadow: 0 0 0 2px var(--accent);
+ }
+}
+
+.emoji-popover {
+ margin: 0 0.5em 0.5em;
+ text-align: center;
+
+ .emoji {
+ width: calc(var(--emoji-size) * 3);
+ height: calc(var(--emoji-size) * 3);
+ }
- .emoji-popover-centered {
- display: flex;
- align-items: center;
- justify-content: center;
- }
+ .emoji-popover-centered {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
- .emoji-popover-button {
- width: 100%;
- margin-top: 0.5em;
- }
+ .emoji-popover-button {
+ width: 100%;
+ margin-top: 0.5em;
+ }
- .Select {
- width: 100%;
- }
+ .Select {
+ width: 100%;
}
+}
</style>
diff --git a/src/components/user_avatar/user_avatar.vue b/src/components/user_avatar/user_avatar.vue
index d9e96d52dd..81ccc6dd8b 100644
--- a/src/components/user_avatar/user_avatar.vue
+++ b/src/components/user_avatar/user_avatar.vue
@@ -1,92 +1,92 @@
<template>
<span
class="Avatar"
:class="{ '-compact': compact }"
>
<StillImage
v-if="user"
class="avatar"
:alt="user.screen_name_ui"
:title="user.screen_name_ui"
:src="url ? url : imgSrc(user.profile_image_url_original)"
:image-load-error="imageLoadError"
:class="{ '-compact': compact, '-better-shadow': betterShadow }"
/>
<div
v-else
class="avatar -placeholder"
:class="{ '-compact': compact }"
/>
<FAIcon
v-if="showActorTypeIndicator && user?.actor_type === 'Service'"
icon="robot"
class="actor-type-indicator"
/>
<FAIcon
v-if="showActorTypeIndicator && user?.actor_type === 'Group'"
icon="people-group"
class="actor-type-indicator"
/>
</span>
</template>
<script src="./user_avatar.js"></script>
<style lang="scss">
.Avatar {
--_avatarShadowBox: var(--shadow);
--_avatarShadowFilter: var(--shadowFilter);
--_avatarShadowInset: var(--shadowInset);
--_still-image-label-visibility: hidden;
display: inline-block;
position: relative;
- width: 3.5em;
- height: 3.5em;
+ width: var(--_avatar-size, 3.5em);
+ height: var(--_avatar-size, 3.5em);
&.-compact {
- width: 2.2em;
- height: 2.2em;
+ width: var(--_avatar-size, 2.2em);
+ height: var(--_avatar-size, 2.2em);
border-radius: var(--roundness);
}
.avatar {
width: 100%;
height: 100%;
box-shadow: var(--_avatarShadowBox);
border-radius: var(--roundness);
&.-better-shadow {
box-shadow: var(--_avatarShadowInset);
filter: var(--_avatarShadowFilter);
}
&.-animated::before {
display: none;
}
&.-compact {
border-radius: var(--roundness);
}
&.-placeholder {
background-color: var(--background);
}
}
img {
width: 100%;
height: 100%;
}
.actor-type-indicator {
position: absolute;
bottom: 0;
right: 0;
margin: -0.2em;
padding: 0.2em;
background: rgb(127 127 127 / 50%);
color: #fff;
border-radius: var(--roundness);
}
}
</style>
diff --git a/src/components/user_popover/user_popover.js b/src/components/user_popover/user_popover.js
index 6a9abf11ea..25281e5e09 100644
--- a/src/components/user_popover/user_popover.js
+++ b/src/components/user_popover/user_popover.js
@@ -1,22 +1,46 @@
import { mapState } from 'pinia'
import Popover from 'src/components/popover/popover.vue'
import UserCard from 'src/components/user_card/user_card.vue'
+import { useInstanceStore } from 'src/stores/instance.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
+import { useUsersStore } from 'src/stores/users.js'
+
+import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
const UserPopover = {
name: 'UserPopover',
- props: ['userId', 'overlayCenters', 'overlayCentersSelector'],
+ props: {
+ userId: String,
+ overlayCenters: Boolean,
+ disabled: Boolean,
+ link: Boolean,
+ overlayCentersSelector: String,
+ },
components: {
UserCard,
Popover,
},
- computed: mapState(useMergedConfigStore, {
- userPopoverAvatarAction: (state) =>
- state.mergedConfig.userPopoverAvatarAction,
- userPopoverOverlay: (state) => state.mergedConfig.userPopoverOverlay,
- }),
+ computed: {
+ user() {
+ return this.userObject ?? useUsersStore().findUser(this.userId)
+ },
+ href() {
+ return this.$router.resolve(
+ generateProfileLink(
+ this.user.id,
+ this.user.screen_name,
+ useInstanceStore().restrictedNicknames,
+ ),
+ ).href
+ },
+ ...mapState(useMergedConfigStore, {
+ userPopoverAvatarAction: (state) =>
+ state.mergedConfig.userPopoverAvatarAction,
+ userPopoverOverlay: (state) => state.mergedConfig.userPopoverOverlay,
+ }),
+ },
}
export default UserPopover
diff --git a/src/components/user_popover/user_popover.vue b/src/components/user_popover/user_popover.vue
index 888c928482..803caa4dd4 100644
--- a/src/components/user_popover/user_popover.vue
+++ b/src/components/user_popover/user_popover.vue
@@ -1,54 +1,56 @@
<template>
<Popover
trigger="click"
popover-class="popover-default user-popover"
:overlay-centers-selector="overlayCentersSelector || '.user-info .Avatar'"
:overlay-centers="overlayCenters && userPopoverOverlay"
+ :link="link"
+ :href="href"
v-bind="$attrs"
>
<template #trigger>
<slot />
</template>
<template #content="{close}">
<UserCard
class="user-popover"
:compact="true"
:show-close="true"
:show-expand="true"
:user-id="userId"
:hide-bio="true"
:avatar-action="userPopoverAvatarAction == 'close' ? close : userPopoverAvatarAction"
@close="close"
/>
</template>
</Popover>
</template>
<script src="./user_popover.js"></script>
<style lang="scss">
/* popover styles load on-demand, so we need to override */
/* stylelint-disable block-no-empty */
.user-popover {
margin: 0;
.user-info {
width: 100%;
margin: 1.2em;
}
.user-identity {
min-width: calc(min(30em, 98vw));
}
.user-card-inner {
display: flex;
}
&.popover {
overflow: hidden;
}
}
/* stylelint-enable block-no-empty */
</style>
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Oct 12, 9:15 PM (1 d, 19 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1786484
Default Alt Text
(33 KB)
Attached To
Mode
rPUFE pleroma-fe-upstream
Attached
Detach File
Event Timeline
Log In to Comment