Merge remote-tracking branch 'upstream/develop' into shigusegubu
* upstream/develop: (32 commits) set flex-shrink and flex-basis explicitly set flex amount correctly update flex-grow calculation logic keep image natural ratio in gallery row populate gallery row height without getting width refactor using Set clean up update event name update condition move modal animation keyframes definition migrate viewClass prop to class attribute fix eslint errors fix message input not auto-focusing bug revert changes to render modal into portal use higher css specificity fix eslint warnings remove needless ref definition render modals into the “modal” portal remove needless console.log remove needless importing ...
This commit is contained in:
commit
6a5d57d2fe
24 changed files with 207 additions and 176 deletions
|
@ -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
|
||||||
|
|
25
src/App.scss
25
src/App.scss
|
@ -717,31 +717,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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 },
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
|
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
|
||||||
export default {
|
export default {
|
||||||
props: ['user'],
|
props: ['user', 'labelFollowing', 'buttonClass'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
inProgress: false
|
inProgress: false
|
||||||
|
@ -23,7 +23,7 @@ export default {
|
||||||
if (this.inProgress) {
|
if (this.inProgress) {
|
||||||
return this.$t('user_card.follow_progress')
|
return this.$t('user_card.follow_progress')
|
||||||
} else if (this.user.following) {
|
} else if (this.user.following) {
|
||||||
return this.$t('user_card.following')
|
return this.labelFollowing || this.$t('user_card.following')
|
||||||
} else if (this.user.requested) {
|
} else if (this.user.requested) {
|
||||||
return this.$t('user_card.follow_sent')
|
return this.$t('user_card.follow_sent')
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,20 +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
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
components: {
|
||||||
BasicUserCard,
|
BasicUserCard,
|
||||||
RemoteFollow
|
RemoteFollow,
|
||||||
|
FollowButton
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isMe () {
|
isMe () {
|
||||||
|
@ -23,20 +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(() => {
|
|
||||||
this.inProgress = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
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="user.requested ? $t('user_card.follow_again') : ''"
|
/>
|
||||||
@click="followUser"
|
|
||||||
>
|
|
||||||
<template v-if="inProgress">
|
|
||||||
{{ $t('user_card.follow_progress') }}
|
|
||||||
</template>
|
|
||||||
<template v-else-if="user.requested">
|
|
||||||
{{ $t('user_card.follow_sent') }}
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
{{ $t('user_card.follow') }}
|
|
||||||
</template>
|
|
||||||
</button>
|
|
||||||
<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>
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
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'
|
import get from 'lodash/get'
|
||||||
|
|
||||||
const PostStatusModal = {
|
const PostStatusModal = {
|
||||||
components: {
|
components: {
|
||||||
PostStatusForm
|
PostStatusForm,
|
||||||
|
Modal
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<Modal
|
||||||
v-if="isLoggedIn && !resettingForm"
|
v-if="isLoggedIn && !resettingForm"
|
||||||
v-show="modalActivated"
|
:is-open="modalActivated"
|
||||||
class="post-form-modal-view modal-view"
|
class="post-form-modal-view"
|
||||||
@click="closeModal"
|
@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>
|
||||||
|
@ -18,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -840,6 +840,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -635,6 +635,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."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue