Merge remote-tracking branch 'upstream/develop' into settings-refactor
* upstream/develop: (89 commits) remove needless ref show preview popover when hover numbered replies refactor conditions do not make too many nested div add fetchStatus action refactor status loading logic split status preview popover into a separate component uninstall mobile-detect library listen both events minor css fix restrict distance at top side only set different trigger event in desktop and mobile by default fix eslint warnings fix popper go behind the top bar migrate Popper to v-popover fix popper go behind the top bar fix eslint warnings reset font-size to normal text size using rem use top placement by default hide status preview popper when hover popper content ...
This commit is contained in:
commit
b66564a30d
67 changed files with 857 additions and 495 deletions
35
src/components/account_actions/account_actions.js
Normal file
35
src/components/account_actions/account_actions.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
import ProgressButton from '../progress_button/progress_button.vue'
|
||||
|
||||
const AccountActions = {
|
||||
props: [
|
||||
'user'
|
||||
],
|
||||
data () {
|
||||
return { }
|
||||
},
|
||||
components: {
|
||||
ProgressButton
|
||||
},
|
||||
methods: {
|
||||
showRepeats () {
|
||||
this.$store.dispatch('showReblogs', this.user.id)
|
||||
},
|
||||
hideRepeats () {
|
||||
this.$store.dispatch('hideReblogs', this.user.id)
|
||||
},
|
||||
blockUser () {
|
||||
this.$store.dispatch('blockUser', this.user.id)
|
||||
},
|
||||
unblockUser () {
|
||||
this.$store.dispatch('unblockUser', this.user.id)
|
||||
},
|
||||
reportUser () {
|
||||
this.$store.dispatch('openUserReportingModal', this.user.id)
|
||||
},
|
||||
mentionUser () {
|
||||
this.$store.dispatch('openPostStatusModal', { replyTo: true, repliedUser: this.user })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default AccountActions
|
93
src/components/account_actions/account_actions.vue
Normal file
93
src/components/account_actions/account_actions.vue
Normal file
|
@ -0,0 +1,93 @@
|
|||
<template>
|
||||
<div class="account-actions">
|
||||
<v-popover
|
||||
trigger="click"
|
||||
class="account-tools-popover"
|
||||
:container="false"
|
||||
placement="bottom-end"
|
||||
:offset="5"
|
||||
>
|
||||
<div slot="popover">
|
||||
<div class="dropdown-menu">
|
||||
<button
|
||||
class="btn btn-default btn-block dropdown-item"
|
||||
@click="mentionUser"
|
||||
>
|
||||
{{ $t('user_card.mention') }}
|
||||
</button>
|
||||
<template v-if="user.following">
|
||||
<div
|
||||
role="separator"
|
||||
class="dropdown-divider"
|
||||
/>
|
||||
<button
|
||||
v-if="user.showing_reblogs"
|
||||
class="btn btn-default dropdown-item"
|
||||
@click="hideRepeats"
|
||||
>
|
||||
{{ $t('user_card.hide_repeats') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="!user.showing_reblogs"
|
||||
class="btn btn-default dropdown-item"
|
||||
@click="showRepeats"
|
||||
>
|
||||
{{ $t('user_card.show_repeats') }}
|
||||
</button>
|
||||
</template>
|
||||
<div
|
||||
role="separator"
|
||||
class="dropdown-divider"
|
||||
/>
|
||||
<button
|
||||
v-if="user.statusnet_blocking"
|
||||
class="btn btn-default btn-block dropdown-item"
|
||||
@click="unblockUser"
|
||||
>
|
||||
{{ $t('user_card.unblock') }}
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="btn btn-default btn-block dropdown-item"
|
||||
@click="blockUser"
|
||||
>
|
||||
{{ $t('user_card.block') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-default btn-block dropdown-item"
|
||||
@click="reportUser"
|
||||
>
|
||||
{{ $t('user_card.report') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn btn-default ellipsis-button">
|
||||
<i class="icon-ellipsis trigger-button" />
|
||||
</div>
|
||||
</v-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./account_actions.js"></script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../_variables.scss';
|
||||
@import '../popper/popper.scss';
|
||||
.account-actions {
|
||||
margin: 0 .8em;
|
||||
}
|
||||
|
||||
.account-actions button.dropdown-item {
|
||||
margin-left: 0;
|
||||
}
|
||||
.account-actions .trigger-button {
|
||||
color: $fallback--lightText;
|
||||
color: var(--lightText, $fallback--lightText);
|
||||
opacity: .8;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: $fallback--text;
|
||||
color: var(--text, $fallback--text);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -10,7 +10,8 @@ const Attachment = {
|
|||
'statusId',
|
||||
'size',
|
||||
'allowPlay',
|
||||
'setMedia'
|
||||
'setMedia',
|
||||
'naturalSizeLoad'
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
|
@ -88,6 +89,11 @@ const Attachment = {
|
|||
} else {
|
||||
this.showHidden = !this.showHidden
|
||||
}
|
||||
},
|
||||
onImageLoad (image) {
|
||||
const width = image.naturalWidth
|
||||
const height = image.naturalHeight
|
||||
this.naturalSizeLoad && this.naturalSizeLoad({ width, height })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
:referrerpolicy="referrerpolicy"
|
||||
:mimetype="attachment.mimetype"
|
||||
:src="attachment.large_thumb_url || attachment.url"
|
||||
:image-load-handler="onImageLoad"
|
||||
/>
|
||||
</a>
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
trigger="click"
|
||||
placement="top"
|
||||
class="extra-button-popover"
|
||||
:offset="5"
|
||||
:container="false"
|
||||
>
|
||||
<div slot="popover">
|
||||
<div class="dropdown-menu">
|
||||
|
|
53
src/components/follow_button/follow_button.js
Normal file
53
src/components/follow_button/follow_button.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
|
||||
export default {
|
||||
props: ['user', 'labelFollowing', 'buttonClass'],
|
||||
data () {
|
||||
return {
|
||||
inProgress: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isPressed () {
|
||||
return this.inProgress || this.user.following
|
||||
},
|
||||
title () {
|
||||
if (this.inProgress || this.user.following) {
|
||||
return this.$t('user_card.follow_unfollow')
|
||||
} else if (this.user.requested) {
|
||||
return this.$t('user_card.follow_again')
|
||||
} else {
|
||||
return this.$t('user_card.follow')
|
||||
}
|
||||
},
|
||||
label () {
|
||||
if (this.inProgress) {
|
||||
return this.$t('user_card.follow_progress')
|
||||
} else if (this.user.following) {
|
||||
return this.labelFollowing || this.$t('user_card.following')
|
||||
} else if (this.user.requested) {
|
||||
return this.$t('user_card.follow_sent')
|
||||
} else {
|
||||
return this.$t('user_card.follow')
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClick () {
|
||||
this.user.following ? this.unfollow() : this.follow()
|
||||
},
|
||||
follow () {
|
||||
this.inProgress = true
|
||||
requestFollow(this.user, this.$store).then(() => {
|
||||
this.inProgress = false
|
||||
})
|
||||
},
|
||||
unfollow () {
|
||||
const store = this.$store
|
||||
this.inProgress = true
|
||||
requestUnfollow(this.user, store).then(() => {
|
||||
this.inProgress = false
|
||||
store.commit('removeStatus', { timeline: 'friends', userId: this.user.id })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
13
src/components/follow_button/follow_button.vue
Normal file
13
src/components/follow_button/follow_button.vue
Normal file
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
<button
|
||||
class="btn btn-default follow-button"
|
||||
:class="{ pressed: isPressed }"
|
||||
:disabled="inProgress"
|
||||
:title="title"
|
||||
@click="onClick"
|
||||
>
|
||||
{{ label }}
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script src="./follow_button.js"></script>
|
|
@ -1,21 +1,16 @@
|
|||
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
|
||||
import RemoteFollow from '../remote_follow/remote_follow.vue'
|
||||
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
|
||||
import FollowButton from '../follow_button/follow_button.vue'
|
||||
|
||||
const FollowCard = {
|
||||
props: [
|
||||
'user',
|
||||
'noFollowsYou'
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
inProgress: false,
|
||||
requestSent: false
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BasicUserCard,
|
||||
RemoteFollow
|
||||
RemoteFollow,
|
||||
FollowButton
|
||||
},
|
||||
computed: {
|
||||
isMe () {
|
||||
|
@ -24,21 +19,6 @@ const FollowCard = {
|
|||
loggedIn () {
|
||||
return this.$store.state.users.currentUser
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
followUser () {
|
||||
this.inProgress = true
|
||||
requestFollow(this.user, this.$store).then(({ sent }) => {
|
||||
this.inProgress = false
|
||||
this.requestSent = sent
|
||||
})
|
||||
},
|
||||
unfollowUser () {
|
||||
this.inProgress = true
|
||||
requestUnfollow(this.user, this.$store).then(() => {
|
||||
this.inProgress = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,36 +16,11 @@
|
|||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<button
|
||||
v-if="!user.following"
|
||||
class="btn btn-default follow-card-follow-button"
|
||||
:disabled="inProgress"
|
||||
:title="requestSent ? $t('user_card.follow_again') : ''"
|
||||
@click="followUser"
|
||||
>
|
||||
<template v-if="inProgress">
|
||||
{{ $t('user_card.follow_progress') }}
|
||||
</template>
|
||||
<template v-else-if="requestSent">
|
||||
{{ $t('user_card.follow_sent') }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ $t('user_card.follow') }}
|
||||
</template>
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="btn btn-default follow-card-follow-button pressed"
|
||||
:disabled="inProgress"
|
||||
@click="unfollowUser"
|
||||
>
|
||||
<template v-if="inProgress">
|
||||
{{ $t('user_card.follow_progress') }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ $t('user_card.follow_unfollow') }}
|
||||
</template>
|
||||
</button>
|
||||
<FollowButton
|
||||
:user="user"
|
||||
class="follow-card-follow-button"
|
||||
:label-following="$t('user_card.follow_unfollow')"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</basic-user-card>
|
||||
|
|
|
@ -1,23 +1,18 @@
|
|||
import Attachment from '../attachment/attachment.vue'
|
||||
import { chunk, last, dropRight } from 'lodash'
|
||||
import { chunk, last, dropRight, sumBy } from 'lodash'
|
||||
|
||||
const Gallery = {
|
||||
data: () => ({
|
||||
width: 500
|
||||
}),
|
||||
props: [
|
||||
'attachments',
|
||||
'nsfw',
|
||||
'setMedia'
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
sizes: {}
|
||||
}
|
||||
},
|
||||
components: { Attachment },
|
||||
mounted () {
|
||||
this.resize()
|
||||
window.addEventListener('resize', this.resize)
|
||||
},
|
||||
destroyed () {
|
||||
window.removeEventListener('resize', this.resize)
|
||||
},
|
||||
computed: {
|
||||
rows () {
|
||||
if (!this.attachments) {
|
||||
|
@ -33,21 +28,24 @@ const Gallery = {
|
|||
}
|
||||
return rows
|
||||
},
|
||||
rowHeight () {
|
||||
return itemsPerRow => ({ 'height': `${(this.width / (itemsPerRow + 0.6))}px` })
|
||||
},
|
||||
useContainFit () {
|
||||
return this.$store.getters.mergedConfig.useContainFit
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resize () {
|
||||
// Quick optimization to make resizing not always trigger state change,
|
||||
// only update attachment size in 10px steps
|
||||
const width = Math.floor(this.$el.getBoundingClientRect().width / 10) * 10
|
||||
if (this.width !== width) {
|
||||
this.width = width
|
||||
}
|
||||
onNaturalSizeLoad (id, size) {
|
||||
this.$set(this.sizes, id, size)
|
||||
},
|
||||
rowStyle (itemsPerRow) {
|
||||
return { 'padding-bottom': `${(100 / (itemsPerRow + 0.6))}%` }
|
||||
},
|
||||
itemStyle (id, row) {
|
||||
const total = sumBy(row, item => this.getAspectRatio(item.id))
|
||||
return { flex: `${this.getAspectRatio(id) / total} 1 0%` }
|
||||
},
|
||||
getAspectRatio (id) {
|
||||
const size = this.sizes[id]
|
||||
return size ? size.width / size.height : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,17 +7,21 @@
|
|||
v-for="(row, index) in rows"
|
||||
:key="index"
|
||||
class="gallery-row"
|
||||
:style="rowHeight(row.length)"
|
||||
:style="rowStyle(row.length)"
|
||||
:class="{ 'contain-fit': useContainFit, 'cover-fit': !useContainFit }"
|
||||
>
|
||||
<attachment
|
||||
v-for="attachment in row"
|
||||
:key="attachment.id"
|
||||
:set-media="setMedia"
|
||||
:nsfw="nsfw"
|
||||
:attachment="attachment"
|
||||
:allow-play="false"
|
||||
/>
|
||||
<div class="gallery-row-inner">
|
||||
<attachment
|
||||
v-for="attachment in row"
|
||||
:key="attachment.id"
|
||||
:set-media="setMedia"
|
||||
:nsfw="nsfw"
|
||||
:attachment="attachment"
|
||||
:allow-play="false"
|
||||
:natural-size-load="onNaturalSizeLoad.bind(null, attachment.id)"
|
||||
:style="itemStyle(attachment.id, row)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -28,15 +32,24 @@
|
|||
@import '../../_variables.scss';
|
||||
|
||||
.gallery-row {
|
||||
height: 200px;
|
||||
position: relative;
|
||||
height: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-content: stretch;
|
||||
flex-grow: 1;
|
||||
margin-top: 0.5em;
|
||||
|
||||
.gallery-row-inner {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-content: stretch;
|
||||
}
|
||||
|
||||
// FIXME: specificity problem with this and .attachments.attachment
|
||||
// we shouldn't have the need for .image here
|
||||
.attachment.image {
|
||||
|
|
|
@ -59,6 +59,8 @@ const LoginForm = {
|
|||
if (result.error) {
|
||||
if (result.error === 'mfa_required') {
|
||||
this.requireMFA({ app: app, settings: result })
|
||||
} else if (result.identifier === 'password_reset_required') {
|
||||
this.$router.push({ name: 'password-reset', params: { passwordResetRequested: true } })
|
||||
} else {
|
||||
this.error = result.error
|
||||
this.focusOnPasswordInput()
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import StillImage from '../still-image/still-image.vue'
|
||||
import VideoAttachment from '../video_attachment/video_attachment.vue'
|
||||
import Modal from '../modal/modal.vue'
|
||||
import fileTypeService from '../../services/file_type/file_type.service.js'
|
||||
|
||||
const MediaModal = {
|
||||
components: {
|
||||
StillImage,
|
||||
VideoAttachment
|
||||
VideoAttachment,
|
||||
Modal
|
||||
},
|
||||
computed: {
|
||||
showing () {
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
<template>
|
||||
<div
|
||||
<Modal
|
||||
v-if="showing"
|
||||
v-body-scroll-lock="showing"
|
||||
class="modal-view media-modal-view"
|
||||
@click.prevent="hide"
|
||||
class="media-modal-view"
|
||||
@backdropClicked="hide"
|
||||
>
|
||||
<img
|
||||
v-if="type === 'image'"
|
||||
|
@ -33,21 +32,15 @@
|
|||
>
|
||||
<i class="icon-right-open arrow-icon" />
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script src="./media_modal.js"></script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../_variables.scss';
|
||||
|
||||
.media-modal-view {
|
||||
.modal-view.media-modal-view {
|
||||
z-index: 1001;
|
||||
|
||||
body:not(.scroll-locked) & {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.modal-view-button-arrow {
|
||||
opacity: 0.75;
|
||||
|
@ -114,5 +107,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
52
src/components/modal/modal.vue
Normal file
52
src/components/modal/modal.vue
Normal file
|
@ -0,0 +1,52 @@
|
|||
<template>
|
||||
<div
|
||||
v-show="isOpen"
|
||||
v-body-scroll-lock="isOpen"
|
||||
class="modal-view"
|
||||
@click.self="$emit('backdropClicked')"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
isOpen: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.modal-view {
|
||||
z-index: 1000;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: auto;
|
||||
animation-duration: 0.2s;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
animation-name: modal-background-fadein;
|
||||
|
||||
body:not(.scroll-locked) & {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes modal-background-fadein {
|
||||
from {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
to {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -3,9 +3,7 @@
|
|||
<v-popover
|
||||
trigger="click"
|
||||
class="moderation-tools-popover"
|
||||
:container="false"
|
||||
placement="bottom-end"
|
||||
:offset="5"
|
||||
@show="showDropDown = true"
|
||||
@hide="showDropDown = false"
|
||||
>
|
||||
|
|
|
@ -25,6 +25,12 @@ const passwordReset = {
|
|||
this.$router.push({ name: 'root' })
|
||||
}
|
||||
},
|
||||
props: {
|
||||
passwordResetRequested: {
|
||||
default: false,
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
dismissError () {
|
||||
this.error = null
|
||||
|
|
|
@ -10,7 +10,10 @@
|
|||
>
|
||||
<div class="container">
|
||||
<div v-if="!mailerEnabled">
|
||||
<p>
|
||||
<p v-if="passwordResetRequested">
|
||||
{{ $t('password_reset.password_reset_required_but_mailer_is_disabled') }}
|
||||
</p>
|
||||
<p v-else>
|
||||
{{ $t('password_reset.password_reset_disabled') }}
|
||||
</p>
|
||||
</div>
|
||||
|
@ -25,6 +28,12 @@
|
|||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p
|
||||
v-if="passwordResetRequested"
|
||||
class="password-reset-required error"
|
||||
>
|
||||
{{ $t('password_reset.password_reset_required') }}
|
||||
</p>
|
||||
<p>
|
||||
{{ $t('password_reset.instruction') }}
|
||||
</p>
|
||||
|
@ -104,6 +113,11 @@
|
|||
margin: 0.3em 0.0em 1em;
|
||||
}
|
||||
|
||||
.password-reset-required {
|
||||
background-color: var(--alertError, $fallback--alertError);
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.notice-dismissible {
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
margin: 5px;
|
||||
border-color: $fallback--bg;
|
||||
border-color: var(--bg, $fallback--bg);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&[x-placement^="top"] {
|
||||
|
@ -31,7 +30,7 @@
|
|||
border-left-color: transparent !important;
|
||||
border-right-color: transparent !important;
|
||||
border-bottom-color: transparent !important;
|
||||
bottom: -5px;
|
||||
bottom: -4px;
|
||||
left: calc(50% - 5px);
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
|
@ -46,7 +45,7 @@
|
|||
border-left-color: transparent !important;
|
||||
border-right-color: transparent !important;
|
||||
border-top-color: transparent !important;
|
||||
top: -5px;
|
||||
top: -4px;
|
||||
left: calc(50% - 5px);
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
|
@ -61,7 +60,7 @@
|
|||
border-left-color: transparent !important;
|
||||
border-top-color: transparent !important;
|
||||
border-bottom-color: transparent !important;
|
||||
left: -5px;
|
||||
left: -4px;
|
||||
top: calc(50% - 5px);
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
|
@ -76,7 +75,7 @@
|
|||
border-top-color: transparent !important;
|
||||
border-right-color: transparent !important;
|
||||
border-bottom-color: transparent !important;
|
||||
right: -5px;
|
||||
right: -4px;
|
||||
top: calc(50% - 5px);
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
|
|
|
@ -1,24 +1,43 @@
|
|||
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
||||
import Modal from '../modal/modal.vue'
|
||||
import get from 'lodash/get'
|
||||
|
||||
const PostStatusModal = {
|
||||
components: {
|
||||
PostStatusForm
|
||||
PostStatusForm,
|
||||
Modal
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
resettingForm: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isLoggedIn () {
|
||||
return !!this.$store.state.users.currentUser
|
||||
},
|
||||
isOpen () {
|
||||
return this.isLoggedIn && this.$store.state.postStatus.modalActivated
|
||||
modalActivated () {
|
||||
return this.$store.state.postStatus.modalActivated
|
||||
},
|
||||
isFormVisible () {
|
||||
return this.isLoggedIn && !this.resettingForm && this.modalActivated
|
||||
},
|
||||
params () {
|
||||
return this.$store.state.postStatus.params || {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isOpen (val) {
|
||||
params (newVal, oldVal) {
|
||||
if (get(newVal, 'repliedUser.id') !== get(oldVal, 'repliedUser.id')) {
|
||||
this.resettingForm = true
|
||||
this.$nextTick(() => {
|
||||
this.resettingForm = false
|
||||
})
|
||||
}
|
||||
},
|
||||
isFormVisible (val) {
|
||||
if (val) {
|
||||
this.$nextTick(() => this.$el.querySelector('textarea').focus())
|
||||
this.$nextTick(() => this.$el && this.$el.querySelector('textarea').focus())
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
<template>
|
||||
<div
|
||||
v-if="isOpen"
|
||||
class="post-form-modal-view modal-view"
|
||||
@click="closeModal"
|
||||
<Modal
|
||||
v-if="isLoggedIn && !resettingForm"
|
||||
:is-open="modalActivated"
|
||||
class="post-form-modal-view"
|
||||
@backdropClicked="closeModal"
|
||||
>
|
||||
<div
|
||||
class="post-form-modal-panel panel"
|
||||
@click.stop=""
|
||||
>
|
||||
<div class="post-form-modal-panel panel">
|
||||
<div class="panel-heading">
|
||||
{{ $t('post_status.new_status') }}
|
||||
</div>
|
||||
|
@ -17,15 +15,13 @@
|
|||
@posted="closeModal"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script src="./post_status_modal.js"></script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../_variables.scss';
|
||||
|
||||
.post-form-modal-view {
|
||||
.modal-view.post-form-modal-view {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
|
|
|
@ -122,6 +122,17 @@
|
|||
{{ $t("nav.about") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
v-if="currentUser && currentUser.role === 'admin'"
|
||||
@click="toggleDrawer"
|
||||
>
|
||||
<a
|
||||
href="/pleroma/admin/#/login-pleroma"
|
||||
target="_blank"
|
||||
>
|
||||
{{ $t("nav.administration") }}
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
v-if="currentUser"
|
||||
@click="toggleDrawer"
|
||||
|
|
|
@ -10,11 +10,12 @@ import Gallery from '../gallery/gallery.vue'
|
|||
import LinkPreview from '../link-preview/link-preview.vue'
|
||||
import AvatarList from '../avatar_list/avatar_list.vue'
|
||||
import Timeago from '../timeago/timeago.vue'
|
||||
import StatusPopover from '../status_popover/status_popover.vue'
|
||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||
import fileType from 'src/services/file_type/file_type.service'
|
||||
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
||||
import { mentionMatchesUrl, extractTagFromUrl } from 'src/services/matcher/matcher.service.js'
|
||||
import { filter, find, unescape, uniqBy } from 'lodash'
|
||||
import { filter, unescape, uniqBy } from 'lodash'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
const Status = {
|
||||
|
@ -38,8 +39,6 @@ const Status = {
|
|||
replying: false,
|
||||
unmuted: false,
|
||||
userExpanded: false,
|
||||
preview: null,
|
||||
showPreview: false,
|
||||
showingTall: this.inConversation && this.focused,
|
||||
showingLongSubject: false,
|
||||
error: null,
|
||||
|
@ -293,7 +292,8 @@ const Status = {
|
|||
Gallery,
|
||||
LinkPreview,
|
||||
AvatarList,
|
||||
Timeago
|
||||
Timeago,
|
||||
StatusPopover
|
||||
},
|
||||
methods: {
|
||||
visibilityIcon (visibility) {
|
||||
|
@ -368,27 +368,6 @@ const Status = {
|
|||
this.expandingSubject = true
|
||||
}
|
||||
},
|
||||
replyEnter (id, event) {
|
||||
this.showPreview = true
|
||||
const targetId = id
|
||||
const statuses = this.$store.state.statuses.allStatuses
|
||||
|
||||
if (!this.preview) {
|
||||
// if we have the status somewhere already
|
||||
this.preview = find(statuses, { 'id': targetId })
|
||||
// or if we have to fetch it
|
||||
if (!this.preview) {
|
||||
this.$store.state.api.backendInteractor.fetchStatus({ id }).then((status) => {
|
||||
this.preview = status
|
||||
})
|
||||
}
|
||||
} else if (this.preview.id !== targetId) {
|
||||
this.preview = find(statuses, { 'id': targetId })
|
||||
}
|
||||
},
|
||||
replyLeave () {
|
||||
this.showPreview = false
|
||||
},
|
||||
generateUserProfileLink (id, name) {
|
||||
return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames)
|
||||
},
|
||||
|
|
|
@ -174,20 +174,26 @@
|
|||
v-if="isReply"
|
||||
class="reply-to-and-accountname"
|
||||
>
|
||||
<a
|
||||
class="reply-to"
|
||||
href="#"
|
||||
:aria-label="$t('tool_tip.reply')"
|
||||
@click.prevent="gotoOriginal(status.in_reply_to_status_id)"
|
||||
@mouseenter.prevent.stop="replyEnter(status.in_reply_to_status_id, $event)"
|
||||
@mouseleave.prevent.stop="replyLeave()"
|
||||
<StatusPopover
|
||||
v-if="!isPreview"
|
||||
:status-id="status.in_reply_to_status_id"
|
||||
>
|
||||
<i
|
||||
v-if="!isPreview"
|
||||
class="button-icon icon-reply"
|
||||
/>
|
||||
<span class="faint-link reply-to-text">{{ $t('status.reply_to') }}</span>
|
||||
</a>
|
||||
<a
|
||||
class="reply-to"
|
||||
href="#"
|
||||
:aria-label="$t('tool_tip.reply')"
|
||||
@click.prevent="gotoOriginal(status.in_reply_to_status_id)"
|
||||
>
|
||||
<i class="button-icon icon-reply" />
|
||||
<span class="faint-link reply-to-text">{{ $t('status.reply_to') }}</span>
|
||||
</a>
|
||||
</StatusPopover>
|
||||
<span
|
||||
v-else
|
||||
class="reply-to"
|
||||
>
|
||||
<span class="reply-to-text">{{ $t('status.reply_to') }}</span>
|
||||
</span>
|
||||
<router-link :to="replyProfileLink">
|
||||
{{ replyToName }}
|
||||
</router-link>
|
||||
|
@ -199,50 +205,25 @@
|
|||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="inConversation && !isPreview"
|
||||
v-if="inConversation && !isPreview && replies && replies.length"
|
||||
class="replies"
|
||||
>
|
||||
<span
|
||||
v-if="replies && replies.length"
|
||||
class="faint"
|
||||
>{{ $t('status.replies_list') }}</span>
|
||||
<template v-if="replies">
|
||||
<span
|
||||
v-for="reply in replies"
|
||||
:key="reply.id"
|
||||
class="reply-link faint"
|
||||
>
|
||||
<a
|
||||
href="#"
|
||||
@click.prevent="gotoOriginal(reply.id)"
|
||||
@mouseenter="replyEnter(reply.id, $event)"
|
||||
@mouseout="replyLeave()"
|
||||
>{{ reply.name }}</a>
|
||||
</span>
|
||||
</template>
|
||||
<span class="faint">{{ $t('status.replies_list') }}</span>
|
||||
<StatusPopover
|
||||
v-for="reply in replies"
|
||||
:key="reply.id"
|
||||
:status-id="reply.id"
|
||||
>
|
||||
<a
|
||||
href="#"
|
||||
class="reply-link"
|
||||
@click.prevent="gotoOriginal(reply.id)"
|
||||
>{{ reply.name }}</a>
|
||||
</StatusPopover>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="showPreview"
|
||||
class="status-preview-container"
|
||||
>
|
||||
<status
|
||||
v-if="preview"
|
||||
class="status-preview"
|
||||
:is-preview="true"
|
||||
:statusoid="preview"
|
||||
:compact="true"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="status-preview status-preview-loading"
|
||||
>
|
||||
<i class="icon-spin4 animate-spin" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="longSubject"
|
||||
class="status-content-wrapper"
|
||||
|
@ -439,18 +420,6 @@ $status-margin: 0.75em;
|
|||
min-width: 0;
|
||||
}
|
||||
|
||||
.status-preview.status-el {
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: $fallback--border;
|
||||
border-color: var(--border, $fallback--border);
|
||||
}
|
||||
|
||||
.status-preview-container {
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.status-pin {
|
||||
padding: $status-margin $status-margin 0;
|
||||
display: flex;
|
||||
|
@ -458,44 +427,6 @@ $status-margin: 0.75em;
|
|||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.status-preview {
|
||||
position: absolute;
|
||||
max-width: 95%;
|
||||
display: flex;
|
||||
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);
|
||||
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
|
||||
box-shadow: var(--popupShadow);
|
||||
margin-top: 0.25em;
|
||||
margin-left: 0.5em;
|
||||
z-index: 50;
|
||||
|
||||
.status {
|
||||
flex: 1;
|
||||
border: 0;
|
||||
min-width: 15em;
|
||||
}
|
||||
}
|
||||
|
||||
.status-preview-loading {
|
||||
display: block;
|
||||
min-width: 15em;
|
||||
padding: 1em;
|
||||
text-align: center;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
|
||||
i {
|
||||
font-size: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.media-left {
|
||||
margin-right: $status-margin;
|
||||
}
|
||||
|
@ -553,11 +484,6 @@ $status-margin: 0.75em;
|
|||
flex-basis: 100%;
|
||||
margin-bottom: 0.5em;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
small {
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
@ -568,6 +494,11 @@ $status-margin: 0.75em;
|
|||
justify-content: space-between;
|
||||
line-height: 18px;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.name-and-account-name {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
|
@ -600,6 +531,7 @@ $status-margin: 0.75em;
|
|||
}
|
||||
|
||||
.heading-reply-row {
|
||||
position: relative;
|
||||
align-content: baseline;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
|
@ -608,11 +540,13 @@ $status-margin: 0.75em;
|
|||
flex-wrap: wrap;
|
||||
align-items: stretch;
|
||||
|
||||
a {
|
||||
> .reply-to-and-accountname > a {
|
||||
max-width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -639,6 +573,8 @@ $status-margin: 0.75em;
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin: 0 0.4em 0 0.2em;
|
||||
color: $fallback--faint;
|
||||
color: var(--faint, $fallback--faint);
|
||||
}
|
||||
|
||||
.replies-separator {
|
||||
|
@ -840,6 +776,11 @@ $status-margin: 0.75em;
|
|||
&.button-icon-active {
|
||||
color: $fallback--cBlue;
|
||||
color: var(--cBlue, $fallback--cBlue);
|
||||
}
|
||||
}
|
||||
|
||||
.button-icon.icon-reply {
|
||||
&:not(.button-icon-disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
|
34
src/components/status_popover/status_popover.js
Normal file
34
src/components/status_popover/status_popover.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { find } from 'lodash'
|
||||
|
||||
const StatusPopover = {
|
||||
name: 'StatusPopover',
|
||||
props: [
|
||||
'statusId'
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
popperOptions: {
|
||||
modifiers: {
|
||||
preventOverflow: { padding: { top: 50 }, boundariesElement: 'viewport' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
status () {
|
||||
return find(this.$store.state.statuses.allStatuses, { id: this.statusId })
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Status: () => import('../status/status.vue')
|
||||
},
|
||||
methods: {
|
||||
enter () {
|
||||
if (!this.status) {
|
||||
this.$store.dispatch('fetchStatus', this.statusId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default StatusPopover
|
85
src/components/status_popover/status_popover.vue
Normal file
85
src/components/status_popover/status_popover.vue
Normal file
|
@ -0,0 +1,85 @@
|
|||
<template>
|
||||
<v-popover
|
||||
popover-class="status-popover"
|
||||
placement="top-start"
|
||||
:popper-options="popperOptions"
|
||||
@show="enter()"
|
||||
>
|
||||
<template slot="popover">
|
||||
<Status
|
||||
v-if="status"
|
||||
:is-preview="true"
|
||||
:statusoid="status"
|
||||
:compact="true"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="status-preview-loading"
|
||||
>
|
||||
<i class="icon-spin4 animate-spin" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<slot />
|
||||
</v-popover>
|
||||
</template>
|
||||
|
||||
<script src="./status_popover.js" ></script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../_variables.scss';
|
||||
|
||||
.tooltip.popover.status-popover {
|
||||
font-size: 1rem;
|
||||
min-width: 15em;
|
||||
max-width: 95%;
|
||||
margin-left: 0.5em;
|
||||
|
||||
.popover-inner {
|
||||
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);
|
||||
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
|
||||
box-shadow: var(--popupShadow);
|
||||
}
|
||||
|
||||
.popover-arrow::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
left: -7px;
|
||||
border: solid 7px transparent;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
&[x-placement^="bottom-start"] .popover-arrow::before {
|
||||
top: -2px;
|
||||
border-top-width: 0;
|
||||
border-bottom-color: $fallback--border;
|
||||
border-bottom-color: var(--border, $fallback--border);
|
||||
}
|
||||
|
||||
&[x-placement^="top-start"] .popover-arrow::before {
|
||||
bottom: -2px;
|
||||
border-bottom-width: 0;
|
||||
border-top-color: $fallback--border;
|
||||
border-top-color: var(--border, $fallback--border);
|
||||
}
|
||||
|
||||
.status-el.status-el {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.status-preview-loading {
|
||||
padding: 1em;
|
||||
text-align: center;
|
||||
|
||||
i {
|
||||
font-size: 2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
|
@ -3,7 +3,8 @@ const StillImage = {
|
|||
'src',
|
||||
'referrerpolicy',
|
||||
'mimetype',
|
||||
'imageLoadError'
|
||||
'imageLoadError',
|
||||
'imageLoadHandler'
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
|
@ -17,6 +18,7 @@ const StillImage = {
|
|||
},
|
||||
methods: {
|
||||
onLoad () {
|
||||
this.imageLoadHandler && this.imageLoadHandler(this.$refs.src)
|
||||
const canvas = this.$refs.canvas
|
||||
if (!canvas) return
|
||||
const width = this.$refs.src.naturalWidth
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||
import RemoteFollow from '../remote_follow/remote_follow.vue'
|
||||
import ProgressButton from '../progress_button/progress_button.vue'
|
||||
import FollowButton from '../follow_button/follow_button.vue'
|
||||
import ModerationTools from '../moderation_tools/moderation_tools.vue'
|
||||
import AccountActions from '../account_actions/account_actions.vue'
|
||||
import { hex2rgb } from '../../services/color_convert/color_convert.js'
|
||||
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
|
||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
props: [ 'user', 'switcher', 'selected', 'hideBio', 'rounded', 'bordered', 'allowZoomingAvatar' ],
|
||||
props: [
|
||||
'user', 'switcher', 'selected', 'hideBio', 'rounded', 'bordered', 'allowZoomingAvatar'
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
followRequestInProgress: false,
|
||||
|
@ -96,30 +99,11 @@ export default {
|
|||
UserAvatar,
|
||||
RemoteFollow,
|
||||
ModerationTools,
|
||||
ProgressButton
|
||||
AccountActions,
|
||||
ProgressButton,
|
||||
FollowButton
|
||||
},
|
||||
methods: {
|
||||
followUser () {
|
||||
const store = this.$store
|
||||
this.followRequestInProgress = true
|
||||
requestFollow(this.user, store).then(() => {
|
||||
this.followRequestInProgress = false
|
||||
})
|
||||
},
|
||||
unfollowUser () {
|
||||
const store = this.$store
|
||||
this.followRequestInProgress = true
|
||||
requestUnfollow(this.user, store).then(() => {
|
||||
this.followRequestInProgress = false
|
||||
store.commit('removeStatus', { timeline: 'friends', userId: this.user.id })
|
||||
})
|
||||
},
|
||||
blockUser () {
|
||||
this.$store.dispatch('blockUser', this.user.id)
|
||||
},
|
||||
unblockUser () {
|
||||
this.$store.dispatch('unblockUser', this.user.id)
|
||||
},
|
||||
muteUser () {
|
||||
this.$store.dispatch('muteUser', this.user.id)
|
||||
},
|
||||
|
@ -147,10 +131,10 @@ export default {
|
|||
}
|
||||
},
|
||||
userProfileLink (user) {
|
||||
return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames)
|
||||
},
|
||||
reportUser () {
|
||||
this.$store.dispatch('openUserReportingModal', this.user.id)
|
||||
return generateProfileLink(
|
||||
user.id, user.screen_name,
|
||||
this.$store.state.instance.restrictedNicknames
|
||||
)
|
||||
},
|
||||
zoomAvatar () {
|
||||
const attachment = {
|
||||
|
@ -159,9 +143,6 @@ export default {
|
|||
}
|
||||
this.$store.dispatch('setMedia', [attachment])
|
||||
this.$store.dispatch('setCurrent', attachment)
|
||||
},
|
||||
mentionUser () {
|
||||
this.$store.dispatch('openPostStatusModal', { replyTo: true, repliedUser: this.user })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,8 +66,11 @@
|
|||
>
|
||||
<i class="icon-link-ext usersettings" />
|
||||
</a>
|
||||
<AccountActions
|
||||
v-if="isOtherUser && loggedIn"
|
||||
:user="user"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="bottom-line">
|
||||
<router-link
|
||||
class="user-screen-name"
|
||||
|
@ -135,72 +138,27 @@
|
|||
v-if="loggedIn && isOtherUser"
|
||||
class="user-interactions"
|
||||
>
|
||||
<div v-if="!user.following">
|
||||
<button
|
||||
class="btn btn-default btn-block"
|
||||
:disabled="followRequestInProgress"
|
||||
:title="user.requested ? $t('user_card.follow_again') : ''"
|
||||
@click="followUser"
|
||||
>
|
||||
<template v-if="followRequestInProgress">
|
||||
{{ $t('user_card.follow_progress') }}
|
||||
</template>
|
||||
<template v-else-if="user.requested">
|
||||
{{ $t('user_card.follow_sent') }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ $t('user_card.follow') }}
|
||||
</template>
|
||||
</button>
|
||||
<div class="btn-group">
|
||||
<FollowButton :user="user" />
|
||||
<template v-if="user.following">
|
||||
<ProgressButton
|
||||
v-if="!user.subscribed"
|
||||
class="btn btn-default"
|
||||
:click="subscribeUser"
|
||||
:title="$t('user_card.subscribe')"
|
||||
>
|
||||
<i class="icon-bell-alt" />
|
||||
</ProgressButton>
|
||||
<ProgressButton
|
||||
v-else
|
||||
class="btn btn-default pressed"
|
||||
:click="unsubscribeUser"
|
||||
:title="$t('user_card.unsubscribe')"
|
||||
>
|
||||
<i class="icon-bell-ringing-o" />
|
||||
</ProgressButton>
|
||||
</template>
|
||||
</div>
|
||||
<div v-else-if="followRequestInProgress">
|
||||
<button
|
||||
class="btn btn-default btn-block pressed"
|
||||
disabled
|
||||
:title="$t('user_card.follow_unfollow')"
|
||||
@click="unfollowUser"
|
||||
>
|
||||
{{ $t('user_card.follow_progress') }}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="btn-group"
|
||||
>
|
||||
<button
|
||||
class="btn btn-default pressed"
|
||||
:title="$t('user_card.follow_unfollow')"
|
||||
@click="unfollowUser"
|
||||
>
|
||||
{{ $t('user_card.following') }}
|
||||
</button>
|
||||
<ProgressButton
|
||||
v-if="!user.subscribed"
|
||||
class="btn btn-default"
|
||||
:click="subscribeUser"
|
||||
:title="$t('user_card.subscribe')"
|
||||
>
|
||||
<i class="icon-bell-alt" />
|
||||
</ProgressButton>
|
||||
<ProgressButton
|
||||
v-else
|
||||
class="btn btn-default pressed"
|
||||
:click="unsubscribeUser"
|
||||
:title="$t('user_card.unsubscribe')"
|
||||
>
|
||||
<i class="icon-bell-ringing-o" />
|
||||
</ProgressButton>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
class="btn btn-default btn-block"
|
||||
@click="mentionUser"
|
||||
>
|
||||
{{ $t('user_card.mention') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
v-if="user.muted"
|
||||
|
@ -217,33 +175,6 @@
|
|||
{{ $t('user_card.mute') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
v-if="user.statusnet_blocking"
|
||||
class="btn btn-default btn-block pressed"
|
||||
@click="unblockUser"
|
||||
>
|
||||
{{ $t('user_card.blocked') }}
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="btn btn-default btn-block"
|
||||
@click="blockUser"
|
||||
>
|
||||
{{ $t('user_card.block') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
class="btn btn-default btn-block"
|
||||
@click="reportUser"
|
||||
>
|
||||
{{ $t('user_card.report') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ModerationTools
|
||||
v-if="loggedIn.role === "admin""
|
||||
:user="user"
|
||||
|
@ -587,13 +518,12 @@
|
|||
position: relative;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-between;
|
||||
margin-right: -.75em;
|
||||
|
||||
> * {
|
||||
flex: 1 0 0;
|
||||
margin: 0 .75em .6em 0;
|
||||
white-space: nowrap;
|
||||
min-width: 95px;
|
||||
}
|
||||
|
||||
button {
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
import Status from '../status/status.vue'
|
||||
import List from '../list/list.vue'
|
||||
import Checkbox from '../checkbox/checkbox.vue'
|
||||
import Modal from '../modal/modal.vue'
|
||||
|
||||
const UserReportingModal = {
|
||||
components: {
|
||||
Status,
|
||||
List,
|
||||
Checkbox
|
||||
Checkbox,
|
||||
Modal
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
<template>
|
||||
<div
|
||||
<Modal
|
||||
v-if="isOpen"
|
||||
class="modal-view"
|
||||
@click="closeModal"
|
||||
@backdropClicked="closeModal"
|
||||
>
|
||||
<div
|
||||
class="user-reporting-panel panel"
|
||||
@click.stop=""
|
||||
>
|
||||
<div class="user-reporting-panel panel">
|
||||
<div class="panel-heading">
|
||||
<div class="title">
|
||||
{{ $t('user_reporting.title', [user.screen_name]) }}
|
||||
|
@ -69,7 +65,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script src="./user_reporting_modal.js"></script>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue