akkoma-fe/src/components/status/status.vue

726 lines
19 KiB
Vue
Raw Normal View History

2018-04-09 16:43:31 +00:00
<template>
2019-02-08 13:17:20 +00:00
<div class="status-el" v-if="!hideStatus" :class="[{ 'status-el_focused': isFocused }, { 'status-conversation': inlineExpanded }]">
<template v-if="muted && !isPreview">
2018-04-09 16:43:31 +00:00
<div class="media status container muted">
<small>
<router-link :to="userProfileLink">
{{status.user.screen_name}}
</router-link>
</small>
2018-04-09 16:43:31 +00:00
<small class="muteWords">{{muteWordHits.join(', ')}}</small>
2018-12-18 18:26:14 +00:00
<a href="#" class="unmute" @click.prevent="toggleMute"><i class="button-icon icon-eye-off"></i></a>
2018-04-09 16:43:31 +00:00
</div>
</template>
<template v-else>
<div v-if="pinned" class="status-pin">
<i class="fa icon-pin faint"></i>
<span class="faint">Pinned</span>
<div class="button-icon button-action-icon" v-if="status.pinned && ownStatus" @click.prevent="unpinStatus" title="Unpin">
<i class="fa icon-cancel"></i>
</div>
</div>
<div v-if="retweet && !noHeading && !inConversation" :class="[repeaterClass, { highlighted: repeaterStyle }]" :style="[repeaterStyle]" class="media container retweet-info">
<UserAvatar class="media-left" v-if="retweet" :betterShadow="betterShadow" :user="statusoid.user"/>
<div class="media-body faint">
<span class="user-name">
2019-02-06 20:17:06 +00:00
<router-link v-if="retweeterHtml" :to="retweeterProfileLink" v-html="retweeterHtml"/>
<router-link v-else :to="retweeterProfileLink">{{retweeter}}</router-link>
</span>
2018-12-03 17:12:43 +00:00
<i class='fa icon-retweet retweeted' :title="$t('tool_tip.repeat')"></i>
2018-04-09 16:43:31 +00:00
{{$t('timeline.repeated')}}
</div>
</div>
<div :class="[userClass, { highlighted: userStyle, 'is-retweet': retweet && !inConversation }]" :style="[ userStyle ]" class="media status">
2018-04-09 16:43:31 +00:00
<div v-if="!noHeading" class="media-left">
<router-link :to="userProfileLink" @click.stop.prevent.capture.native="toggleUserExpanded">
<UserAvatar :compact="compact" :betterShadow="betterShadow" :user="status.user"/>
</router-link>
2018-04-09 16:43:31 +00:00
</div>
<div class="status-body">
2019-03-06 02:48:07 +00:00
<UserCard :user="status.user" :rounded="true" :bordered="true" class="status-usercard" v-if="userExpanded"/>
<div v-if="!noHeading" class="media-heading">
<div class="heading-name-row">
<div class="name-and-account-name">
<h4 class="user-name" v-if="status.user.name_html" v-html="status.user.name_html"></h4>
<h4 class="user-name" v-else>{{status.user.name}}</h4>
<router-link class="account-name" :to="userProfileLink">
{{status.user.screen_name}}
</router-link>
</div>
<span class="heading-right">
<router-link class="timeago faint-link" :to="{ name: 'conversation', params: { id: status.id } }">
<timeago :since="status.created_at" :auto-update="60"></timeago>
</router-link>
<div class="button-icon visibility-icon" v-if="status.visibility">
<i :class="visibilityIcon(status.visibility)" :title="status.visibility | capitalize"></i>
</div>
<a :href="status.external_url" target="_blank" v-if="!status.is_local && !isPreview" class="source_url" title="Source">
<i class="button-icon icon-link-ext-alt"></i>
</a>
2019-04-04 16:56:13 +00:00
<div class="button-icon button-action-icon" v-if="!status.pinned && ownStatus" @click.prevent="pinStatus" title="Pin">
2019-04-04 16:47:25 +00:00
<i class="fa icon-pin"></i>
</div>
<div class="button-icon button-action-icon" v-if="expandable && !isPreview" @click.prevent="toggleExpanded" title="Expand">
<i class="button-icon icon-plus-squared"></i>
</div>
<div class="button-icon button-action-icon" v-if="unmuted" @click.prevent="toggleMute" title="Toggle Mute">
<i class="button-icon icon-eye-off"></i>
</div>
</span>
</div>
<div class="heading-reply-row">
<div v-if="isReply" class="reply-to-and-accountname">
<a class="reply-to"
href="#" @click.prevent="gotoOriginal(status.in_reply_to_status_id)"
:aria-label="$t('tool_tip.reply')"
@mouseenter.prevent.stop="replyEnter(status.in_reply_to_status_id, $event)"
@mouseleave.prevent.stop="replyLeave()"
>
<i class="button-icon icon-reply" v-if="!isPreview"></i>
<span class="faint-link reply-to-text">{{$t('status.reply_to')}}</span>
</a>
<router-link :to="replyProfileLink">
{{replyToName}}
</router-link>
2019-03-02 18:39:04 +00:00
<span class="faint replies-separator" v-if="replies && replies.length">
-
2018-04-09 16:43:31 +00:00
</span>
</div>
<div class="replies" v-if="inConversation && !isPreview">
2019-03-02 18:39:04 +00:00
<span class="faint" v-if="replies && replies.length">{{$t('status.replies_list')}}</span>
<span class="reply-link faint" v-if="replies" v-for="reply in replies">
<a href="#" @click.prevent="gotoOriginal(reply.id)" @mouseenter="replyEnter(reply.id, $event)" @mouseout="replyLeave()">{{reply.name}}</a>
</span>
</div>
2018-04-09 16:43:31 +00:00
</div>
2018-04-09 16:43:31 +00:00
</div>
<div v-if="showPreview" class="status-preview-container">
<status class="status-preview"
v-if="preview"
:isPreview="true"
:statusoid="preview"
:compact=true
/>
<div v-else class="status-preview status-preview-loading">
<i class="icon-spin4 animate-spin"></i>
2018-04-09 16:43:31 +00:00
</div>
</div>
<div class="status-content-wrapper" :class="{ 'tall-status': !showingLongSubject }" v-if="longSubject">
2019-04-01 16:58:23 +00:00
<a class="tall-status-hider" :class="{ 'tall-status-hider_focused': isFocused }" v-if="!showingLongSubject" href="#" @click.prevent="showingLongSubject=true">{{$t("general.show_more")}}</a>
<div @click.prevent="linkClicked" class="status-content media-body" v-html="contentHtml"></div>
2019-04-01 16:58:23 +00:00
<a v-if="showingLongSubject" href="#" class="status-unhider" @click.prevent="showingLongSubject=false">{{$t("general.show_less")}}</a>
</div>
<div :class="{'tall-status': hideTallStatus}" class="status-content-wrapper" v-else>
2019-04-01 16:58:23 +00:00
<a class="tall-status-hider" :class="{ 'tall-status-hider_focused': isFocused }" v-if="hideTallStatus" href="#" @click.prevent="toggleShowMore">{{$t("general.show_more")}}</a>
<div @click.prevent="linkClicked" class="status-content media-body" v-html="contentHtml" v-if="!hideSubjectStatus"></div>
2019-01-13 13:38:00 +00:00
<div @click.prevent="linkClicked" class="status-content media-body" v-html="status.summary_html" v-else></div>
2019-04-01 16:58:23 +00:00
<a v-if="hideSubjectStatus" href="#" class="cw-status-hider" @click.prevent="toggleShowMore">{{$t("general.show_more")}}</a>
<a v-if="showingMore" href="#" class="status-unhider" @click.prevent="toggleShowMore">{{$t("general.show_less")}}</a>
2018-04-09 16:43:31 +00:00
</div>
<div v-if="status.attachments && (!hideSubjectStatus || showingLongSubject)" class="attachments media-body">
<attachment
2019-01-26 15:45:03 +00:00
class="non-gallery"
v-for="attachment in nonGalleryAttachments"
:size="attachmentSize"
:nsfw="nsfwClickthrough"
:attachment="attachment"
2019-01-26 15:45:03 +00:00
:allowPlay="true"
:setMedia="setMedia()"
:key="attachment.id"
/>
<gallery
v-if="galleryAttachments.length > 0"
:nsfw="nsfwClickthrough"
:attachments="galleryAttachments"
:setMedia="setMedia()"
/>
2018-04-09 16:43:31 +00:00
</div>
2019-01-27 20:33:36 +00:00
<div v-if="status.card && !hideSubjectStatus && !noHeading" class="link-preview media-body">
<link-preview :card="status.card" :size="attachmentSize" :nsfw="nsfwClickthrough" />
2019-01-27 13:47:30 +00:00
</div>
2019-04-06 18:54:23 +00:00
<transition name="fade">
2019-05-07 19:54:49 +00:00
<div class="favs-repeated-users" v-if="isFocused && combinedFavsAndRepeatsUsers.length > 0">
<div class="stats">
<div class="stat-count" v-if="statusFromGlobalRepository.rebloggedBy && statusFromGlobalRepository.rebloggedBy.length > 0">
2019-04-22 18:37:27 +00:00
<a class="stat-title">{{ $t('status.repeats') }}</a>
<div class="stat-number">{{ statusFromGlobalRepository.rebloggedBy.length }}</div>
</div>
<div class="stat-count" v-if="statusFromGlobalRepository.favoritedBy && statusFromGlobalRepository.favoritedBy.length > 0">
2019-04-22 18:37:27 +00:00
<a class="stat-title">{{ $t('status.favorites') }}</a>
<div class="stat-number">{{ statusFromGlobalRepository.favoritedBy.length }}</div>
</div>
<div class="avatar-row">
<AvatarList :users="combinedFavsAndRepeatsUsers"></AvatarList>
</div>
</div>
2019-04-06 18:54:23 +00:00
</div>
</transition>
<div v-if="!noHeading && !isPreview" class='status-actions media-body'>
<div>
<i class="button-icon icon-reply" v-on:click.prevent="toggleReplying" :title="$t('tool_tip.reply')" :class="{'button-icon-active': replying}" v-if="loggedIn"/>
<i class="button-icon button-icon-disabled icon-reply" :title="$t('tool_tip.reply')" v-else />
<span v-if="status.replies_count > 0">{{status.replies_count}}</span>
2018-04-09 16:43:31 +00:00
</div>
2018-08-09 16:47:08 +00:00
<retweet-button :visibility='status.visibility' :loggedIn='loggedIn' :status='status'></retweet-button>
2018-04-09 16:43:31 +00:00
<favorite-button :loggedIn='loggedIn' :status='status'></favorite-button>
<delete-button :status='status'></delete-button>
</div>
</div>
</div>
<div class="container" v-if="replying">
<div class="reply-left"/>
2018-09-25 12:16:26 +00:00
<post-status-form class="reply-body" :reply-to="status.id" :attentions="status.attentions" :repliedUser="status.user" :copy-message-scope="status.visibility" :subject="replySubject" v-on:posted="toggleReplying"/>
2018-04-09 16:43:31 +00:00
</div>
</template>
</div>
</template>
2016-10-28 13:19:42 +00:00
<script src="./status.js" ></script>
2016-10-28 23:38:41 +00:00
<style lang="scss">
2018-04-09 16:43:31 +00:00
@import '../../_variables.scss';
$status-margin: 0.75em;
2018-04-09 16:43:31 +00:00
.status-body {
flex: 1;
min-width: 0;
}
.status-preview.status-el {
border-style: solid;
border-width: 1px;
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
2018-04-09 16:43:31 +00:00
}
.status-preview-container {
position: relative;
max-width: 100%;
}
.status-pin {
padding: 0.75em 0.75em 0;
display: flex;
align-items: center;
justify-content: flex-end;
}
2018-04-09 16:43:31 +00:00
.status-preview {
position: absolute;
max-width: 95%;
display: flex;
2018-04-07 16:30:27 +00:00
background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg);
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
border-style: solid;
border-width: 1px;
border-radius: $fallback--tooltipRadius;
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
2018-04-09 16:43:31 +00:00
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
box-shadow: var(--popupShadow);
margin-top: 0.25em;
margin-left: 0.5em;
2018-04-09 16:43:31 +00:00
z-index: 50;
2018-04-09 16:43:31 +00:00
.status {
flex: 1;
border: 0;
min-width: 15em;
2018-04-09 16:43:31 +00:00
}
}
.status-preview-loading {
display: block;
min-width: 15em;
padding: 1em;
2018-04-09 16:43:31 +00:00
text-align: center;
border-width: 1px;
border-style: solid;
i {
font-size: 2em;
}
2018-04-09 16:43:31 +00:00
}
.media-left {
margin-right: $status-margin;
}
2018-04-09 16:43:31 +00:00
.status-el {
hyphens: auto;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-word;
border-left-width: 0px;
min-width: 0;
2018-04-07 16:30:27 +00:00
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
border-left: 4px $fallback--cRed;
border-left: 4px var(--cRed, $fallback--cRed);
2018-04-07 16:30:27 +00:00
&_focused {
background-color: $fallback--lightBg;
background-color: var(--lightBg, $fallback--lightBg);
}
2018-04-09 16:43:31 +00:00
.timeline & {
border-bottom-width: 1px;
border-bottom-style: solid;
}
2018-04-09 16:43:31 +00:00
.media-body {
flex: 1;
padding: 0;
2018-04-10 19:12:59 +00:00
}
2019-03-06 02:48:07 +00:00
.status-usercard {
margin-bottom: $status-margin;
}
.user-name {
white-space: nowrap;
font-size: 14px;
overflow: hidden;
flex-shrink: 0;
max-width: 85%;
font-weight: bold;
img {
width: 14px;
height: 14px;
vertical-align: middle;
object-fit: contain
}
}
.media-heading {
2018-04-09 16:43:31 +00:00
padding: 0;
vertical-align: bottom;
flex-basis: 100%;
margin-bottom: 0.5em;
2018-04-09 16:43:31 +00:00
2019-01-27 20:33:36 +00:00
a {
display: inline-block;
word-break: break-all;
}
2018-04-09 16:43:31 +00:00
small {
font-weight: lighter;
}
.heading-name-row {
2018-04-09 16:43:31 +00:00
padding: 0;
display: flex;
justify-content: space-between;
line-height: 18px;
.name-and-account-name {
display: flex;
min-width: 0;
}
.user-name {
flex-shrink: 1;
margin-right: 0.4em;
overflow: hidden;
text-overflow: ellipsis;
}
2018-08-13 12:36:10 +00:00
.account-name {
min-width: 1.6em;
margin-right: 0.4em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1 1 0;
}
2018-04-09 16:43:31 +00:00
}
.heading-right {
display: flex;
flex-shrink: 0;
}
.timeago {
margin-right: 0.2em;
}
.heading-reply-row {
align-content: baseline;
2018-04-09 16:43:31 +00:00
font-size: 12px;
line-height: 18px;
max-width: 100%;
display: flex;
flex-wrap: wrap;
align-items: stretch;
a {
max-width: 100%;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
.reply-to-and-accountname {
display: flex;
height: 18px;
margin-right: 0.5em;
overflow: hidden;
max-width: 100%;
.icon-reply {
transform: scaleX(-1);
}
2018-04-09 16:43:31 +00:00
}
.reply-info {
display: flex;
}
.reply-to {
display: flex;
2018-04-09 16:43:31 +00:00
}
.reply-to-text {
overflow: hidden;
text-overflow: ellipsis;
margin: 0 0.4em 0 0.2em;
2018-04-09 16:43:31 +00:00
}
.replies-separator {
margin-left: 0.4em;
}
.replies {
line-height: 18px;
2018-04-09 16:43:31 +00:00
font-size: 12px;
display: flex;
flex-wrap: wrap;
& > * {
margin-right: 0.4em;
}
2018-04-09 16:43:31 +00:00
}
.reply-link {
height: 17px;
}
2018-04-09 16:43:31 +00:00
}
.tall-status {
position: relative;
height: 220px;
overflow-x: hidden;
overflow-y: hidden;
}
.tall-status-hider {
2019-01-27 20:33:36 +00:00
display: inline-block;
word-break: break-all;
2018-04-09 16:43:31 +00:00
position: absolute;
height: 70px;
margin-top: 150px;
width: 100%;
text-align: center;
line-height: 110px;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), $fallback--bg 80%);
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), var(--bg, $fallback--bg) 80%);
&_focused {
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), $fallback--lightBg 80%);
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), var(--lightBg, $fallback--lightBg) 80%);
}
2018-04-09 16:43:31 +00:00
}
.status-unhider, .cw-status-hider {
2018-04-09 16:43:31 +00:00
width: 100%;
text-align: center;
2019-01-27 20:33:36 +00:00
display: inline-block;
word-break: break-all;
2018-04-09 16:43:31 +00:00
}
.status-content {
2018-11-25 19:06:49 +00:00
font-family: var(--postFont, sans-serif);
line-height: 1.4em;
2018-11-25 19:06:49 +00:00
2018-04-09 16:43:31 +00:00
img, video {
max-width: 100%;
max-height: 400px;
vertical-align: middle;
object-fit: contain;
2019-03-05 18:15:18 +00:00
&.emoji {
width: 32px;
height: 32px;
}
2018-04-09 16:43:31 +00:00
}
blockquote {
margin: 0.2em 0 0.2em 2em;
font-style: italic;
}
pre {
overflow: auto;
}
2018-11-25 21:23:07 +00:00
code, samp, kbd, var, pre {
font-family: var(--postCodeFont, monospace);
}
2018-04-09 16:43:31 +00:00
p {
margin: 0 0 1em 0;
}
p:last-child {
margin: 0 0 0 0;
2018-04-09 16:43:31 +00:00
}
h1 {
font-size: 1.1em;
line-height: 1.2em;
2018-08-31 13:13:43 +00:00
margin: 1.4em 0;
}
h2 {
2018-08-31 13:13:43 +00:00
font-size: 1.1em;
margin: 1.0em 0;
}
h3 {
font-size: 1em;
2018-08-31 13:13:43 +00:00
margin: 1.2em 0;
}
h4 {
2018-08-31 13:13:43 +00:00
margin: 1.1em 0;
}
2018-04-09 16:43:31 +00:00
}
2018-04-09 16:43:31 +00:00
.retweet-info {
padding: 0.4em $status-margin;
2018-06-18 08:36:58 +00:00
margin: 0;
.avatar.still-image {
border-radius: $fallback--avatarAltRadius;
border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
2018-04-09 16:43:31 +00:00
margin-left: 28px;
width: 20px;
height: 20px;
}
2018-04-09 16:43:31 +00:00
.media-body {
font-size: 1em;
line-height: 22px;
display: flex;
align-content: center;
flex-wrap: wrap;
.user-name {
font-weight: bold;
overflow: hidden;
text-overflow: ellipsis;
2018-08-13 12:36:10 +00:00
img {
width: 14px;
height: 14px;
vertical-align: middle;
2018-08-13 12:36:10 +00:00
object-fit: contain
}
}
i {
padding: 0 0.2em;
}
a {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
2018-04-09 16:43:31 +00:00
}
2019-04-08 18:06:18 +00:00
.status-fadein {
animation-duration: 0.4s;
animation-name: fadein;
}
@keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
2018-04-09 16:43:31 +00:00
.greentext {
color: green;
}
.status-conversation {
border-left-style: solid;
}
.status-actions {
width: 100%;
display: flex;
margin-top: $status-margin;
2018-04-09 16:43:31 +00:00
div, favorite-button {
max-width: 4em;
2018-04-09 16:43:31 +00:00
flex: 1;
}
}
.button-icon.icon-reply {
&:not(.button-icon-disabled):hover,
&.button-icon-active {
color: $fallback--cBlue;
color: var(--cBlue, $fallback--cBlue);
cursor: pointer;
}
2018-04-09 16:43:31 +00:00
}
.status:hover .animated.avatar {
canvas {
display: none;
}
img {
visibility: visible;
}
}
.status {
display: flex;
padding: $status-margin;
2018-06-18 08:36:58 +00:00
&.is-retweet {
padding-top: 0;
2018-06-18 08:36:58 +00:00
}
2018-04-09 16:43:31 +00:00
}
.status-conversation:last-child {
border-bottom: none;
}
.muted {
padding: 0.25em 0.5em;
button {
margin-left: auto;
}
.muteWords {
margin-left: 10px;
}
}
a.unmute {
display: block;
margin-left: auto;
}
.reply-left {
flex: 0;
min-width: 48px;
}
.reply-body {
flex: 1;
}
.timeline > {
.status-el:last-child {
2019-02-12 16:00:09 +00:00
border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius;
border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius);
2018-11-25 14:42:41 +00:00
border-bottom: none;
}
}
2019-04-05 07:08:40 +00:00
.favs-repeated-users {
margin-top: $status-margin;
2019-04-05 07:08:40 +00:00
2019-04-05 17:35:05 +00:00
.stats {
width: 100%;
display: flex;
2019-04-05 18:20:50 +00:00
line-height: 1em;
2019-04-05 17:35:05 +00:00
.stat-count {
2019-04-22 18:07:29 +00:00
margin-right: $status-margin;
2019-04-05 17:35:05 +00:00
2019-04-05 18:20:50 +00:00
.stat-title {
2019-04-05 18:55:19 +00:00
color: var(--faint, $fallback--faint);
2019-04-05 18:20:50 +00:00
font-size: 12px;
text-transform: uppercase;
position: relative;
}
.stat-number {
font-weight: bolder;
font-size: 16px;
line-height: 1em;
}
2019-04-05 17:35:05 +00:00
}
.avatar-row {
flex: 1;
overflow: hidden;
2019-04-05 18:20:50 +00:00
position: relative;
2019-04-22 18:07:29 +00:00
display: flex;
align-items: center;
2019-04-05 18:20:50 +00:00
&::before {
content: '';
position: absolute;
height: 100%;
width: 1px;
left: 0;
background-color: var(--faint, $fallback--faint);
}
2019-04-05 17:35:05 +00:00
}
2019-04-05 07:08:40 +00:00
}
2019-04-02 02:30:06 +00:00
}
2019-04-04 16:47:25 +00:00
.button-action-icon {
cursor: pointer;
}
2019-01-26 15:45:03 +00:00
@media all and (max-width: 800px) {
2018-04-09 16:43:31 +00:00
.status-el {
.retweet-info {
.avatar.still-image {
2018-04-09 16:43:31 +00:00
margin-left: 20px;
}
}
}
.status {
max-width: 100%;
}
.status .avatar.still-image {
2018-04-09 16:43:31 +00:00
width: 40px;
height: 40px;
&.avatar-compact {
width: 32px;
height: 32px;
}
}
2018-04-09 16:43:31 +00:00
}
2017-06-01 14:35:00 +00:00
2016-10-28 23:38:41 +00:00
</style>