Commit a3305799 authored by HJ's avatar HJ 🐼

Merge remote-tracking branch 'upstream/develop' into emoji-selector-update

* upstream/develop: (42 commits)
  Fix formatting in oc.json
  avoid using global class
  fix logo moving bug when lightbox is open
  Reserve scrollbar gap when body scroll is locked
  setting display: initial makes trouble, instead, toggle display: none using classname
  lock body scroll
  add body-scroll-lock directive
  install body-scroll-lock
  wire up props with PostStatusModal
  rename component
  recover autofocusing behavior
  refactor MobilePostStatusModal using new PostStatusModal
  add new module and modal to post new status
  remove needless condition
  add mention button
  wire up user state with global store
  collapse fav/repeat notifications from muted users
  do not collapse thread muted posts in conversation
  detect thread-muted posts
  do not change word based muting logic
  ...
parents 2f26e8ac e55645ae
Pipeline #17637 failed with stages
in 5 minutes and 12 seconds
......@@ -9,7 +9,7 @@
<link rel="stylesheet" href="/static/font/css/fontello.css">
<link rel="stylesheet" href="/static/font/css/animation.css">
</head>
<body>
<body class="hidden">
<noscript>To use Pleroma, please enable JavaScript.</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
......
......@@ -8,9 +8,10 @@ import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_pan
import ChatPanel from './components/chat_panel/chat_panel.vue'
import MediaModal from './components/media_modal/media_modal.vue'
import SideDrawer from './components/side_drawer/side_drawer.vue'
import MobilePostStatusModal from './components/mobile_post_status_modal/mobile_post_status_modal.vue'
import MobilePostStatusButton from './components/mobile_post_status_button/mobile_post_status_button.vue'
import MobileNav from './components/mobile_nav/mobile_nav.vue'
import UserReportingModal from './components/user_reporting_modal/user_reporting_modal.vue'
import PostStatusModal from './components/post_status_modal/post_status_modal.vue'
import { windowWidth } from './services/window_utils/window_utils'
export default {
......@@ -26,9 +27,10 @@ export default {
ChatPanel,
MediaModal,
SideDrawer,
MobilePostStatusModal,
MobilePostStatusButton,
MobileNav,
UserReportingModal
UserReportingModal,
PostStatusModal
},
data: () => ({
mobileActivePanel: 'timeline',
......
......@@ -10,13 +10,14 @@
position: fixed;
z-index: -1;
height: 100%;
width: 100%;
left: 0;
right: -20px;
background-size: cover;
background-repeat: no-repeat;
background-position: 0 50%;
}
i {
i[class^='icon-'] {
user-select: none;
}
......@@ -49,6 +50,10 @@ body {
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
&.hidden {
display: none;
}
}
a {
......@@ -343,6 +348,7 @@ i[class*=icon-] {
align-items: center;
position: fixed;
height: 50px;
box-sizing: border-box;
.logo {
display: flex;
......@@ -382,6 +388,7 @@ i[class*=icon-] {
}
.inner-nav {
position: relative;
margin: auto;
box-sizing: border-box;
padding-left: 10px;
......
......@@ -4,6 +4,7 @@
:style="bgAppStyle"
>
<div
id="app_bg_wrapper"
class="app-bg-wrapper"
:style="bgStyle"
/>
......@@ -14,20 +15,20 @@
class="nav-bar container"
@click="scrollToTop()"
>
<div
class="logo"
:style="logoBgStyle"
>
<div class="inner-nav">
<div
class="mask"
:style="logoMaskStyle"
/>
<img
:src="logo"
:style="logoStyle"
class="logo"
:style="logoBgStyle"
>
</div>
<div class="inner-nav">
<div
class="mask"
:style="logoMaskStyle"
/>
<img
:src="logo"
:style="logoStyle"
>
</div>
<div class="item">
<router-link
class="site-name"
......@@ -107,8 +108,9 @@
:floating="true"
class="floating-chat mobile-hidden"
/>
<MobilePostStatusModal />
<MobilePostStatusButton />
<UserReportingModal />
<PostStatusModal />
<portal-target name="modal" />
</div>
</template>
......
......@@ -5,12 +5,8 @@ const conversationPage = {
Conversation
},
computed: {
statusoid () {
const id = this.$route.params.id
const statuses = this.$store.state.statuses.allStatusesObject
const status = statuses[id]
return status
statusId () {
return this.$route.params.id
}
}
}
......
......@@ -2,7 +2,7 @@
<conversation
:collapsable="false"
is-page="true"
:statusoid="statusoid"
:status-id="statusId"
/>
</template>
......
import { reduce, filter, findIndex, clone } from 'lodash'
import { reduce, filter, findIndex, clone, get } from 'lodash'
import Status from '../status/status.vue'
const sortById = (a, b) => {
......@@ -39,10 +39,11 @@ const conversation = {
}
},
props: [
'statusoid',
'statusId',
'collapsable',
'isPage',
'pinnedStatusIdsObject'
'pinnedStatusIdsObject',
'inProfile'
],
created () {
if (this.isPage) {
......@@ -51,21 +52,17 @@ const conversation = {
},
computed: {
status () {
return this.statusoid
return this.$store.state.statuses.allStatusesObject[this.statusId]
},
statusId () {
if (this.statusoid.retweeted_status) {
return this.statusoid.retweeted_status.id
originalStatusId () {
if (this.status.retweeted_status) {
return this.status.retweeted_status.id
} else {
return this.statusoid.id
return this.statusId
}
},
conversationId () {
if (this.statusoid.retweeted_status) {
return this.statusoid.retweeted_status.statusnet_conversation_id
} else {
return this.statusoid.statusnet_conversation_id
}
return this.getConversationId(this.statusId)
},
conversation () {
if (!this.status) {
......@@ -77,7 +74,7 @@ const conversation = {
}
const conversation = clone(this.$store.state.statuses.conversationsObject[this.conversationId])
const statusIndex = findIndex(conversation, { id: this.statusId })
const statusIndex = findIndex(conversation, { id: this.originalStatusId })
if (statusIndex !== -1) {
conversation[statusIndex] = this.status
}
......@@ -110,7 +107,15 @@ const conversation = {
Status
},
watch: {
status: 'fetchConversation',
statusId (newVal, oldVal) {
const newConversationId = this.getConversationId(newVal)
const oldConversationId = this.getConversationId(oldVal)
if (newConversationId && oldConversationId && newConversationId === oldConversationId) {
this.setHighlight(this.originalStatusId)
} else {
this.fetchConversation()
}
},
expanded (value) {
if (value) {
this.fetchConversation()
......@@ -120,24 +125,25 @@ const conversation = {
methods: {
fetchConversation () {
if (this.status) {
this.$store.state.api.backendInteractor.fetchConversation({ id: this.status.id })
this.$store.state.api.backendInteractor.fetchConversation({ id: this.statusId })
.then(({ ancestors, descendants }) => {
this.$store.dispatch('addNewStatuses', { statuses: ancestors })
this.$store.dispatch('addNewStatuses', { statuses: descendants })
this.setHighlight(this.originalStatusId)
})
.then(() => this.setHighlight(this.statusId))
} else {
const id = this.$route.params.id
this.$store.state.api.backendInteractor.fetchStatus({ id })
.then((status) => this.$store.dispatch('addNewStatuses', { statuses: [status] }))
.then(() => this.fetchConversation())
this.$store.state.api.backendInteractor.fetchStatus({ id: this.statusId })
.then((status) => {
this.$store.dispatch('addNewStatuses', { statuses: [status] })
this.fetchConversation()
})
}
},
getReplies (id) {
return this.replies[id] || []
},
focused (id) {
return (this.isExpanded) && id === this.status.id
return (this.isExpanded) && id === this.statusId
},
setHighlight (id) {
if (!id) return
......@@ -149,6 +155,10 @@ const conversation = {
},
toggleExpanded () {
this.expanded = !this.expanded
},
getConversationId (statusId) {
const status = this.$store.state.statuses.allStatusesObject[statusId]
return get(status, 'retweeted_status.statusnet_conversation_id', get(status, 'statusnet_conversation_id'))
}
}
}
......
......@@ -26,6 +26,7 @@
:in-conversation="isExpanded"
:highlight="getHighlight()"
:replies="getReplies(status.id)"
:in-profile="inProfile"
class="status-fadein panel-body"
@goto="setHighlight"
@toggleExpanded="toggleExpanded"
......
......@@ -10,14 +10,14 @@
<div slot="popover">
<div class="dropdown-menu">
<button
v-if="canMute && !status.muted"
v-if="canMute && !status.thread_muted"
class="dropdown-item dropdown-item-icon"
@click.prevent="muteConversation"
>
<i class="icon-eye-off" /><span>{{ $t("status.mute_conversation") }}</span>
</button>
<button
v-if="canMute && status.muted"
v-if="canMute && status.thread_muted"
class="dropdown-item dropdown-item-icon"
@click.prevent="unmuteConversation"
>
......
<template>
<div
v-if="showing"
v-body-scroll-lock="showing"
class="modal-view media-modal-view"
@click.prevent="hide"
>
......@@ -43,6 +44,10 @@
.media-modal-view {
z-index: 1001;
body:not(.scroll-locked) & {
display: none;
}
&:hover {
.modal-view-button-arrow {
opacity: 0.75;
......
import PostStatusForm from '../post_status_form/post_status_form.vue'
import { debounce } from 'lodash'
const MobilePostStatusModal = {
components: {
PostStatusForm
},
const MobilePostStatusButton = {
data () {
return {
hidden: false,
postFormOpen: false,
scrollingDown: false,
inputActive: false,
oldScrollPos: 0,
......@@ -28,8 +23,8 @@ const MobilePostStatusModal = {
window.removeEventListener('resize', this.handleOSK)
},
computed: {
currentUser () {
return this.$store.state.users.currentUser
isLoggedIn () {
return !!this.$store.state.users.currentUser
},
isHidden () {
return this.autohideFloatingPostButton && (this.hidden || this.inputActive)
......@@ -57,17 +52,7 @@ const MobilePostStatusModal = {
window.removeEventListener('scroll', this.handleScrollEnd)
},
openPostForm () {
this.postFormOpen = true
this.hidden = true
const el = this.$el.querySelector('textarea')
this.$nextTick(function () {
el.focus()
})
},
closePostForm () {
this.postFormOpen = false
this.hidden = false
this.$store.dispatch('openPostStatusModal')
},
handleOSK () {
// This is a big hack: we're guessing from changed window sizes if the
......@@ -105,4 +90,4 @@ const MobilePostStatusModal = {
}
}
export default MobilePostStatusModal
export default MobilePostStatusButton
<template>
<div v-if="currentUser">
<div
v-show="postFormOpen"
class="post-form-modal-view modal-view"
@click="closePostForm"
>
<div
class="post-form-modal-panel panel"
@click.stop=""
>
<div class="panel-heading">
{{ $t('post_status.new_status') }}
</div>
<PostStatusForm
class="panel-body"
@posted="closePostForm"
/>
</div>
</div>
<div v-if="isLoggedIn">
<button
class="new-status-button"
:class="{ 'hidden': isHidden }"
......@@ -28,27 +10,11 @@
</div>
</template>
<script src="./mobile_post_status_modal.js"></script>
<script src="./mobile_post_status_button.js"></script>
<style lang="scss">
@import '../../_variables.scss';
.post-form-modal-view {
align-items: flex-start;
}
.post-form-modal-panel {
flex-shrink: 0;
margin-top: 25%;
margin-bottom: 2em;
width: 100%;
max-width: 700px;
@media (orientation: landscape) {
margin-top: 8%;
}
}
.new-status-button {
width: 5em;
height: 5em;
......
......@@ -9,7 +9,8 @@ const Notification = {
data () {
return {
userExpanded: false,
betterShadow: this.$store.state.interface.browserSupport.cssFilter
betterShadow: this.$store.state.interface.browserSupport.cssFilter,
unmuted: false
}
},
props: [ 'notification' ],
......@@ -23,11 +24,14 @@ const Notification = {
toggleUserExpanded () {
this.userExpanded = !this.userExpanded
},
userProfileLink (user) {
generateUserProfileLink (user) {
return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames)
},
getUser (notification) {
return this.$store.state.users.usersObject[notification.from_profile.id]
},
toggleMute () {
this.unmuted = !this.unmuted
}
},
computed: {
......@@ -47,6 +51,12 @@ const Notification = {
return this.userInStore
}
return this.notification.from_profile
},
userProfileLink () {
return this.generateUserProfileLink(this.user)
},
needMute () {
return this.user.muted
}
}
}
......
......@@ -4,104 +4,126 @@
:compact="true"
:statusoid="notification.status"
/>
<div
v-else
class="non-mention"
:class="[userClass, { highlighted: userStyle }]"
:style="[ userStyle ]"
>
<a
class="avatar-container"
:href="notification.from_profile.statusnet_profile_url"
@click.stop.prevent.capture="toggleUserExpanded"
<div v-else>
<div
v-if="needMute && !unmuted"
class="container muted"
>
<UserAvatar
:compact="true"
:better-shadow="betterShadow"
:user="notification.from_profile"
/>
</a>
<div class="notification-right">
<UserCard
v-if="userExpanded"
:user="getUser(notification)"
:rounded="true"
:bordered="true"
/>
<span class="notification-details">
<div class="name-and-action">
<!-- eslint-disable vue/no-v-html -->
<span
v-if="!!notification.from_profile.name_html"
class="username"
:title="'@'+notification.from_profile.screen_name"
v-html="notification.from_profile.name_html"
/>
<!-- eslint-enable vue/no-v-html -->
<span
v-else
class="username"
:title="'@'+notification.from_profile.screen_name"
>{{ notification.from_profile.name }}</span>
<span v-if="notification.type === 'like'">
<i class="fa icon-star lit" />
<small>{{ $t('notifications.favorited_you') }}</small>
</span>
<span v-if="notification.type === 'repeat'">
<i
class="fa icon-retweet lit"
:title="$t('tool_tip.repeat')"
<small>
<router-link :to="userProfileLink">
{{ notification.from_profile.screen_name }}
</router-link>
</small>
<a
href="#"
class="unmute"
@click.prevent="toggleMute"
><i class="button-icon icon-eye-off" /></a>
</div>
<div
v-else
class="non-mention"
:class="[userClass, { highlighted: userStyle }]"
:style="[ userStyle ]"
>
<a
class="avatar-container"
:href="notification.from_profile.statusnet_profile_url"
@click.stop.prevent.capture="toggleUserExpanded"
>
<UserAvatar
:compact="true"
:better-shadow="betterShadow"
:user="notification.from_profile"
/>
</a>
<div class="notification-right">
<UserCard
v-if="userExpanded"
:user="getUser(notification)"
:rounded="true"
:bordered="true"
/>
<span class="notification-details">
<div class="name-and-action">
<!-- eslint-disable vue/no-v-html -->
<span
v-if="!!notification.from_profile.name_html"
class="username"
:title="'@'+notification.from_profile.screen_name"
v-html="notification.from_profile.name_html"
/>
<small>{{ $t('notifications.repeated_you') }}</small>
</span>
<span v-if="notification.type === 'follow'">
<i class="fa icon-user-plus lit" />
<small>{{ $t('notifications.followed_you') }}</small>
</span>
</div>
<!-- eslint-enable vue/no-v-html -->
<span
v-else
class="username"
:title="'@'+notification.from_profile.screen_name"
>{{ notification.from_profile.name }}</span>
<span v-if="notification.type === 'like'">
<i class="fa icon-star lit" />
<small>{{ $t('notifications.favorited_you') }}</small>
</span>
<span v-if="notification.type === 'repeat'">
<i
class="fa icon-retweet lit"
:title="$t('tool_tip.repeat')"
/>
<small>{{ $t('notifications.repeated_you') }}</small>
</span>
<span v-if="notification.type === 'follow'">
<i class="fa icon-user-plus lit" />
<small>{{ $t('notifications.followed_you') }}</small>
</span>
</div>
<div
v-if="notification.type === 'follow'"
class="timeago"
>
<span class="faint">
<Timeago
:time="notification.created_at"
:auto-update="240"
/>
</span>
</div>
<div
v-else
class="timeago"
>
<router-link
v-if="notification.status"
:to="{ name: 'conversation', params: { id: notification.status.id } }"
class="faint-link"
>
<Timeago
:time="notification.created_at"
:auto-update="240"
/>
</router-link>
</div>
<a
v-if="needMute"
href="#"
@click.prevent="toggleMute"
><i class="button-icon icon-eye-off" /></a>
</span>
<div
v-if="notification.type === 'follow'"
class="timeago"
>
<span class="faint">
<Timeago
:time="notification.created_at"
:auto-update="240"
/>
</span>
</div>
<div
v-else
class="timeago"
class="follow-text"
>
<router-link
v-if="notification.status"
:to="{ name: 'conversation', params: { id: notification.status.id } }"
class="faint-link"
>
<Timeago
:time="notification.created_at"
:auto-update="240"
/>
<router-link :to="userProfileLink">
@{{ notification.from_profile.screen_name }}
</router-link>
</div>
</span>
<div
v-if="notification.type === 'follow'"
class="follow-text"
>
<router-link :to="userProfileLink(notification.from_profile)">
@{{ notification.from_profile.screen_name }}
</router-link>
<template v-else>
<status
class="faint"
:compact="true"
:statusoid="notification.action"
:no-heading="true"
/>
</template>
</div>
<template v-else>
<status
class="faint"
:compact="true"
:statusoid="notification.action"
:no-heading="true"
/>
</template>
</div>
</div>
</template>
......
......@@ -33,7 +33,6 @@
.notification {
box-sizing: border-box;
display: flex;
border-bottom: 1px solid;
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
......@@ -47,6 +46,10 @@
}
}
.muted {
padding: .25em .6em;