forked from AkkomaGang/akkoma-fe
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
|
@ -7,6 +7,7 @@ stages:
|
||||||
- lint
|
- lint
|
||||||
- build
|
- build
|
||||||
- test
|
- test
|
||||||
|
- deploy
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
stage: lint
|
stage: lint
|
||||||
|
@ -33,3 +34,13 @@ build:
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- dist/
|
- dist/
|
||||||
|
|
||||||
|
docs-deploy:
|
||||||
|
stage: deploy
|
||||||
|
image: alpine:latest
|
||||||
|
only:
|
||||||
|
- develop@pleroma/pleroma-fe
|
||||||
|
before_script:
|
||||||
|
- apk add curl
|
||||||
|
script:
|
||||||
|
- curl -X POST -F"token=$DOCS_PIPELINE_TRIGGER" -F'ref=master' https://git.pleroma.social/api/v4/projects/673/trigger/pipeline
|
||||||
|
|
|
@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
|
- Ability to hide/show repeats from user
|
||||||
|
- User profile button clutter organized into a menu
|
||||||
- Emoji picker
|
- Emoji picker
|
||||||
- Started changelog anew
|
- Started changelog anew
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Pleroma-FE configuration and customization for instance administrators
|
# Pleroma-FE configuration and customization for instance administrators
|
||||||
|
|
||||||
* *For user configuration, see USER_GUIDE.md*
|
* *For user configuration, see [Pleroma-FE user guide](USER_GUIDE.md)*
|
||||||
* *For local development server configuration, see HACKING.md*
|
* *For local development server configuration, see [Hacking, tweaking, contributing](HACKING.md)*
|
||||||
|
|
||||||
## Where configuration is stored
|
## Where configuration is stored
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# Pleroma-FE user guide
|
||||||
|
|
||||||
> Be prepared for breaking changes, unexpected behavior and this user guide becoming obsolete and wrong.
|
> Be prepared for breaking changes, unexpected behavior and this user guide becoming obsolete and wrong.
|
||||||
|
|
||||||
> If there was no insanity
|
> If there was no insanity
|
||||||
|
@ -6,8 +8,6 @@
|
||||||
>
|
>
|
||||||
> --Catbag
|
> --Catbag
|
||||||
|
|
||||||
# Pleroma-FE user guide
|
|
||||||
|
|
||||||
Pleroma-FE user interface is modeled after Qvitter which is modeled after older Twitter design. It provides a simple 2-column interface for microblogging. While being simple by default it also provides many powerful customization options.
|
Pleroma-FE user interface is modeled after Qvitter which is modeled after older Twitter design. It provides a simple 2-column interface for microblogging. While being simple by default it also provides many powerful customization options.
|
||||||
|
|
||||||
## Posting, reading, basic functions.
|
## Posting, reading, basic functions.
|
||||||
|
|
30
src/App.scss
30
src/App.scss
|
@ -39,10 +39,13 @@ h4 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
font-family: var(--interfaceFont, sans-serif);
|
font-family: var(--interfaceFont, sans-serif);
|
||||||
font-size: 14px;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: $fallback--text;
|
color: $fallback--text;
|
||||||
color: var(--text, $fallback--text);
|
color: var(--text, $fallback--text);
|
||||||
|
@ -705,31 +708,6 @@ nav {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes modal-background-fadein {
|
|
||||||
from {
|
|
||||||
background-color: rgba(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-icon {
|
.button-icon {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,15 @@
|
||||||
:title="$t('nav.preferences')"
|
:title="$t('nav.preferences')"
|
||||||
/>
|
/>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
<a
|
||||||
|
v-if="currentUser && currentUser.role === 'admin'"
|
||||||
|
href="/pleroma/admin/#/login-pleroma"
|
||||||
|
class="mobile-hidden"
|
||||||
|
target="_blank"
|
||||||
|
><i
|
||||||
|
class="button-icon icon-gauge nav-icon"
|
||||||
|
:title="$t('nav.administration')"
|
||||||
|
/></a>
|
||||||
<a
|
<a
|
||||||
v-if="currentUser"
|
v-if="currentUser"
|
||||||
href="#"
|
href="#"
|
||||||
|
|
|
@ -47,7 +47,7 @@ export default (store) => {
|
||||||
{ name: 'dms', path: '/users/:username/dms', component: DMs, beforeEnter: validateAuthenticatedRoute },
|
{ name: 'dms', path: '/users/:username/dms', component: DMs, beforeEnter: validateAuthenticatedRoute },
|
||||||
{ name: 'settings', path: '/settings', component: Settings },
|
{ name: 'settings', path: '/settings', component: Settings },
|
||||||
{ name: 'registration', path: '/registration', component: Registration },
|
{ name: 'registration', path: '/registration', component: Registration },
|
||||||
{ name: 'password-reset', path: '/password-reset', component: PasswordReset },
|
{ name: 'password-reset', path: '/password-reset', component: PasswordReset, props: true },
|
||||||
{ name: 'registration-token', path: '/registration/:token', component: Registration },
|
{ name: 'registration-token', path: '/registration/:token', component: Registration },
|
||||||
{ name: 'friend-requests', path: '/friend-requests', component: FollowRequests, beforeEnter: validateAuthenticatedRoute },
|
{ name: 'friend-requests', path: '/friend-requests', component: FollowRequests, beforeEnter: validateAuthenticatedRoute },
|
||||||
{ name: 'user-settings', path: '/user-settings', component: UserSettings, beforeEnter: validateAuthenticatedRoute },
|
{ name: 'user-settings', path: '/user-settings', component: UserSettings, beforeEnter: validateAuthenticatedRoute },
|
||||||
|
|
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',
|
'statusId',
|
||||||
'size',
|
'size',
|
||||||
'allowPlay',
|
'allowPlay',
|
||||||
'setMedia'
|
'setMedia',
|
||||||
|
'naturalSizeLoad'
|
||||||
],
|
],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
@ -88,6 +89,11 @@ const Attachment = {
|
||||||
} else {
|
} else {
|
||||||
this.showHidden = !this.showHidden
|
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"
|
:referrerpolicy="referrerpolicy"
|
||||||
:mimetype="attachment.mimetype"
|
:mimetype="attachment.mimetype"
|
||||||
:src="attachment.large_thumb_url || attachment.url"
|
:src="attachment.large_thumb_url || attachment.url"
|
||||||
|
:image-load-handler="onImageLoad"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
trigger="click"
|
trigger="click"
|
||||||
placement="top"
|
placement="top"
|
||||||
class="extra-button-popover"
|
class="extra-button-popover"
|
||||||
:offset="5"
|
|
||||||
:container="false"
|
|
||||||
>
|
>
|
||||||
<div slot="popover">
|
<div slot="popover">
|
||||||
<div class="dropdown-menu">
|
<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 BasicUserCard from '../basic_user_card/basic_user_card.vue'
|
||||||
import RemoteFollow from '../remote_follow/remote_follow.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 = {
|
const FollowCard = {
|
||||||
props: [
|
props: [
|
||||||
'user',
|
'user',
|
||||||
'noFollowsYou'
|
'noFollowsYou'
|
||||||
],
|
],
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
inProgress: false,
|
|
||||||
requestSent: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
components: {
|
||||||
BasicUserCard,
|
BasicUserCard,
|
||||||
RemoteFollow
|
RemoteFollow,
|
||||||
|
FollowButton
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isMe () {
|
isMe () {
|
||||||
|
@ -24,21 +19,6 @@ const FollowCard = {
|
||||||
loggedIn () {
|
loggedIn () {
|
||||||
return this.$store.state.users.currentUser
|
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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<button
|
<FollowButton
|
||||||
v-if="!user.following"
|
:user="user"
|
||||||
class="btn btn-default follow-card-follow-button"
|
class="follow-card-follow-button"
|
||||||
:disabled="inProgress"
|
:label-following="$t('user_card.follow_unfollow')"
|
||||||
: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>
|
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</basic-user-card>
|
</basic-user-card>
|
||||||
|
|
|
@ -1,23 +1,18 @@
|
||||||
import Attachment from '../attachment/attachment.vue'
|
import Attachment from '../attachment/attachment.vue'
|
||||||
import { chunk, last, dropRight } from 'lodash'
|
import { chunk, last, dropRight, sumBy } from 'lodash'
|
||||||
|
|
||||||
const Gallery = {
|
const Gallery = {
|
||||||
data: () => ({
|
|
||||||
width: 500
|
|
||||||
}),
|
|
||||||
props: [
|
props: [
|
||||||
'attachments',
|
'attachments',
|
||||||
'nsfw',
|
'nsfw',
|
||||||
'setMedia'
|
'setMedia'
|
||||||
],
|
],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
sizes: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
components: { Attachment },
|
components: { Attachment },
|
||||||
mounted () {
|
|
||||||
this.resize()
|
|
||||||
window.addEventListener('resize', this.resize)
|
|
||||||
},
|
|
||||||
destroyed () {
|
|
||||||
window.removeEventListener('resize', this.resize)
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
rows () {
|
rows () {
|
||||||
if (!this.attachments) {
|
if (!this.attachments) {
|
||||||
|
@ -33,21 +28,24 @@ const Gallery = {
|
||||||
}
|
}
|
||||||
return rows
|
return rows
|
||||||
},
|
},
|
||||||
rowHeight () {
|
|
||||||
return itemsPerRow => ({ 'height': `${(this.width / (itemsPerRow + 0.6))}px` })
|
|
||||||
},
|
|
||||||
useContainFit () {
|
useContainFit () {
|
||||||
return this.$store.getters.mergedConfig.useContainFit
|
return this.$store.getters.mergedConfig.useContainFit
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
resize () {
|
onNaturalSizeLoad (id, size) {
|
||||||
// Quick optimization to make resizing not always trigger state change,
|
this.$set(this.sizes, id, size)
|
||||||
// only update attachment size in 10px steps
|
},
|
||||||
const width = Math.floor(this.$el.getBoundingClientRect().width / 10) * 10
|
rowStyle (itemsPerRow) {
|
||||||
if (this.width !== width) {
|
return { 'padding-bottom': `${(100 / (itemsPerRow + 0.6))}%` }
|
||||||
this.width = width
|
},
|
||||||
}
|
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"
|
v-for="(row, index) in rows"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="gallery-row"
|
class="gallery-row"
|
||||||
:style="rowHeight(row.length)"
|
:style="rowStyle(row.length)"
|
||||||
:class="{ 'contain-fit': useContainFit, 'cover-fit': !useContainFit }"
|
:class="{ 'contain-fit': useContainFit, 'cover-fit': !useContainFit }"
|
||||||
>
|
>
|
||||||
<attachment
|
<div class="gallery-row-inner">
|
||||||
v-for="attachment in row"
|
<attachment
|
||||||
:key="attachment.id"
|
v-for="attachment in row"
|
||||||
:set-media="setMedia"
|
:key="attachment.id"
|
||||||
:nsfw="nsfw"
|
:set-media="setMedia"
|
||||||
:attachment="attachment"
|
:nsfw="nsfw"
|
||||||
:allow-play="false"
|
:attachment="attachment"
|
||||||
/>
|
:allow-play="false"
|
||||||
|
:natural-size-load="onNaturalSizeLoad.bind(null, attachment.id)"
|
||||||
|
:style="itemStyle(attachment.id, row)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -28,15 +32,24 @@
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
.gallery-row {
|
.gallery-row {
|
||||||
height: 200px;
|
position: relative;
|
||||||
|
height: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
align-content: stretch;
|
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
margin-top: 0.5em;
|
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
|
// FIXME: specificity problem with this and .attachments.attachment
|
||||||
// we shouldn't have the need for .image here
|
// we shouldn't have the need for .image here
|
||||||
.attachment.image {
|
.attachment.image {
|
||||||
|
|
|
@ -59,6 +59,8 @@ const LoginForm = {
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
if (result.error === 'mfa_required') {
|
if (result.error === 'mfa_required') {
|
||||||
this.requireMFA({ app: app, settings: result })
|
this.requireMFA({ app: app, settings: result })
|
||||||
|
} else if (result.identifier === 'password_reset_required') {
|
||||||
|
this.$router.push({ name: 'password-reset', params: { passwordResetRequested: true } })
|
||||||
} else {
|
} else {
|
||||||
this.error = result.error
|
this.error = result.error
|
||||||
this.focusOnPasswordInput()
|
this.focusOnPasswordInput()
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import StillImage from '../still-image/still-image.vue'
|
import StillImage from '../still-image/still-image.vue'
|
||||||
import VideoAttachment from '../video_attachment/video_attachment.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'
|
import fileTypeService from '../../services/file_type/file_type.service.js'
|
||||||
|
|
||||||
const MediaModal = {
|
const MediaModal = {
|
||||||
components: {
|
components: {
|
||||||
StillImage,
|
StillImage,
|
||||||
VideoAttachment
|
VideoAttachment,
|
||||||
|
Modal
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
showing () {
|
showing () {
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<Modal
|
||||||
v-if="showing"
|
v-if="showing"
|
||||||
v-body-scroll-lock="showing"
|
class="media-modal-view"
|
||||||
class="modal-view media-modal-view"
|
@backdropClicked="hide"
|
||||||
@click.prevent="hide"
|
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
v-if="type === 'image'"
|
v-if="type === 'image'"
|
||||||
|
@ -33,21 +32,15 @@
|
||||||
>
|
>
|
||||||
<i class="icon-right-open arrow-icon" />
|
<i class="icon-right-open arrow-icon" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./media_modal.js"></script>
|
<script src="./media_modal.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
.modal-view.media-modal-view {
|
||||||
|
|
||||||
.media-modal-view {
|
|
||||||
z-index: 1001;
|
z-index: 1001;
|
||||||
|
|
||||||
body:not(.scroll-locked) & {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.modal-view-button-arrow {
|
.modal-view-button-arrow {
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
|
@ -114,5 +107,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</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
|
<v-popover
|
||||||
trigger="click"
|
trigger="click"
|
||||||
class="moderation-tools-popover"
|
class="moderation-tools-popover"
|
||||||
:container="false"
|
|
||||||
placement="bottom-end"
|
placement="bottom-end"
|
||||||
:offset="5"
|
|
||||||
@show="showDropDown = true"
|
@show="showDropDown = true"
|
||||||
@hide="showDropDown = false"
|
@hide="showDropDown = false"
|
||||||
>
|
>
|
||||||
|
|
|
@ -25,6 +25,12 @@ const passwordReset = {
|
||||||
this.$router.push({ name: 'root' })
|
this.$router.push({ name: 'root' })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
props: {
|
||||||
|
passwordResetRequested: {
|
||||||
|
default: false,
|
||||||
|
type: Boolean
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
dismissError () {
|
dismissError () {
|
||||||
this.error = null
|
this.error = null
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
>
|
>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div v-if="!mailerEnabled">
|
<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') }}
|
{{ $t('password_reset.password_reset_disabled') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,6 +28,12 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
<p
|
||||||
|
v-if="passwordResetRequested"
|
||||||
|
class="password-reset-required error"
|
||||||
|
>
|
||||||
|
{{ $t('password_reset.password_reset_required') }}
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{ $t('password_reset.instruction') }}
|
{{ $t('password_reset.instruction') }}
|
||||||
</p>
|
</p>
|
||||||
|
@ -104,6 +113,11 @@
|
||||||
margin: 0.3em 0.0em 1em;
|
margin: 0.3em 0.0em 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.password-reset-required {
|
||||||
|
background-color: var(--alertError, $fallback--alertError);
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.notice-dismissible {
|
.notice-dismissible {
|
||||||
padding-right: 2rem;
|
padding-right: 2rem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
border-color: $fallback--bg;
|
border-color: $fallback--bg;
|
||||||
border-color: var(--bg, $fallback--bg);
|
border-color: var(--bg, $fallback--bg);
|
||||||
z-index: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&[x-placement^="top"] {
|
&[x-placement^="top"] {
|
||||||
|
@ -31,7 +30,7 @@
|
||||||
border-left-color: transparent !important;
|
border-left-color: transparent !important;
|
||||||
border-right-color: transparent !important;
|
border-right-color: transparent !important;
|
||||||
border-bottom-color: transparent !important;
|
border-bottom-color: transparent !important;
|
||||||
bottom: -5px;
|
bottom: -4px;
|
||||||
left: calc(50% - 5px);
|
left: calc(50% - 5px);
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
@ -46,7 +45,7 @@
|
||||||
border-left-color: transparent !important;
|
border-left-color: transparent !important;
|
||||||
border-right-color: transparent !important;
|
border-right-color: transparent !important;
|
||||||
border-top-color: transparent !important;
|
border-top-color: transparent !important;
|
||||||
top: -5px;
|
top: -4px;
|
||||||
left: calc(50% - 5px);
|
left: calc(50% - 5px);
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
@ -61,7 +60,7 @@
|
||||||
border-left-color: transparent !important;
|
border-left-color: transparent !important;
|
||||||
border-top-color: transparent !important;
|
border-top-color: transparent !important;
|
||||||
border-bottom-color: transparent !important;
|
border-bottom-color: transparent !important;
|
||||||
left: -5px;
|
left: -4px;
|
||||||
top: calc(50% - 5px);
|
top: calc(50% - 5px);
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
@ -76,7 +75,7 @@
|
||||||
border-top-color: transparent !important;
|
border-top-color: transparent !important;
|
||||||
border-right-color: transparent !important;
|
border-right-color: transparent !important;
|
||||||
border-bottom-color: transparent !important;
|
border-bottom-color: transparent !important;
|
||||||
right: -5px;
|
right: -4px;
|
||||||
top: calc(50% - 5px);
|
top: calc(50% - 5px);
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
|
|
@ -1,24 +1,43 @@
|
||||||
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
||||||
|
import Modal from '../modal/modal.vue'
|
||||||
|
import get from 'lodash/get'
|
||||||
|
|
||||||
const PostStatusModal = {
|
const PostStatusModal = {
|
||||||
components: {
|
components: {
|
||||||
PostStatusForm
|
PostStatusForm,
|
||||||
|
Modal
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
resettingForm: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isLoggedIn () {
|
isLoggedIn () {
|
||||||
return !!this.$store.state.users.currentUser
|
return !!this.$store.state.users.currentUser
|
||||||
},
|
},
|
||||||
isOpen () {
|
modalActivated () {
|
||||||
return this.isLoggedIn && this.$store.state.postStatus.modalActivated
|
return this.$store.state.postStatus.modalActivated
|
||||||
|
},
|
||||||
|
isFormVisible () {
|
||||||
|
return this.isLoggedIn && !this.resettingForm && this.modalActivated
|
||||||
},
|
},
|
||||||
params () {
|
params () {
|
||||||
return this.$store.state.postStatus.params || {}
|
return this.$store.state.postStatus.params || {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
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) {
|
if (val) {
|
||||||
this.$nextTick(() => this.$el.querySelector('textarea').focus())
|
this.$nextTick(() => this.$el && this.$el.querySelector('textarea').focus())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<Modal
|
||||||
v-if="isOpen"
|
v-if="isLoggedIn && !resettingForm"
|
||||||
class="post-form-modal-view modal-view"
|
:is-open="modalActivated"
|
||||||
@click="closeModal"
|
class="post-form-modal-view"
|
||||||
|
@backdropClicked="closeModal"
|
||||||
>
|
>
|
||||||
<div
|
<div class="post-form-modal-panel panel">
|
||||||
class="post-form-modal-panel panel"
|
|
||||||
@click.stop=""
|
|
||||||
>
|
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
{{ $t('post_status.new_status') }}
|
{{ $t('post_status.new_status') }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,15 +15,13 @@
|
||||||
@posted="closeModal"
|
@posted="closeModal"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./post_status_modal.js"></script>
|
<script src="./post_status_modal.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
.modal-view.post-form-modal-view {
|
||||||
|
|
||||||
.post-form-modal-view {
|
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -122,6 +122,17 @@
|
||||||
{{ $t("nav.about") }}
|
{{ $t("nav.about") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
|
<li
|
||||||
|
v-if="currentUser && currentUser.role === 'admin'"
|
||||||
|
@click="toggleDrawer"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="/pleroma/admin/#/login-pleroma"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ $t("nav.administration") }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<li
|
<li
|
||||||
v-if="currentUser"
|
v-if="currentUser"
|
||||||
@click="toggleDrawer"
|
@click="toggleDrawer"
|
||||||
|
|
|
@ -10,11 +10,12 @@ import Gallery from '../gallery/gallery.vue'
|
||||||
import LinkPreview from '../link-preview/link-preview.vue'
|
import LinkPreview from '../link-preview/link-preview.vue'
|
||||||
import AvatarList from '../avatar_list/avatar_list.vue'
|
import AvatarList from '../avatar_list/avatar_list.vue'
|
||||||
import Timeago from '../timeago/timeago.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 generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||||
import fileType from 'src/services/file_type/file_type.service'
|
import fileType from 'src/services/file_type/file_type.service'
|
||||||
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
||||||
import { mentionMatchesUrl, extractTagFromUrl } from 'src/services/matcher/matcher.service.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'
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
const Status = {
|
const Status = {
|
||||||
|
@ -38,8 +39,6 @@ const Status = {
|
||||||
replying: false,
|
replying: false,
|
||||||
unmuted: false,
|
unmuted: false,
|
||||||
userExpanded: false,
|
userExpanded: false,
|
||||||
preview: null,
|
|
||||||
showPreview: false,
|
|
||||||
showingTall: this.inConversation && this.focused,
|
showingTall: this.inConversation && this.focused,
|
||||||
showingLongSubject: false,
|
showingLongSubject: false,
|
||||||
error: null,
|
error: null,
|
||||||
|
@ -293,7 +292,8 @@ const Status = {
|
||||||
Gallery,
|
Gallery,
|
||||||
LinkPreview,
|
LinkPreview,
|
||||||
AvatarList,
|
AvatarList,
|
||||||
Timeago
|
Timeago,
|
||||||
|
StatusPopover
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
visibilityIcon (visibility) {
|
visibilityIcon (visibility) {
|
||||||
|
@ -368,27 +368,6 @@ const Status = {
|
||||||
this.expandingSubject = true
|
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) {
|
generateUserProfileLink (id, name) {
|
||||||
return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames)
|
return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames)
|
||||||
},
|
},
|
||||||
|
|
|
@ -174,20 +174,26 @@
|
||||||
v-if="isReply"
|
v-if="isReply"
|
||||||
class="reply-to-and-accountname"
|
class="reply-to-and-accountname"
|
||||||
>
|
>
|
||||||
<a
|
<StatusPopover
|
||||||
class="reply-to"
|
v-if="!isPreview"
|
||||||
href="#"
|
:status-id="status.in_reply_to_status_id"
|
||||||
: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()"
|
|
||||||
>
|
>
|
||||||
<i
|
<a
|
||||||
v-if="!isPreview"
|
class="reply-to"
|
||||||
class="button-icon icon-reply"
|
href="#"
|
||||||
/>
|
:aria-label="$t('tool_tip.reply')"
|
||||||
<span class="faint-link reply-to-text">{{ $t('status.reply_to') }}</span>
|
@click.prevent="gotoOriginal(status.in_reply_to_status_id)"
|
||||||
</a>
|
>
|
||||||
|
<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">
|
<router-link :to="replyProfileLink">
|
||||||
{{ replyToName }}
|
{{ replyToName }}
|
||||||
</router-link>
|
</router-link>
|
||||||
|
@ -199,50 +205,25 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="inConversation && !isPreview"
|
v-if="inConversation && !isPreview && replies && replies.length"
|
||||||
class="replies"
|
class="replies"
|
||||||
>
|
>
|
||||||
<span
|
<span class="faint">{{ $t('status.replies_list') }}</span>
|
||||||
v-if="replies && replies.length"
|
<StatusPopover
|
||||||
class="faint"
|
v-for="reply in replies"
|
||||||
>{{ $t('status.replies_list') }}</span>
|
:key="reply.id"
|
||||||
<template v-if="replies">
|
:status-id="reply.id"
|
||||||
<span
|
>
|
||||||
v-for="reply in replies"
|
<a
|
||||||
:key="reply.id"
|
href="#"
|
||||||
class="reply-link faint"
|
class="reply-link"
|
||||||
>
|
@click.prevent="gotoOriginal(reply.id)"
|
||||||
<a
|
>{{ reply.name }}</a>
|
||||||
href="#"
|
</StatusPopover>
|
||||||
@click.prevent="gotoOriginal(reply.id)"
|
|
||||||
@mouseenter="replyEnter(reply.id, $event)"
|
|
||||||
@mouseout="replyLeave()"
|
|
||||||
>{{ reply.name }}</a>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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
|
<div
|
||||||
v-if="longSubject"
|
v-if="longSubject"
|
||||||
class="status-content-wrapper"
|
class="status-content-wrapper"
|
||||||
|
@ -439,18 +420,6 @@ $status-margin: 0.75em;
|
||||||
min-width: 0;
|
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 {
|
.status-pin {
|
||||||
padding: $status-margin $status-margin 0;
|
padding: $status-margin $status-margin 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -458,44 +427,6 @@ $status-margin: 0.75em;
|
||||||
justify-content: flex-end;
|
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 {
|
.media-left {
|
||||||
margin-right: $status-margin;
|
margin-right: $status-margin;
|
||||||
}
|
}
|
||||||
|
@ -553,11 +484,6 @@ $status-margin: 0.75em;
|
||||||
flex-basis: 100%;
|
flex-basis: 100%;
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
|
|
||||||
a {
|
|
||||||
display: inline-block;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
small {
|
small {
|
||||||
font-weight: lighter;
|
font-weight: lighter;
|
||||||
}
|
}
|
||||||
|
@ -568,6 +494,11 @@ $status-margin: 0.75em;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: inline-block;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
.name-and-account-name {
|
.name-and-account-name {
|
||||||
display: flex;
|
display: flex;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
@ -600,6 +531,7 @@ $status-margin: 0.75em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heading-reply-row {
|
.heading-reply-row {
|
||||||
|
position: relative;
|
||||||
align-content: baseline;
|
align-content: baseline;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
|
@ -608,11 +540,13 @@ $status-margin: 0.75em;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
|
||||||
a {
|
> .reply-to-and-accountname > a {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
display: inline-block;
|
||||||
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,6 +573,8 @@ $status-margin: 0.75em;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
margin: 0 0.4em 0 0.2em;
|
margin: 0 0.4em 0 0.2em;
|
||||||
|
color: $fallback--faint;
|
||||||
|
color: var(--faint, $fallback--faint);
|
||||||
}
|
}
|
||||||
|
|
||||||
.replies-separator {
|
.replies-separator {
|
||||||
|
@ -840,6 +776,11 @@ $status-margin: 0.75em;
|
||||||
&.button-icon-active {
|
&.button-icon-active {
|
||||||
color: $fallback--cBlue;
|
color: $fallback--cBlue;
|
||||||
color: var(--cBlue, $fallback--cBlue);
|
color: var(--cBlue, $fallback--cBlue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-icon.icon-reply {
|
||||||
|
&:not(.button-icon-disabled) {
|
||||||
cursor: pointer;
|
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',
|
'src',
|
||||||
'referrerpolicy',
|
'referrerpolicy',
|
||||||
'mimetype',
|
'mimetype',
|
||||||
'imageLoadError'
|
'imageLoadError',
|
||||||
|
'imageLoadHandler'
|
||||||
],
|
],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
@ -17,6 +18,7 @@ const StillImage = {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onLoad () {
|
onLoad () {
|
||||||
|
this.imageLoadHandler && this.imageLoadHandler(this.$refs.src)
|
||||||
const canvas = this.$refs.canvas
|
const canvas = this.$refs.canvas
|
||||||
if (!canvas) return
|
if (!canvas) return
|
||||||
const width = this.$refs.src.naturalWidth
|
const width = this.$refs.src.naturalWidth
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||||
import RemoteFollow from '../remote_follow/remote_follow.vue'
|
import RemoteFollow from '../remote_follow/remote_follow.vue'
|
||||||
import ProgressButton from '../progress_button/progress_button.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 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 { 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 generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: [ 'user', 'switcher', 'selected', 'hideBio', 'rounded', 'bordered', 'allowZoomingAvatar' ],
|
props: [
|
||||||
|
'user', 'switcher', 'selected', 'hideBio', 'rounded', 'bordered', 'allowZoomingAvatar'
|
||||||
|
],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
followRequestInProgress: false,
|
followRequestInProgress: false,
|
||||||
|
@ -96,30 +99,11 @@ export default {
|
||||||
UserAvatar,
|
UserAvatar,
|
||||||
RemoteFollow,
|
RemoteFollow,
|
||||||
ModerationTools,
|
ModerationTools,
|
||||||
ProgressButton
|
AccountActions,
|
||||||
|
ProgressButton,
|
||||||
|
FollowButton
|
||||||
},
|
},
|
||||||
methods: {
|
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 () {
|
muteUser () {
|
||||||
this.$store.dispatch('muteUser', this.user.id)
|
this.$store.dispatch('muteUser', this.user.id)
|
||||||
},
|
},
|
||||||
|
@ -147,10 +131,10 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
userProfileLink (user) {
|
userProfileLink (user) {
|
||||||
return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames)
|
return generateProfileLink(
|
||||||
},
|
user.id, user.screen_name,
|
||||||
reportUser () {
|
this.$store.state.instance.restrictedNicknames
|
||||||
this.$store.dispatch('openUserReportingModal', this.user.id)
|
)
|
||||||
},
|
},
|
||||||
zoomAvatar () {
|
zoomAvatar () {
|
||||||
const attachment = {
|
const attachment = {
|
||||||
|
@ -159,9 +143,6 @@ export default {
|
||||||
}
|
}
|
||||||
this.$store.dispatch('setMedia', [attachment])
|
this.$store.dispatch('setMedia', [attachment])
|
||||||
this.$store.dispatch('setCurrent', 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" />
|
<i class="icon-link-ext usersettings" />
|
||||||
</a>
|
</a>
|
||||||
|
<AccountActions
|
||||||
|
v-if="isOtherUser && loggedIn"
|
||||||
|
:user="user"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bottom-line">
|
<div class="bottom-line">
|
||||||
<router-link
|
<router-link
|
||||||
class="user-screen-name"
|
class="user-screen-name"
|
||||||
|
@ -135,72 +138,27 @@
|
||||||
v-if="loggedIn && isOtherUser"
|
v-if="loggedIn && isOtherUser"
|
||||||
class="user-interactions"
|
class="user-interactions"
|
||||||
>
|
>
|
||||||
<div v-if="!user.following">
|
<div class="btn-group">
|
||||||
<button
|
<FollowButton :user="user" />
|
||||||
class="btn btn-default btn-block"
|
<template v-if="user.following">
|
||||||
:disabled="followRequestInProgress"
|
<ProgressButton
|
||||||
:title="user.requested ? $t('user_card.follow_again') : ''"
|
v-if="!user.subscribed"
|
||||||
@click="followUser"
|
class="btn btn-default"
|
||||||
>
|
:click="subscribeUser"
|
||||||
<template v-if="followRequestInProgress">
|
:title="$t('user_card.subscribe')"
|
||||||
{{ $t('user_card.follow_progress') }}
|
>
|
||||||
</template>
|
<i class="icon-bell-alt" />
|
||||||
<template v-else-if="user.requested">
|
</ProgressButton>
|
||||||
{{ $t('user_card.follow_sent') }}
|
<ProgressButton
|
||||||
</template>
|
v-else
|
||||||
<template v-else>
|
class="btn btn-default pressed"
|
||||||
{{ $t('user_card.follow') }}
|
:click="unsubscribeUser"
|
||||||
</template>
|
:title="$t('user_card.unsubscribe')"
|
||||||
</button>
|
>
|
||||||
|
<i class="icon-bell-ringing-o" />
|
||||||
|
</ProgressButton>
|
||||||
|
</template>
|
||||||
</div>
|
</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>
|
<div>
|
||||||
<button
|
<button
|
||||||
v-if="user.muted"
|
v-if="user.muted"
|
||||||
|
@ -217,33 +175,6 @@
|
||||||
{{ $t('user_card.mute') }}
|
{{ $t('user_card.mute') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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
|
<ModerationTools
|
||||||
v-if="loggedIn.role === "admin""
|
v-if="loggedIn.role === "admin""
|
||||||
:user="user"
|
:user="user"
|
||||||
|
@ -587,13 +518,12 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
justify-content: space-between;
|
|
||||||
margin-right: -.75em;
|
margin-right: -.75em;
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
flex: 1 0 0;
|
|
||||||
margin: 0 .75em .6em 0;
|
margin: 0 .75em .6em 0;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
min-width: 95px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
import Status from '../status/status.vue'
|
import Status from '../status/status.vue'
|
||||||
import List from '../list/list.vue'
|
import List from '../list/list.vue'
|
||||||
import Checkbox from '../checkbox/checkbox.vue'
|
import Checkbox from '../checkbox/checkbox.vue'
|
||||||
|
import Modal from '../modal/modal.vue'
|
||||||
|
|
||||||
const UserReportingModal = {
|
const UserReportingModal = {
|
||||||
components: {
|
components: {
|
||||||
Status,
|
Status,
|
||||||
List,
|
List,
|
||||||
Checkbox
|
Checkbox,
|
||||||
|
Modal
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<Modal
|
||||||
v-if="isOpen"
|
v-if="isOpen"
|
||||||
class="modal-view"
|
@backdropClicked="closeModal"
|
||||||
@click="closeModal"
|
|
||||||
>
|
>
|
||||||
<div
|
<div class="user-reporting-panel panel">
|
||||||
class="user-reporting-panel panel"
|
|
||||||
@click.stop=""
|
|
||||||
>
|
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
{{ $t('user_reporting.title', [user.screen_name]) }}
|
{{ $t('user_reporting.title', [user.screen_name]) }}
|
||||||
|
@ -69,7 +65,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./user_reporting_modal.js"></script>
|
<script src="./user_reporting_modal.js"></script>
|
||||||
|
|
|
@ -2,42 +2,49 @@ import * as bodyScrollLock from 'body-scroll-lock'
|
||||||
|
|
||||||
let previousNavPaddingRight
|
let previousNavPaddingRight
|
||||||
let previousAppBgWrapperRight
|
let previousAppBgWrapperRight
|
||||||
|
const lockerEls = new Set([])
|
||||||
|
|
||||||
const disableBodyScroll = (el) => {
|
const disableBodyScroll = (el) => {
|
||||||
const scrollBarGap = window.innerWidth - document.documentElement.clientWidth
|
const scrollBarGap = window.innerWidth - document.documentElement.clientWidth
|
||||||
bodyScrollLock.disableBodyScroll(el, {
|
bodyScrollLock.disableBodyScroll(el, {
|
||||||
reserveScrollBarGap: true
|
reserveScrollBarGap: true
|
||||||
})
|
})
|
||||||
|
lockerEls.add(el)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// If previousNavPaddingRight is already set, don't set it again.
|
if (lockerEls.size <= 1) {
|
||||||
if (previousNavPaddingRight === undefined) {
|
// If previousNavPaddingRight is already set, don't set it again.
|
||||||
const navEl = document.getElementById('nav')
|
if (previousNavPaddingRight === undefined) {
|
||||||
previousNavPaddingRight = window.getComputedStyle(navEl).getPropertyValue('padding-right')
|
const navEl = document.getElementById('nav')
|
||||||
navEl.style.paddingRight = previousNavPaddingRight ? `calc(${previousNavPaddingRight} + ${scrollBarGap}px)` : `${scrollBarGap}px`
|
previousNavPaddingRight = window.getComputedStyle(navEl).getPropertyValue('padding-right')
|
||||||
|
navEl.style.paddingRight = previousNavPaddingRight ? `calc(${previousNavPaddingRight} + ${scrollBarGap}px)` : `${scrollBarGap}px`
|
||||||
|
}
|
||||||
|
// If previousAppBgWrapeprRight is already set, don't set it again.
|
||||||
|
if (previousAppBgWrapperRight === undefined) {
|
||||||
|
const appBgWrapperEl = document.getElementById('app_bg_wrapper')
|
||||||
|
previousAppBgWrapperRight = window.getComputedStyle(appBgWrapperEl).getPropertyValue('right')
|
||||||
|
appBgWrapperEl.style.right = previousAppBgWrapperRight ? `calc(${previousAppBgWrapperRight} + ${scrollBarGap}px)` : `${scrollBarGap}px`
|
||||||
|
}
|
||||||
|
document.body.classList.add('scroll-locked')
|
||||||
}
|
}
|
||||||
// If previousAppBgWrapeprRight is already set, don't set it again.
|
|
||||||
if (previousAppBgWrapperRight === undefined) {
|
|
||||||
const appBgWrapperEl = document.getElementById('app_bg_wrapper')
|
|
||||||
previousAppBgWrapperRight = window.getComputedStyle(appBgWrapperEl).getPropertyValue('right')
|
|
||||||
appBgWrapperEl.style.right = previousAppBgWrapperRight ? `calc(${previousAppBgWrapperRight} + ${scrollBarGap}px)` : `${scrollBarGap}px`
|
|
||||||
}
|
|
||||||
document.body.classList.add('scroll-locked')
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const enableBodyScroll = (el) => {
|
const enableBodyScroll = (el) => {
|
||||||
|
lockerEls.delete(el)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (previousNavPaddingRight !== undefined) {
|
if (lockerEls.size === 0) {
|
||||||
document.getElementById('nav').style.paddingRight = previousNavPaddingRight
|
if (previousNavPaddingRight !== undefined) {
|
||||||
// Restore previousNavPaddingRight to undefined so disableBodyScroll knows it can be set again.
|
document.getElementById('nav').style.paddingRight = previousNavPaddingRight
|
||||||
previousNavPaddingRight = undefined
|
// Restore previousNavPaddingRight to undefined so disableBodyScroll knows it can be set again.
|
||||||
|
previousNavPaddingRight = undefined
|
||||||
|
}
|
||||||
|
if (previousAppBgWrapperRight !== undefined) {
|
||||||
|
document.getElementById('app_bg_wrapper').style.right = previousAppBgWrapperRight
|
||||||
|
// Restore previousAppBgWrapperRight to undefined so disableBodyScroll knows it can be set again.
|
||||||
|
previousAppBgWrapperRight = undefined
|
||||||
|
}
|
||||||
|
document.body.classList.remove('scroll-locked')
|
||||||
}
|
}
|
||||||
if (previousAppBgWrapperRight !== undefined) {
|
|
||||||
document.getElementById('app_bg_wrapper').style.right = previousAppBgWrapperRight
|
|
||||||
// Restore previousAppBgWrapperRight to undefined so disableBodyScroll knows it can be set again.
|
|
||||||
previousAppBgWrapperRight = undefined
|
|
||||||
}
|
|
||||||
document.body.classList.remove('scroll-locked')
|
|
||||||
})
|
})
|
||||||
bodyScrollLock.enableBodyScroll(el)
|
bodyScrollLock.enableBodyScroll(el)
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
"about": "About",
|
"about": "About",
|
||||||
|
"administration": "Administration",
|
||||||
"back": "Back",
|
"back": "Back",
|
||||||
"chat": "Local Chat",
|
"chat": "Local Chat",
|
||||||
"friend_requests": "Follow Requests",
|
"friend_requests": "Follow Requests",
|
||||||
|
@ -554,6 +555,8 @@
|
||||||
"unmute": "Unmute",
|
"unmute": "Unmute",
|
||||||
"unmute_progress": "Unmuting...",
|
"unmute_progress": "Unmuting...",
|
||||||
"mute_progress": "Muting...",
|
"mute_progress": "Muting...",
|
||||||
|
"hide_repeats": "Hide repeats",
|
||||||
|
"show_repeats": "Show repeats",
|
||||||
"admin_menu": {
|
"admin_menu": {
|
||||||
"moderation": "Moderation",
|
"moderation": "Moderation",
|
||||||
"grant_admin": "Grant Admin",
|
"grant_admin": "Grant Admin",
|
||||||
|
@ -629,6 +632,8 @@
|
||||||
"return_home": "Return to the home page",
|
"return_home": "Return to the home page",
|
||||||
"not_found": "We couldn't find that email or username.",
|
"not_found": "We couldn't find that email or username.",
|
||||||
"too_many_requests": "You have reached the limit of attempts, try again later.",
|
"too_many_requests": "You have reached the limit of attempts, try again later.",
|
||||||
"password_reset_disabled": "Password reset is disabled. Please contact your instance administrator."
|
"password_reset_disabled": "Password reset is disabled. Please contact your instance administrator.",
|
||||||
|
"password_reset_required": "You must reset your password to log in.",
|
||||||
|
"password_reset_required_but_mailer_is_disabled": "You must reset your password, but password reset is disabled. Please contact your instance administrator."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,8 +45,8 @@
|
||||||
"error": "Se ha producido un error al importar el archivo."
|
"error": "Se ha producido un error al importar el archivo."
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"login": "Identificación",
|
"login": "Identificarse",
|
||||||
"description": "Identificación con OAuth",
|
"description": "Identificarse con OAuth",
|
||||||
"logout": "Cerrar sesión",
|
"logout": "Cerrar sesión",
|
||||||
"password": "Contraseña",
|
"password": "Contraseña",
|
||||||
"placeholder": "p.ej. lain",
|
"placeholder": "p.ej. lain",
|
||||||
|
@ -68,6 +68,7 @@
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
"about": "Acerca de",
|
"about": "Acerca de",
|
||||||
|
"administration": "Administración",
|
||||||
"back": "Volver",
|
"back": "Volver",
|
||||||
"chat": "Chat Local",
|
"chat": "Chat Local",
|
||||||
"friend_requests": "Solicitudes de seguimiento",
|
"friend_requests": "Solicitudes de seguimiento",
|
||||||
|
@ -106,6 +107,15 @@
|
||||||
"expired": "La encuesta terminó hace {0}",
|
"expired": "La encuesta terminó hace {0}",
|
||||||
"not_enough_options": "Muy pocas opciones únicas en la encuesta"
|
"not_enough_options": "Muy pocas opciones únicas en la encuesta"
|
||||||
},
|
},
|
||||||
|
"emoji": {
|
||||||
|
"stickers": "Pegatinas",
|
||||||
|
"emoji": "Emoji",
|
||||||
|
"keep_open": "Mantener el selector abierto",
|
||||||
|
"search_emoji": "Buscar un emoji",
|
||||||
|
"add_emoji": "Insertar un emoji",
|
||||||
|
"custom": "Emojis personalizados",
|
||||||
|
"unicode": "Emojis unicode"
|
||||||
|
},
|
||||||
"stickers": {
|
"stickers": {
|
||||||
"add_sticker": "Añadir Pegatina"
|
"add_sticker": "Añadir Pegatina"
|
||||||
},
|
},
|
||||||
|
@ -222,7 +232,9 @@
|
||||||
"data_import_export_tab": "Importar / Exportar Datos",
|
"data_import_export_tab": "Importar / Exportar Datos",
|
||||||
"default_vis": "Alcance de visibilidad por defecto",
|
"default_vis": "Alcance de visibilidad por defecto",
|
||||||
"delete_account": "Eliminar la cuenta",
|
"delete_account": "Eliminar la cuenta",
|
||||||
|
"discoverable": "Permitir la aparición de esta cuenta en los resultados de búsqueda y otros servicios",
|
||||||
"delete_account_description": "Eliminar para siempre la cuenta y todos los mensajes.",
|
"delete_account_description": "Eliminar para siempre la cuenta y todos los mensajes.",
|
||||||
|
"pad_emoji": "Rellenar con espacios al agregar emojis desde el selector",
|
||||||
"delete_account_error": "Hubo un error al eliminar tu cuenta. Si el fallo persiste, ponte en contacto con el administrador de tu instancia.",
|
"delete_account_error": "Hubo un error al eliminar tu cuenta. Si el fallo persiste, ponte en contacto con el administrador de tu instancia.",
|
||||||
"delete_account_instructions": "Escribe tu contraseña para confirmar la eliminación de tu cuenta.",
|
"delete_account_instructions": "Escribe tu contraseña para confirmar la eliminación de tu cuenta.",
|
||||||
"avatar_size_instruction": "El tamaño mínimo recomendado para el avatar es de 150X150 píxeles.",
|
"avatar_size_instruction": "El tamaño mínimo recomendado para el avatar es de 150X150 píxeles.",
|
||||||
|
@ -277,6 +289,8 @@
|
||||||
"no_mutes": "No hay usuarios sinlenciados",
|
"no_mutes": "No hay usuarios sinlenciados",
|
||||||
"hide_follows_description": "No mostrar a quién sigo",
|
"hide_follows_description": "No mostrar a quién sigo",
|
||||||
"hide_followers_description": "No mostrar quién me sigue",
|
"hide_followers_description": "No mostrar quién me sigue",
|
||||||
|
"hide_follows_count_description": "No mostrar el número de cuentas que sigo",
|
||||||
|
"hide_followers_count_description": "No mostrar el número de cuentas que me siguen",
|
||||||
"show_admin_badge": "Mostrar la insignia de Administrador en mi perfil",
|
"show_admin_badge": "Mostrar la insignia de Administrador en mi perfil",
|
||||||
"show_moderator_badge": "Mostrar la insignia de Moderador en mi perfil",
|
"show_moderator_badge": "Mostrar la insignia de Moderador en mi perfil",
|
||||||
"nsfw_clickthrough": "Activar el clic para ocultar los adjuntos NSFW",
|
"nsfw_clickthrough": "Activar el clic para ocultar los adjuntos NSFW",
|
||||||
|
@ -529,6 +543,7 @@
|
||||||
"follows_you": "¡Te sigue!",
|
"follows_you": "¡Te sigue!",
|
||||||
"its_you": "¡Eres tú!",
|
"its_you": "¡Eres tú!",
|
||||||
"media": "Media",
|
"media": "Media",
|
||||||
|
"mention": "Mencionar",
|
||||||
"mute": "Silenciar",
|
"mute": "Silenciar",
|
||||||
"muted": "Silenciado",
|
"muted": "Silenciado",
|
||||||
"per_day": "por día",
|
"per_day": "por día",
|
||||||
|
|
|
@ -68,6 +68,7 @@
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
"about": "Honi buruz",
|
"about": "Honi buruz",
|
||||||
|
"administration": "Administrazioa",
|
||||||
"back": "Atzera",
|
"back": "Atzera",
|
||||||
"chat": "Txat lokala",
|
"chat": "Txat lokala",
|
||||||
"friend_requests": "Jarraitzeko eskaerak",
|
"friend_requests": "Jarraitzeko eskaerak",
|
||||||
|
@ -106,6 +107,15 @@
|
||||||
"expired": "Inkesta {0} bukatu zen",
|
"expired": "Inkesta {0} bukatu zen",
|
||||||
"not_enough_options": "Aukera gutxiegi inkestan"
|
"not_enough_options": "Aukera gutxiegi inkestan"
|
||||||
},
|
},
|
||||||
|
"emoji": {
|
||||||
|
"stickers": "Pegatinak",
|
||||||
|
"emoji": "Emoji",
|
||||||
|
"keep_open": "Mantendu hautatzailea zabalik",
|
||||||
|
"search_emoji": "Bilatu emoji bat",
|
||||||
|
"add_emoji": "Emoji bat gehitu",
|
||||||
|
"custom": "Ohiko emojiak",
|
||||||
|
"unicode": "Unicode emojiak"
|
||||||
|
},
|
||||||
"stickers": {
|
"stickers": {
|
||||||
"add_sticker": "Pegatina gehitu"
|
"add_sticker": "Pegatina gehitu"
|
||||||
},
|
},
|
||||||
|
@ -199,12 +209,12 @@
|
||||||
"avatarRadius": "Avatarrak",
|
"avatarRadius": "Avatarrak",
|
||||||
"background": "Atzeko planoa",
|
"background": "Atzeko planoa",
|
||||||
"bio": "Biografia",
|
"bio": "Biografia",
|
||||||
"block_export": "Bloke esportatzea",
|
"block_export": "Blokeatu dituzunak esportatu",
|
||||||
"block_export_button": "Esportatu zure blokeak csv fitxategi batera",
|
"block_export_button": "Esportatu blokeatutakoak csv fitxategi batera",
|
||||||
"block_import": "Bloke inportazioa",
|
"block_import": "Blokeatu dituzunak inportatu",
|
||||||
"block_import_error": "Errorea blokeak inportatzen",
|
"block_import_error": "Errorea blokeatutakoak inportatzen",
|
||||||
"blocks_imported": "Blokeak inportaturik! Hauek prozesatzeak denbora hartuko du.",
|
"blocks_imported": "Blokeatutakoak inportaturik! Hauek prozesatzeak denbora hartuko du.",
|
||||||
"blocks_tab": "Blokeak",
|
"blocks_tab": "Blokeatutakoak",
|
||||||
"btnRadius": "Botoiak",
|
"btnRadius": "Botoiak",
|
||||||
"cBlue": "Urdina (erantzun, jarraitu)",
|
"cBlue": "Urdina (erantzun, jarraitu)",
|
||||||
"cGreen": "Berdea (Bertxiotu)",
|
"cGreen": "Berdea (Bertxiotu)",
|
||||||
|
@ -222,7 +232,9 @@
|
||||||
"data_import_export_tab": "Datuak Inportatu / Esportatu",
|
"data_import_export_tab": "Datuak Inportatu / Esportatu",
|
||||||
"default_vis": "Lehenetsitako ikusgaitasunak",
|
"default_vis": "Lehenetsitako ikusgaitasunak",
|
||||||
"delete_account": "Ezabatu kontua",
|
"delete_account": "Ezabatu kontua",
|
||||||
|
"discoverable": "Baimendu zure kontua kanpo bilaketa-emaitzetan eta bestelako zerbitzuetan agertzea",
|
||||||
"delete_account_description": "Betirako ezabatu zure kontua eta zure mezu guztiak",
|
"delete_account_description": "Betirako ezabatu zure kontua eta zure mezu guztiak",
|
||||||
|
"pad_emoji": "Zuriuneak gehitu emoji bat aukeratzen denean",
|
||||||
"delete_account_error": "Arazo bat gertatu da zure kontua ezabatzerakoan. Arazoa jarraitu eskero, administratzailearekin harremanetan jarri.",
|
"delete_account_error": "Arazo bat gertatu da zure kontua ezabatzerakoan. Arazoa jarraitu eskero, administratzailearekin harremanetan jarri.",
|
||||||
"delete_account_instructions": "Idatzi zure pasahitza kontua ezabatzeko.",
|
"delete_account_instructions": "Idatzi zure pasahitza kontua ezabatzeko.",
|
||||||
"avatar_size_instruction": "Avatar irudien gomendatutako gutxieneko tamaina 150x150 pixel dira.",
|
"avatar_size_instruction": "Avatar irudien gomendatutako gutxieneko tamaina 150x150 pixel dira.",
|
||||||
|
@ -254,7 +266,7 @@
|
||||||
"instance_default": "(lehenetsia: {value})",
|
"instance_default": "(lehenetsia: {value})",
|
||||||
"instance_default_simple": "(lehenetsia)",
|
"instance_default_simple": "(lehenetsia)",
|
||||||
"interface": "Interfazea",
|
"interface": "Interfazea",
|
||||||
"interfaceLanguage": "Interfaze hizkuntza",
|
"interfaceLanguage": "Interfazearen hizkuntza",
|
||||||
"invalid_theme_imported": "Hautatutako fitxategia ez da onartutako Pleroma gaia. Ez da zure gaian aldaketarik burutu.",
|
"invalid_theme_imported": "Hautatutako fitxategia ez da onartutako Pleroma gaia. Ez da zure gaian aldaketarik burutu.",
|
||||||
"limited_availability": "Ez dago erabilgarri zure nabigatzailean",
|
"limited_availability": "Ez dago erabilgarri zure nabigatzailean",
|
||||||
"links": "Estekak",
|
"links": "Estekak",
|
||||||
|
@ -277,6 +289,8 @@
|
||||||
"no_mutes": "Ez daude erabiltzaile mututuak",
|
"no_mutes": "Ez daude erabiltzaile mututuak",
|
||||||
"hide_follows_description": "Ez erakutsi nor jarraitzen ari naizen",
|
"hide_follows_description": "Ez erakutsi nor jarraitzen ari naizen",
|
||||||
"hide_followers_description": "Ez erakutsi nor ari den ni jarraitzen",
|
"hide_followers_description": "Ez erakutsi nor ari den ni jarraitzen",
|
||||||
|
"hide_follows_count_description": "Ez erakutsi jarraitzen ari naizen kontuen kopurua",
|
||||||
|
"hide_followers_count_description": "Ez erakutsi nire jarraitzaileen kontuen kopurua",
|
||||||
"show_admin_badge": "Erakutsi Administratzaile etiketa nire profilan",
|
"show_admin_badge": "Erakutsi Administratzaile etiketa nire profilan",
|
||||||
"show_moderator_badge": "Erakutsi Moderatzaile etiketa nire profilan",
|
"show_moderator_badge": "Erakutsi Moderatzaile etiketa nire profilan",
|
||||||
"nsfw_clickthrough": "Gaitu klika hunkigarri eranskinak ezkutatzeko",
|
"nsfw_clickthrough": "Gaitu klika hunkigarri eranskinak ezkutatzeko",
|
||||||
|
@ -449,7 +463,7 @@
|
||||||
},
|
},
|
||||||
"version": {
|
"version": {
|
||||||
"title": "Bertsioa",
|
"title": "Bertsioa",
|
||||||
"backend_version": "Backend Bertsio",
|
"backend_version": "Backend Bertsioa",
|
||||||
"frontend_version": "Frontend Bertsioa"
|
"frontend_version": "Frontend Bertsioa"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -529,6 +543,7 @@
|
||||||
"follows_you": "Jarraitzen dizu!",
|
"follows_you": "Jarraitzen dizu!",
|
||||||
"its_you": "Zu zara!",
|
"its_you": "Zu zara!",
|
||||||
"media": "Multimedia",
|
"media": "Multimedia",
|
||||||
|
"mention": "Aipatu",
|
||||||
"mute": "Isilarazi",
|
"mute": "Isilarazi",
|
||||||
"muted": "Isilduta",
|
"muted": "Isilduta",
|
||||||
"per_day": "eguneko",
|
"per_day": "eguneko",
|
||||||
|
|
|
@ -106,6 +106,15 @@
|
||||||
"expired": "いれふだは {0} まえに、おわりました",
|
"expired": "いれふだは {0} まえに、おわりました",
|
||||||
"not_enough_options": "ユニークなオプションが、たりません"
|
"not_enough_options": "ユニークなオプションが、たりません"
|
||||||
},
|
},
|
||||||
|
"emoji": {
|
||||||
|
"stickers": "ステッカー",
|
||||||
|
"emoji": "えもじ",
|
||||||
|
"keep_open": "ピッカーをあけたままにする",
|
||||||
|
"search_emoji": "えもじをさがす",
|
||||||
|
"add_emoji": "えもじをうちこむ",
|
||||||
|
"custom": "カスタムえもじ",
|
||||||
|
"unicode": "ユニコードえもじ"
|
||||||
|
},
|
||||||
"stickers": {
|
"stickers": {
|
||||||
"add_sticker": "ステッカーをふやす"
|
"add_sticker": "ステッカーをふやす"
|
||||||
},
|
},
|
||||||
|
@ -224,10 +233,11 @@
|
||||||
"default_vis": "デフォルトのこうかいはんい",
|
"default_vis": "デフォルトのこうかいはんい",
|
||||||
"delete_account": "アカウントをけす",
|
"delete_account": "アカウントをけす",
|
||||||
"delete_account_description": "あなたのアカウントとメッセージが、きえます。",
|
"delete_account_description": "あなたのアカウントとメッセージが、きえます。",
|
||||||
"delete_account_error": "アカウントをけすことが、できなかったかもしれません。インスタンスのかんりしゃに、れんらくしてください。",
|
"delete_account_error": "アカウントをけすことが、できなかったかもしれません。インスタンスのアドミニストレーターに、おといあわせください。",
|
||||||
"delete_account_instructions": "ほんとうにアカウントをけしてもいいなら、パスワードをかいてください。",
|
"delete_account_instructions": "ほんとうにアカウントをけしてもいいなら、パスワードをかいてください。",
|
||||||
"discoverable": "けんさくなどのサービスで、このアカウントをみつけてもよい",
|
"discoverable": "けんさくなどのサービスで、このアカウントをみつけてもよい",
|
||||||
"avatar_size_instruction": "アバターのおおきさは、150×150ピクセルか、それよりもおおきくするといいです。",
|
"avatar_size_instruction": "アバターのおおきさは、150×150ピクセルか、それよりもおおきくするといいです。",
|
||||||
|
"pad_emoji": "えもじをピッカーでえらんだとき、えもじのまわりにスペースをいれる",
|
||||||
"export_theme": "セーブ",
|
"export_theme": "セーブ",
|
||||||
"filtering": "フィルタリング",
|
"filtering": "フィルタリング",
|
||||||
"filtering_explanation": "これらのことばをふくむすべてのものがミュートされます。1ぎょうに1つのことばをかいてください。",
|
"filtering_explanation": "これらのことばをふくむすべてのものがミュートされます。1ぎょうに1つのことばをかいてください。",
|
||||||
|
@ -280,6 +290,8 @@
|
||||||
"no_mutes": "ミュートしていません",
|
"no_mutes": "ミュートしていません",
|
||||||
"hide_follows_description": "フォローしているひとをみせない",
|
"hide_follows_description": "フォローしているひとをみせない",
|
||||||
"hide_followers_description": "フォロワーをみせない",
|
"hide_followers_description": "フォロワーをみせない",
|
||||||
|
"hide_follows_count_description": "フォローしているひとのかずをみせない",
|
||||||
|
"hide_followers_count_description": "フォロワーのかずをみせない",
|
||||||
"show_admin_badge": "アドミンのしるしをみせる",
|
"show_admin_badge": "アドミンのしるしをみせる",
|
||||||
"show_moderator_badge": "モデレーターのしるしをみせる",
|
"show_moderator_badge": "モデレーターのしるしをみせる",
|
||||||
"nsfw_clickthrough": "NSFWなファイルをかくす",
|
"nsfw_clickthrough": "NSFWなファイルをかくす",
|
||||||
|
@ -532,6 +544,7 @@
|
||||||
"follows_you": "フォローされました!",
|
"follows_you": "フォローされました!",
|
||||||
"its_you": "これはあなたです!",
|
"its_you": "これはあなたです!",
|
||||||
"media": "メディア",
|
"media": "メディア",
|
||||||
|
"mention": "メンション",
|
||||||
"mute": "ミュート",
|
"mute": "ミュート",
|
||||||
"muted": "ミュートしています!",
|
"muted": "ミュートしています!",
|
||||||
"per_day": "/日",
|
"per_day": "/日",
|
||||||
|
@ -611,5 +624,16 @@
|
||||||
"person_talking": "{count} にんが、はなしています",
|
"person_talking": "{count} にんが、はなしています",
|
||||||
"people_talking": "{count} にんが、はなしています",
|
"people_talking": "{count} にんが、はなしています",
|
||||||
"no_results": "みつかりませんでした"
|
"no_results": "みつかりませんでした"
|
||||||
|
},
|
||||||
|
"password_reset": {
|
||||||
|
"forgot_password": "パスワードを、わすれましたか?",
|
||||||
|
"password_reset": "パスワードリセット",
|
||||||
|
"instruction": "あなたのメールアドレスかユーザーめいをいれてください。パスワードをリセットするためのリンクをおくります。",
|
||||||
|
"placeholder": "あなたのメールアドレスかユーザーめい",
|
||||||
|
"check_email": "パスワードをリセットするためのリンクがかかれたメールが、とどいているかどうか、みてください。",
|
||||||
|
"return_home": "ホームページにもどる",
|
||||||
|
"not_found": "そのメールアドレスまたはユーザーめいを、みつけることができませんでした。",
|
||||||
|
"too_many_requests": "パスワードリセットを、ためすことが、おおすぎます。しばらくしてから、ためしてください。",
|
||||||
|
"password_reset_disabled": "このインスタンスでは、パスワードリセットは、できません。インスタンスのアドミニストレーターに、おといあわせください。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,6 +106,15 @@
|
||||||
"expired": "投票は {0} 前に終了しました",
|
"expired": "投票は {0} 前に終了しました",
|
||||||
"not_enough_options": "相異なる選択肢が不足しています"
|
"not_enough_options": "相異なる選択肢が不足しています"
|
||||||
},
|
},
|
||||||
|
"emoji": {
|
||||||
|
"stickers": "ステッカー",
|
||||||
|
"emoji": "絵文字",
|
||||||
|
"keep_open": "ピッカーを開いたままにする",
|
||||||
|
"search_emoji": "絵文字を検索",
|
||||||
|
"add_emoji": "絵文字を挿入",
|
||||||
|
"custom": "カスタム絵文字",
|
||||||
|
"unicode": "Unicode絵文字"
|
||||||
|
},
|
||||||
"stickers": {
|
"stickers": {
|
||||||
"add_sticker": "ステッカーを追加"
|
"add_sticker": "ステッカーを追加"
|
||||||
},
|
},
|
||||||
|
@ -228,6 +237,7 @@
|
||||||
"delete_account_instructions": "本当にアカウントを消してもいいなら、パスワードを入力してください。",
|
"delete_account_instructions": "本当にアカウントを消してもいいなら、パスワードを入力してください。",
|
||||||
"discoverable": "検索などのサービスでこのアカウントを見つけることを許可する",
|
"discoverable": "検索などのサービスでこのアカウントを見つけることを許可する",
|
||||||
"avatar_size_instruction": "アバターの大きさは、150×150ピクセルか、それよりも大きくするといいです。",
|
"avatar_size_instruction": "アバターの大きさは、150×150ピクセルか、それよりも大きくするといいです。",
|
||||||
|
"pad_emoji": "ピッカーから絵文字を挿入するとき、絵文字の両側にスペースを入れる",
|
||||||
"export_theme": "保存",
|
"export_theme": "保存",
|
||||||
"filtering": "フィルタリング",
|
"filtering": "フィルタリング",
|
||||||
"filtering_explanation": "これらの言葉を含むすべてのものがミュートされます。1行に1つの言葉を書いてください。",
|
"filtering_explanation": "これらの言葉を含むすべてのものがミュートされます。1行に1つの言葉を書いてください。",
|
||||||
|
@ -280,6 +290,8 @@
|
||||||
"no_mutes": "ミュートはありません",
|
"no_mutes": "ミュートはありません",
|
||||||
"hide_follows_description": "フォローしている人を見せない",
|
"hide_follows_description": "フォローしている人を見せない",
|
||||||
"hide_followers_description": "フォロワーを見せない",
|
"hide_followers_description": "フォロワーを見せない",
|
||||||
|
"hide_follows_count_description": "フォローしている人の数を見せない",
|
||||||
|
"hide_followers_count_description": "フォロワーの数を見せない",
|
||||||
"show_admin_badge": "管理者のバッジを見せる",
|
"show_admin_badge": "管理者のバッジを見せる",
|
||||||
"show_moderator_badge": "モデレーターのバッジを見せる",
|
"show_moderator_badge": "モデレーターのバッジを見せる",
|
||||||
"nsfw_clickthrough": "NSFWなファイルを隠す",
|
"nsfw_clickthrough": "NSFWなファイルを隠す",
|
||||||
|
@ -532,6 +544,7 @@
|
||||||
"follows_you": "フォローされました!",
|
"follows_you": "フォローされました!",
|
||||||
"its_you": "これはあなたです!",
|
"its_you": "これはあなたです!",
|
||||||
"media": "メディア",
|
"media": "メディア",
|
||||||
|
"mention": "メンション",
|
||||||
"mute": "ミュート",
|
"mute": "ミュート",
|
||||||
"muted": "ミュートしています!",
|
"muted": "ミュートしています!",
|
||||||
"per_day": "/日",
|
"per_day": "/日",
|
||||||
|
@ -611,5 +624,16 @@
|
||||||
"person_talking": "{count} 人が話しています",
|
"person_talking": "{count} 人が話しています",
|
||||||
"people_talking": "{count} 人が話しています",
|
"people_talking": "{count} 人が話しています",
|
||||||
"no_results": "見つかりませんでした"
|
"no_results": "見つかりませんでした"
|
||||||
|
},
|
||||||
|
"password_reset": {
|
||||||
|
"forgot_password": "パスワードを忘れましたか?",
|
||||||
|
"password_reset": "パスワードリセット",
|
||||||
|
"instruction": "メールアドレスまたはユーザー名を入力してください。パスワードをリセットするためのリンクを送信します。",
|
||||||
|
"placeholder": "メールアドレスまたはユーザー名",
|
||||||
|
"check_email": "パスワードをリセットするためのリンクが記載されたメールが届いているか確認してください。",
|
||||||
|
"return_home": "ホームページに戻る",
|
||||||
|
"not_found": "メールアドレスまたはユーザー名が見つかりませんでした。",
|
||||||
|
"too_many_requests": "試行回数の制限に達しました。しばらく時間を置いてから再試行してください。",
|
||||||
|
"password_reset_disabled": "このインスタンスではパスワードリセットは無効になっています。インスタンスの管理者に連絡してください。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,13 @@ Vue.use(VueChatScroll)
|
||||||
Vue.use(VueClickOutside)
|
Vue.use(VueClickOutside)
|
||||||
Vue.use(PortalVue)
|
Vue.use(PortalVue)
|
||||||
Vue.use(VBodyScrollLock)
|
Vue.use(VBodyScrollLock)
|
||||||
Vue.use(VTooltip)
|
Vue.use(VTooltip, {
|
||||||
|
popover: {
|
||||||
|
defaultTrigger: 'hover click',
|
||||||
|
defaultContainer: false,
|
||||||
|
defaultOffset: 5
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const i18n = new VueI18n({
|
const i18n = new VueI18n({
|
||||||
// By default, use the browser locale, we will update it if neccessary
|
// By default, use the browser locale, we will update it if neccessary
|
||||||
|
|
|
@ -537,6 +537,10 @@ const statuses = {
|
||||||
setNotificationsSilence ({ rootState, commit }, { value }) {
|
setNotificationsSilence ({ rootState, commit }, { value }) {
|
||||||
commit('setNotificationsSilence', { value })
|
commit('setNotificationsSilence', { value })
|
||||||
},
|
},
|
||||||
|
fetchStatus ({ rootState, dispatch }, id) {
|
||||||
|
rootState.api.backendInteractor.fetchStatus({ id })
|
||||||
|
.then((status) => dispatch('addNewStatuses', { statuses: [status] }))
|
||||||
|
},
|
||||||
deleteStatus ({ rootState, commit }, status) {
|
deleteStatus ({ rootState, commit }, status) {
|
||||||
commit('setDeleted', { status })
|
commit('setDeleted', { status })
|
||||||
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
|
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
|
||||||
|
|
|
@ -60,6 +60,18 @@ const unmuteUser = (store, id) => {
|
||||||
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hideReblogs = (store, userId) => {
|
||||||
|
return store.rootState.api.backendInteractor.followUser({ id: userId, reblogs: false })
|
||||||
|
.then((relationship) => {
|
||||||
|
store.commit('updateUserRelationship', [relationship])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const showReblogs = (store, userId) => {
|
||||||
|
return store.rootState.api.backendInteractor.followUser({ id: userId, reblogs: true })
|
||||||
|
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
||||||
|
}
|
||||||
|
|
||||||
export const mutations = {
|
export const mutations = {
|
||||||
setMuted (state, { user: { id }, muted }) {
|
setMuted (state, { user: { id }, muted }) {
|
||||||
const user = state.usersObject[id]
|
const user = state.usersObject[id]
|
||||||
|
@ -135,6 +147,7 @@ export const mutations = {
|
||||||
user.muted = relationship.muting
|
user.muted = relationship.muting
|
||||||
user.statusnet_blocking = relationship.blocking
|
user.statusnet_blocking = relationship.blocking
|
||||||
user.subscribed = relationship.subscribing
|
user.subscribed = relationship.subscribing
|
||||||
|
user.showing_reblogs = relationship.showing_reblogs
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -272,6 +285,12 @@ const users = {
|
||||||
unmuteUser (store, id) {
|
unmuteUser (store, id) {
|
||||||
return unmuteUser(store, id)
|
return unmuteUser(store, id)
|
||||||
},
|
},
|
||||||
|
hideReblogs (store, id) {
|
||||||
|
return hideReblogs(store, id)
|
||||||
|
},
|
||||||
|
showReblogs (store, id) {
|
||||||
|
return showReblogs(store, id)
|
||||||
|
},
|
||||||
muteUsers (store, ids = []) {
|
muteUsers (store, ids = []) {
|
||||||
return Promise.all(ids.map(id => muteUser(store, id)))
|
return Promise.all(ids.map(id => muteUser(store, id)))
|
||||||
},
|
},
|
||||||
|
|
|
@ -219,10 +219,16 @@ const authHeaders = (accessToken) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const followUser = ({ id, credentials }) => {
|
const followUser = ({ id, credentials, ...options }) => {
|
||||||
let url = MASTODON_FOLLOW_URL(id)
|
let url = MASTODON_FOLLOW_URL(id)
|
||||||
|
const form = {}
|
||||||
|
if (options.reblogs !== undefined) { form['reblogs'] = options.reblogs }
|
||||||
return fetch(url, {
|
return fetch(url, {
|
||||||
headers: authHeaders(credentials),
|
body: JSON.stringify(form),
|
||||||
|
headers: {
|
||||||
|
...authHeaders(credentials),
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
}).then((data) => data.json())
|
}).then((data) => data.json())
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,8 @@ const backendInteractorService = credentials => {
|
||||||
return apiService.fetchUserRelationship({ id, credentials })
|
return apiService.fetchUserRelationship({ id, credentials })
|
||||||
}
|
}
|
||||||
|
|
||||||
const followUser = (id) => {
|
const followUser = ({ id, reblogs }) => {
|
||||||
return apiService.followUser({ credentials, id })
|
return apiService.followUser({ credentials, id, reblogs })
|
||||||
}
|
}
|
||||||
|
|
||||||
const unfollowUser = (id) => {
|
const unfollowUser = (id) => {
|
||||||
|
|
|
@ -69,6 +69,7 @@ export const parseUser = (data) => {
|
||||||
output.following = relationship.following
|
output.following = relationship.following
|
||||||
output.statusnet_blocking = relationship.blocking
|
output.statusnet_blocking = relationship.blocking
|
||||||
output.muted = relationship.muting
|
output.muted = relationship.muting
|
||||||
|
output.showing_reblogs = relationship.showing_reblogs
|
||||||
output.subscribed = relationship.subscribing
|
output.subscribed = relationship.subscribing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,9 +197,11 @@ export const parseAttachment = (data) => {
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
export const addEmojis = (string, emojis) => {
|
export const addEmojis = (string, emojis) => {
|
||||||
|
const matchOperatorsRegex = /[|\\{}()[\]^$+*?.-]/g
|
||||||
return emojis.reduce((acc, emoji) => {
|
return emojis.reduce((acc, emoji) => {
|
||||||
|
const regexSafeShortCode = emoji.shortcode.replace(matchOperatorsRegex, '\\$&')
|
||||||
return acc.replace(
|
return acc.replace(
|
||||||
new RegExp(`:${emoji.shortcode}:`, 'g'),
|
new RegExp(`:${regexSafeShortCode}:`, 'g'),
|
||||||
`<img src='${emoji.url}' alt='${emoji.shortcode}' title='${emoji.shortcode}' class='emoji' />`
|
`<img src='${emoji.url}' alt='${emoji.shortcode}' title='${emoji.shortcode}' class='emoji' />`
|
||||||
)
|
)
|
||||||
}, string)
|
}, string)
|
||||||
|
|
|
@ -14,7 +14,7 @@ const fetchUser = (attempt, user, store) => new Promise((resolve, reject) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
export const requestFollow = (user, store) => new Promise((resolve, reject) => {
|
export const requestFollow = (user, store) => new Promise((resolve, reject) => {
|
||||||
store.state.api.backendInteractor.followUser(user.id)
|
store.state.api.backendInteractor.followUser({ id: user.id })
|
||||||
.then((updated) => {
|
.then((updated) => {
|
||||||
store.commit('updateUserRelationship', [updated])
|
store.commit('updateUserRelationship', [updated])
|
||||||
|
|
||||||
|
|
0
static/font/LICENSE.txt
Normal file → Executable file
0
static/font/LICENSE.txt
Normal file → Executable file
0
static/font/README.txt
Normal file → Executable file
0
static/font/README.txt
Normal file → Executable file
6
static/font/config.json
Normal file → Executable file
6
static/font/config.json
Normal file → Executable file
|
@ -297,6 +297,12 @@
|
||||||
"css": "zoom-in",
|
"css": "zoom-in",
|
||||||
"code": 59420,
|
"code": 59420,
|
||||||
"src": "fontawesome"
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "0bda4bc779d4c32623dec2e43bd67ee8",
|
||||||
|
"css": "gauge",
|
||||||
|
"code": 61668,
|
||||||
|
"src": "fontawesome"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
0
static/font/css/animation.css
Normal file → Executable file
0
static/font/css/animation.css
Normal file → Executable file
1
static/font/css/fontello-codes.css
vendored
Normal file → Executable file
1
static/font/css/fontello-codes.css
vendored
Normal file → Executable file
|
@ -34,6 +34,7 @@
|
||||||
.icon-link-ext-alt:before { content: '\f08f'; } /* '' */
|
.icon-link-ext-alt:before { content: '\f08f'; } /* '' */
|
||||||
.icon-menu:before { content: '\f0c9'; } /* '' */
|
.icon-menu:before { content: '\f0c9'; } /* '' */
|
||||||
.icon-mail-alt:before { content: '\f0e0'; } /* '' */
|
.icon-mail-alt:before { content: '\f0e0'; } /* '' */
|
||||||
|
.icon-gauge:before { content: '\f0e4'; } /* '' */
|
||||||
.icon-comment-empty:before { content: '\f0e5'; } /* '' */
|
.icon-comment-empty:before { content: '\f0e5'; } /* '' */
|
||||||
.icon-bell-alt:before { content: '\f0f3'; } /* '' */
|
.icon-bell-alt:before { content: '\f0f3'; } /* '' */
|
||||||
.icon-plus-squared:before { content: '\f0fe'; } /* '' */
|
.icon-plus-squared:before { content: '\f0fe'; } /* '' */
|
||||||
|
|
14
static/font/css/fontello-embedded.css
vendored
Normal file → Executable file
14
static/font/css/fontello-embedded.css
vendored
Normal file → Executable file
File diff suppressed because one or more lines are too long
1
static/font/css/fontello-ie7-codes.css
vendored
Normal file → Executable file
1
static/font/css/fontello-ie7-codes.css
vendored
Normal file → Executable file
|
@ -34,6 +34,7 @@
|
||||||
.icon-link-ext-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-link-ext-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-menu { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-menu { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-mail-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-mail-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-gauge { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-comment-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-comment-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-bell-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-bell-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-plus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-plus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
|
1
static/font/css/fontello-ie7.css
vendored
Normal file → Executable file
1
static/font/css/fontello-ie7.css
vendored
Normal file → Executable file
|
@ -45,6 +45,7 @@
|
||||||
.icon-link-ext-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-link-ext-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-menu { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-menu { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-mail-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-mail-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-gauge { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-comment-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-comment-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-bell-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-bell-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-plus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-plus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
|
16
static/font/css/fontello.css
vendored
Normal file → Executable file
16
static/font/css/fontello.css
vendored
Normal file → Executable file
|
@ -1,11 +1,11 @@
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'fontello';
|
font-family: 'fontello';
|
||||||
src: url('../font/fontello.eot?94788965');
|
src: url('../font/fontello.eot?70867224');
|
||||||
src: url('../font/fontello.eot?94788965#iefix') format('embedded-opentype'),
|
src: url('../font/fontello.eot?70867224#iefix') format('embedded-opentype'),
|
||||||
url('../font/fontello.woff2?94788965') format('woff2'),
|
url('../font/fontello.woff2?70867224') format('woff2'),
|
||||||
url('../font/fontello.woff?94788965') format('woff'),
|
url('../font/fontello.woff?70867224') format('woff'),
|
||||||
url('../font/fontello.ttf?94788965') format('truetype'),
|
url('../font/fontello.ttf?70867224') format('truetype'),
|
||||||
url('../font/fontello.svg?94788965#fontello') format('svg');
|
url('../font/fontello.svg?70867224#fontello') format('svg');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'fontello';
|
font-family: 'fontello';
|
||||||
src: url('../font/fontello.svg?94788965#fontello') format('svg');
|
src: url('../font/fontello.svg?70867224#fontello') format('svg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
@ -83,12 +83,14 @@
|
||||||
.icon-pin:before { content: '\e819'; } /* '' */
|
.icon-pin:before { content: '\e819'; } /* '' */
|
||||||
.icon-wrench:before { content: '\e81a'; } /* '' */
|
.icon-wrench:before { content: '\e81a'; } /* '' */
|
||||||
.icon-chart-bar:before { content: '\e81b'; } /* '' */
|
.icon-chart-bar:before { content: '\e81b'; } /* '' */
|
||||||
|
.icon-zoom-in:before { content: '\e81c'; } /* '' */
|
||||||
.icon-spin3:before { content: '\e832'; } /* '' */
|
.icon-spin3:before { content: '\e832'; } /* '' */
|
||||||
.icon-spin4:before { content: '\e834'; } /* '' */
|
.icon-spin4:before { content: '\e834'; } /* '' */
|
||||||
.icon-link-ext:before { content: '\f08e'; } /* '' */
|
.icon-link-ext:before { content: '\f08e'; } /* '' */
|
||||||
.icon-link-ext-alt:before { content: '\f08f'; } /* '' */
|
.icon-link-ext-alt:before { content: '\f08f'; } /* '' */
|
||||||
.icon-menu:before { content: '\f0c9'; } /* '' */
|
.icon-menu:before { content: '\f0c9'; } /* '' */
|
||||||
.icon-mail-alt:before { content: '\f0e0'; } /* '' */
|
.icon-mail-alt:before { content: '\f0e0'; } /* '' */
|
||||||
|
.icon-gauge:before { content: '\f0e4'; } /* '' */
|
||||||
.icon-comment-empty:before { content: '\f0e5'; } /* '' */
|
.icon-comment-empty:before { content: '\f0e5'; } /* '' */
|
||||||
.icon-bell-alt:before { content: '\f0f3'; } /* '' */
|
.icon-bell-alt:before { content: '\f0f3'; } /* '' */
|
||||||
.icon-plus-squared:before { content: '\f0fe'; } /* '' */
|
.icon-plus-squared:before { content: '\f0fe'; } /* '' */
|
||||||
|
|
26
static/font/demo.html
Normal file → Executable file
26
static/font/demo.html
Normal file → Executable file
|
@ -229,11 +229,11 @@ body {
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'fontello';
|
font-family: 'fontello';
|
||||||
src: url('./font/fontello.eot?31206390');
|
src: url('./font/fontello.eot?56851497');
|
||||||
src: url('./font/fontello.eot?31206390#iefix') format('embedded-opentype'),
|
src: url('./font/fontello.eot?56851497#iefix') format('embedded-opentype'),
|
||||||
url('./font/fontello.woff?31206390') format('woff'),
|
url('./font/fontello.woff?56851497') format('woff'),
|
||||||
url('./font/fontello.ttf?31206390') format('truetype'),
|
url('./font/fontello.ttf?56851497') format('truetype'),
|
||||||
url('./font/fontello.svg?31206390#fontello') format('svg');
|
url('./font/fontello.svg?56851497#fontello') format('svg');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
@ -340,30 +340,32 @@ body {
|
||||||
<div class="the-icons span3" title="Code: 0xe81b"><i class="demo-icon icon-chart-bar"></i> <span class="i-name">icon-chart-bar</span><span class="i-code">0xe81b</span></div>
|
<div class="the-icons span3" title="Code: 0xe81b"><i class="demo-icon icon-chart-bar"></i> <span class="i-name">icon-chart-bar</span><span class="i-code">0xe81b</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<div class="the-icons span3" title="Code: 0xe81c"><i class="demo-icon icon-zoom-in"></i> <span class="i-name">icon-zoom-in</span><span class="i-code">0xe81c</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xe832"><i class="demo-icon icon-spin3 animate-spin"></i> <span class="i-name">icon-spin3</span><span class="i-code">0xe832</span></div>
|
<div class="the-icons span3" title="Code: 0xe832"><i class="demo-icon icon-spin3 animate-spin"></i> <span class="i-name">icon-spin3</span><span class="i-code">0xe832</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xe834"><i class="demo-icon icon-spin4 animate-spin"></i> <span class="i-name">icon-spin4</span><span class="i-code">0xe834</span></div>
|
<div class="the-icons span3" title="Code: 0xe834"><i class="demo-icon icon-spin4 animate-spin"></i> <span class="i-name">icon-spin4</span><span class="i-code">0xe834</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext"></i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div>
|
<div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext"></i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf08f"><i class="demo-icon icon-link-ext-alt"></i> <span class="i-name">icon-link-ext-alt</span><span class="i-code">0xf08f</span></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<div class="the-icons span3" title="Code: 0xf08f"><i class="demo-icon icon-link-ext-alt"></i> <span class="i-name">icon-link-ext-alt</span><span class="i-code">0xf08f</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf0c9"><i class="demo-icon icon-menu"></i> <span class="i-name">icon-menu</span><span class="i-code">0xf0c9</span></div>
|
<div class="the-icons span3" title="Code: 0xf0c9"><i class="demo-icon icon-menu"></i> <span class="i-name">icon-menu</span><span class="i-code">0xf0c9</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf0e0"><i class="demo-icon icon-mail-alt"></i> <span class="i-name">icon-mail-alt</span><span class="i-code">0xf0e0</span></div>
|
<div class="the-icons span3" title="Code: 0xf0e0"><i class="demo-icon icon-mail-alt"></i> <span class="i-name">icon-mail-alt</span><span class="i-code">0xf0e0</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xf0e4"><i class="demo-icon icon-gauge"></i> <span class="i-name">icon-gauge</span><span class="i-code">0xf0e4</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
<div class="the-icons span3" title="Code: 0xf0e5"><i class="demo-icon icon-comment-empty"></i> <span class="i-name">icon-comment-empty</span><span class="i-code">0xf0e5</span></div>
|
<div class="the-icons span3" title="Code: 0xf0e5"><i class="demo-icon icon-comment-empty"></i> <span class="i-name">icon-comment-empty</span><span class="i-code">0xf0e5</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf0f3"><i class="demo-icon icon-bell-alt"></i> <span class="i-name">icon-bell-alt</span><span class="i-code">0xf0f3</span></div>
|
<div class="the-icons span3" title="Code: 0xf0f3"><i class="demo-icon icon-bell-alt"></i> <span class="i-name">icon-bell-alt</span><span class="i-code">0xf0f3</span></div>
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="the-icons span3" title="Code: 0xf0fe"><i class="demo-icon icon-plus-squared"></i> <span class="i-name">icon-plus-squared</span><span class="i-code">0xf0fe</span></div>
|
<div class="the-icons span3" title="Code: 0xf0fe"><i class="demo-icon icon-plus-squared"></i> <span class="i-name">icon-plus-squared</span><span class="i-code">0xf0fe</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf112"><i class="demo-icon icon-reply"></i> <span class="i-name">icon-reply</span><span class="i-code">0xf112</span></div>
|
<div class="the-icons span3" title="Code: 0xf112"><i class="demo-icon icon-reply"></i> <span class="i-name">icon-reply</span><span class="i-code">0xf112</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
<div class="the-icons span3" title="Code: 0xf118"><i class="demo-icon icon-smile"></i> <span class="i-name">icon-smile</span><span class="i-code">0xf118</span></div>
|
<div class="the-icons span3" title="Code: 0xf118"><i class="demo-icon icon-smile"></i> <span class="i-name">icon-smile</span><span class="i-code">0xf118</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf13e"><i class="demo-icon icon-lock-open-alt"></i> <span class="i-name">icon-lock-open-alt</span><span class="i-code">0xf13e</span></div>
|
<div class="the-icons span3" title="Code: 0xf13e"><i class="demo-icon icon-lock-open-alt"></i> <span class="i-name">icon-lock-open-alt</span><span class="i-code">0xf13e</span></div>
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="the-icons span3" title="Code: 0xf141"><i class="demo-icon icon-ellipsis"></i> <span class="i-name">icon-ellipsis</span><span class="i-code">0xf141</span></div>
|
<div class="the-icons span3" title="Code: 0xf141"><i class="demo-icon icon-ellipsis"></i> <span class="i-name">icon-ellipsis</span><span class="i-code">0xf141</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf144"><i class="demo-icon icon-play-circled"></i> <span class="i-name">icon-play-circled</span><span class="i-code">0xf144</span></div>
|
<div class="the-icons span3" title="Code: 0xf144"><i class="demo-icon icon-play-circled"></i> <span class="i-name">icon-play-circled</span><span class="i-code">0xf144</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf164"><i class="demo-icon icon-thumbs-up-alt"></i> <span class="i-name">icon-thumbs-up-alt</span><span class="i-code">0xf164</span></div>
|
|
||||||
<div class="the-icons span3" title="Code: 0xf1e5"><i class="demo-icon icon-binoculars"></i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<div class="the-icons span3" title="Code: 0xf164"><i class="demo-icon icon-thumbs-up-alt"></i> <span class="i-name">icon-thumbs-up-alt</span><span class="i-code">0xf164</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xf1e5"><i class="demo-icon icon-binoculars"></i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf234"><i class="demo-icon icon-user-plus"></i> <span class="i-name">icon-user-plus</span><span class="i-code">0xf234</span></div>
|
<div class="the-icons span3" title="Code: 0xf234"><i class="demo-icon icon-user-plus"></i> <span class="i-name">icon-user-plus</span><span class="i-code">0xf234</span></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
BIN
static/font/font/fontello.eot
Normal file → Executable file
BIN
static/font/font/fontello.eot
Normal file → Executable file
Binary file not shown.
2
static/font/font/fontello.svg
Normal file → Executable file
2
static/font/font/fontello.svg
Normal file → Executable file
|
@ -76,6 +76,8 @@
|
||||||
|
|
||||||
<glyph glyph-name="mail-alt" unicode="" d="M1000 461v-443q0-37-26-63t-63-27h-822q-36 0-63 27t-26 63v443q25-27 56-49 202-137 278-192 32-24 51-37t53-27 61-13h2q28 0 61 13t53 27 51 37q95 68 278 192 32 22 56 49z m0 164q0-44-27-84t-68-69q-210-146-262-181-5-4-23-17t-30-22-29-18-32-15-28-5h-2q-12 0-27 5t-32 15-30 18-30 22-23 17q-51 35-147 101t-114 80q-35 23-65 64t-31 77q0 43 23 72t66 29h822q36 0 63-26t26-63z" horiz-adv-x="1000" />
|
<glyph glyph-name="mail-alt" unicode="" d="M1000 461v-443q0-37-26-63t-63-27h-822q-36 0-63 27t-26 63v443q25-27 56-49 202-137 278-192 32-24 51-37t53-27 61-13h2q28 0 61 13t53 27 51 37q95 68 278 192 32 22 56 49z m0 164q0-44-27-84t-68-69q-210-146-262-181-5-4-23-17t-30-22-29-18-32-15-28-5h-2q-12 0-27 5t-32 15-30 18-30 22-23 17q-51 35-147 101t-114 80q-35 23-65 64t-31 77q0 43 23 72t66 29h822q36 0 63-26t26-63z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
|
<glyph glyph-name="gauge" unicode="" d="M214 214q0 30-21 51t-50 21-51-21-21-51 21-50 51-21 50 21 21 50z m107 250q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m239-268l57 213q3 14-5 27t-21 16-27-3-17-22l-56-213q-33-3-60-25t-35-55q-11-43 11-81t66-50 81 11 50 66q9 33-4 65t-40 51z m369 18q0 30-21 51t-51 21-50-21-21-51 21-50 50-21 51 21 21 50z m-358 357q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m250-107q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m179-250q0-145-79-269-10-17-30-17h-782q-20 0-30 17-79 123-79 269 0 102 40 194t106 160 160 107 194 39 194-39 160-107 106-160 40-194z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
<glyph glyph-name="comment-empty" unicode="" d="M500 643q-114 0-213-39t-157-105-59-142q0-62 40-119t113-98l48-28-15-53q-13-51-39-97 85 36 154 96l24 21 32-3q38-5 72-5 114 0 213 39t157 105 59 142-59 142-157 105-213 39z m500-286q0-97-67-179t-182-130-251-48q-39 0-81 4-110-97-257-135-27-8-63-12h-3q-8 0-15 6t-9 15v1q-2 2 0 6t1 6 2 5l4 5t4 5 4 5q4 5 17 19t20 22 17 22 18 28 15 33 15 42q-88 50-138 123t-51 157q0 97 67 179t182 130 251 48 251-48 182-130 67-179z" horiz-adv-x="1000" />
|
<glyph glyph-name="comment-empty" unicode="" d="M500 643q-114 0-213-39t-157-105-59-142q0-62 40-119t113-98l48-28-15-53q-13-51-39-97 85 36 154 96l24 21 32-3q38-5 72-5 114 0 213 39t157 105 59 142-59 142-157 105-213 39z m500-286q0-97-67-179t-182-130-251-48q-39 0-81 4-110-97-257-135-27-8-63-12h-3q-8 0-15 6t-9 15v1q-2 2 0 6t1 6 2 5l4 5t4 5 4 5q4 5 17 19t20 22 17 22 18 28 15 33 15 42q-88 50-138 123t-51 157q0 97 67 179t182 130 251 48 251-48 182-130 67-179z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
<glyph glyph-name="bell-alt" unicode="" d="M509-89q0 8-9 8-33 0-57 24t-23 57q0 9-9 9t-9-9q0-41 29-70t69-28q9 0 9 9z m455 160q0-29-21-50t-50-21h-250q0-59-42-101t-101-42-101 42-42 101h-250q-29 0-50 21t-21 50q28 24 51 49t47 67 42 89 27 115 11 145q0 84 66 157t171 89q-5 10-5 21 0 23 16 38t38 16 38-16 16-38q0-11-5-21 106-16 171-89t66-157q0-78 11-145t28-115 41-89 48-67 50-49z" horiz-adv-x="1000" />
|
<glyph glyph-name="bell-alt" unicode="" d="M509-89q0 8-9 8-33 0-57 24t-23 57q0 9-9 9t-9-9q0-41 29-70t69-28q9 0 9 9z m455 160q0-29-21-50t-50-21h-250q0-59-42-101t-101-42-101 42-42 101h-250q-29 0-50 21t-21 50q28 24 51 49t47 67 42 89 27 115 11 145q0 84 66 157t171 89q-5 10-5 21 0 23 16 38t38 16 38-16 16-38q0-11-5-21 106-16 171-89t66-157q0-78 11-145t28-115 41-89 48-67 50-49z" horiz-adv-x="1000" />
|
||||||
|
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 22 KiB |
BIN
static/font/font/fontello.ttf
Normal file → Executable file
BIN
static/font/font/fontello.ttf
Normal file → Executable file
Binary file not shown.
BIN
static/font/font/fontello.woff
Normal file → Executable file
BIN
static/font/font/fontello.woff
Normal file → Executable file
Binary file not shown.
BIN
static/font/font/fontello.woff2
Normal file → Executable file
BIN
static/font/font/fontello.woff2
Normal file → Executable file
Binary file not shown.
|
@ -345,5 +345,16 @@ describe('API Entities normalizer', () => {
|
||||||
const result = addEmojis('Admin add the :tenshi: emoji', emojis)
|
const result = addEmojis('Admin add the :tenshi: emoji', emojis)
|
||||||
expect(result).to.equal('Admin add the :tenshi: emoji')
|
expect(result).to.equal('Admin add the :tenshi: emoji')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Doesn\'t blow up on regex special characters', () => {
|
||||||
|
const emojis = makeMockEmojiMasto([{
|
||||||
|
shortcode: 'c++'
|
||||||
|
}, {
|
||||||
|
shortcode: '[a-z] {|}*'
|
||||||
|
}])
|
||||||
|
const result = addEmojis('This post has :c++: emoji and :[a-z] {|}*: emoji', emojis)
|
||||||
|
expect(result).to.include('title=\'c++\'')
|
||||||
|
expect(result).to.include('title=\'[a-z] {|}*\'')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue