diff options
40 files changed, 958 insertions, 285 deletions
@@ -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 --> diff --git a/package.json b/package.json index 0f6ae9c8..f039d412 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@chenfengyuan/vue-qrcode": "^1.0.0", "babel-plugin-add-module-exports": "^0.2.1", "babel-plugin-lodash": "^3.2.11", + "body-scroll-lock": "^2.6.4", "chromatism": "^3.0.0", "cropperjs": "^1.4.3", "diff": "^3.0.1", @@ -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', diff --git a/src/App.scss b/src/App.scss index fac800bc..2190f91a 100644 --- a/src/App.scss +++ b/src/App.scss @@ -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; diff --git a/src/App.vue b/src/App.vue index 719e00a4..8d7f6c79 100644 --- a/src/App.vue +++ b/src/App.vue @@ -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> diff --git a/src/components/conversation-page/conversation-page.js b/src/components/conversation-page/conversation-page.js index 1da70ce9..8f996be1 100644 --- a/src/components/conversation-page/conversation-page.js +++ b/src/components/conversation-page/conversation-page.js @@ -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 } } } diff --git a/src/components/conversation-page/conversation-page.vue b/src/components/conversation-page/conversation-page.vue index 532f785c..8cc0a55f 100644 --- a/src/components/conversation-page/conversation-page.vue +++ b/src/components/conversation-page/conversation-page.vue @@ -2,7 +2,7 @@ <conversation :collapsable="false" is-page="true" - :statusoid="statusoid" + :status-id="statusId" /> </template> diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 49fa8612..72ee9c39 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -1,4 +1,4 @@ -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')) } } } diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index f184c071..0f1de55f 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -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" diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue index ed0f3aa4..6781a4f8 100644 --- a/src/components/extra_buttons/extra_buttons.vue +++ b/src/components/extra_buttons/extra_buttons.vue @@ -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" > diff --git a/src/components/media_modal/media_modal.vue b/src/components/media_modal/media_modal.vue index ab5a36a5..06ced5a1 100644 --- a/src/components/media_modal/media_modal.vue +++ b/src/components/media_modal/media_modal.vue @@ -1,6 +1,7 @@ <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; diff --git a/src/components/mobile_post_status_modal/mobile_post_status_modal.js b/src/components/mobile_post_status_button/mobile_post_status_button.js index 3cec23c6..3e77148a 100644 --- a/src/components/mobile_post_status_modal/mobile_post_status_modal.js +++ b/src/components/mobile_post_status_button/mobile_post_status_button.js @@ -1,14 +1,9 @@ -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 diff --git a/src/components/mobile_post_status_modal/mobile_post_status_modal.vue b/src/components/mobile_post_status_button/mobile_post_status_button.vue index b6d7d3ba..9cf45de3 100644 --- a/src/components/mobile_post_status_modal/mobile_post_status_modal.vue +++ b/src/components/mobile_post_status_button/mobile_post_status_button.vue @@ -1,23 +1,5 @@ <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; diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 896c6d52..8e817f3b 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -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 } } } diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index bafcd026..1f192c77 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -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> diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 622d12f4..71876b14 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -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; + } + .non-mention { display: flex; flex: 1; diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 76bcfa71..9b2a9c90 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -8,7 +8,7 @@ import { findOffset } from '../../services/offset_finder/offset_finder.service.j import { reject, map, uniqBy } from 'lodash' import suggestor from '../emoji_input/suggestor.js' -const buildMentionsString = ({ user, attentions }, currentUser) => { +const buildMentionsString = ({ user, attentions = [] }, currentUser) => { let allAttentions = [...attentions] allAttentions.unshift(user) diff --git a/src/components/post_status_modal/post_status_modal.js b/src/components/post_status_modal/post_status_modal.js new file mode 100644 index 00000000..1033ba11 --- /dev/null +++ b/src/components/post_status_modal/post_status_modal.js @@ -0,0 +1,32 @@ +import PostStatusForm from '../post_status_form/post_status_form.vue' + +const PostStatusModal = { + components: { + PostStatusForm + }, + computed: { + isLoggedIn () { + return !!this.$store.state.users.currentUser + }, + isOpen () { + return this.isLoggedIn && this.$store.state.postStatus.modalActivated + }, + params () { + return this.$store.state.postStatus.params || {} + } + }, + watch: { + isOpen (val) { + if (val) { + this.$nextTick(() => this.$el.querySelector('textarea').focus()) + } + } + }, + methods: { + closeModal () { + this.$store.dispatch('closePostStatusModal') + } + } +} + +export default PostStatusModal diff --git a/src/components/post_status_modal/post_status_modal.vue b/src/components/post_status_modal/post_status_modal.vue new file mode 100644 index 00000000..3f8eec69 --- /dev/null +++ b/src/components/post_status_modal/post_status_modal.vue @@ -0,0 +1,43 @@ +<template> + <div + v-if="isOpen" + class="post-form-modal-view modal-view" + @click="closeModal" + > + <div + class="post-form-modal-panel panel" + @click.stop="" + > + <div class="panel-heading"> + {{ $t('post_status.new_status') }} + </div> + <PostStatusForm + class="panel-body" + v-bind="params" + @posted="closeModal" + /> + </div> + </div> +</template> + +<script src="./post_status_modal.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%; + } +} +</style> diff --git a/src/components/status/status.js b/src/components/status/status.js index 502d9583..d17ba318 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -29,7 +29,8 @@ const Status = { 'isPreview', 'noHeading', 'inlineExpanded', - 'showPinned' + 'showPinned', + 'inProfile' ], data () { return { @@ -117,7 +118,7 @@ const Status = { return hits }, - muted () { return !this.unmuted && (this.status.user.muted || this.muteWordHits.length > 0) }, + muted () { return !this.unmuted && ((!this.inProfile && this.status.user.muted) || (!this.inConversation && this.status.thread_muted) || this.muteWordHits.length > 0) }, hideFilteredStatuses () { return typeof this.$store.state.config.hideFilteredStatuses === 'undefined' ? this.$store.state.instance.hideFilteredStatuses diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index 8df48f7f..0594576c 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -25,7 +25,8 @@ const Timeline = { 'tag', 'embedded', 'count', - 'pinnedStatusIds' + 'pinnedStatusIds', + 'inProfile' ], data () { return { diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index 4ad51714..f1d3903a 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -33,9 +33,10 @@ v-if="timeline.statusesObject[statusId]" :key="statusId + '-pinned'" class="status-fadein" - :statusoid="timeline.statusesObject[statusId]" + :status-id="statusId" :collapsable="true" :pinned-status-ids-object="pinnedStatusIdsObject" + :in-profile="inProfile" /> </template> <template v-for="status in timeline.visibleStatuses"> @@ -43,8 +44,9 @@ v-if="!excludedStatusIdsObject[status.id]" :key="status.id" class="status-fadein" - :statusoid="status" + :status-id="status.id" :collapsable="true" + :in-profile="inProfile" /> </template> </div> diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index e41a3180..9c931c01 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -11,7 +11,6 @@ export default { data () { return { followRequestInProgress: false, - followRequestSent: false, hideUserStatsLocal: typeof this.$store.state.config.hideUserStats === 'undefined' ? this.$store.state.instance.hideUserStats : this.$store.state.config.hideUserStats, @@ -103,9 +102,8 @@ export default { followUser () { const store = this.$store this.followRequestInProgress = true - requestFollow(this.user, store).then(({ sent }) => { + requestFollow(this.user, store).then(() => { this.followRequestInProgress = false - this.followRequestSent = sent }) }, unfollowUser () { @@ -161,6 +159,9 @@ export default { } this.$store.dispatch('setMedia', [attachment]) this.$store.dispatch('setCurrent', attachment) + }, + mentionUser () { + this.$store.dispatch('openPostStatusModal', { replyTo: true, repliedUser: this.user }) } } } diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue index 629d1fd0..5b6f66e7 100644 --- a/src/components/user_card/user_card.vue +++ b/src/components/user_card/user_card.vue @@ -139,13 +139,13 @@ <button class="btn btn-default btn-block" :disabled="followRequestInProgress" - :title="followRequestSent ? $t('user_card.follow_again') : ''" + :title="user.requested ? $t('user_card.follow_again') : ''" @click="followUser" > <template v-if="followRequestInProgress"> {{ $t('user_card.follow_progress') }} </template> - <template v-else-if="followRequestSent"> + <template v-else-if="user.requested"> {{ $t('user_card.follow_sent') }} </template> <template v-else> @@ -194,6 +194,15 @@ <div> <button + class="btn btn-default btn-block" + @click="mentionUser" + > + {{ $t('user_card.mention') }} + </button> + </div> + + <div> + <button v-if="user.muted" class="btn btn-default btn-block pressed" @click="unmuteUser" diff --git a/src/components/user_panel/user_panel.vue b/src/components/user_panel/user_panel.vue index 5cdb2914..e9f08015 100644 --- a/src/components/user_panel/user_panel.vue +++ b/src/components/user_panel/user_panel.vue @@ -11,7 +11,7 @@ rounded="top" /> <div class="panel-footer"> - <PostStatusForm v-if="user" /> + <PostStatusForm /> </div> </div> <auth-form diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index 42516916..14082e83 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -26,6 +26,7 @@ timeline-name="user" :user-id="userId" :pinned-status-ids="user.pinnedStatusIds" + :in-profile="true" /> <div v-if="followsTabVisible" @@ -69,6 +70,7 @@ timeline-name="media" :timeline="media" :user-id="userId" + :in-profile="true" /> <Timeline v-if="isUs" @@ -79,6 +81,7 @@ :title="$t('user_card.favorites')" timeline-name="favorites" :timeline="favorites" + :in-profile="true" /> </tab-switcher> </div> diff --git a/src/directives/body_scroll_lock.js b/src/directives/body_scroll_lock.js new file mode 100644 index 00000000..6ab20c3f --- /dev/null +++ b/src/directives/body_scroll_lock.js @@ -0,0 +1,69 @@ +import * as bodyScrollLock from 'body-scroll-lock' + +let previousNavPaddingRight +let previousAppBgWrapperRight + +const disableBodyScroll = (el) => { + const scrollBarGap = window.innerWidth - document.documentElement.clientWidth + bodyScrollLock.disableBodyScroll(el, { + reserveScrollBarGap: true + }) + setTimeout(() => { + // If previousNavPaddingRight is already set, don't set it again. + if (previousNavPaddingRight === undefined) { + const navEl = document.getElementById('nav') + 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') + }) +} + +const enableBodyScroll = (el) => { + setTimeout(() => { + if (previousNavPaddingRight !== undefined) { + document.getElementById('nav').style.paddingRight = previousNavPaddingRight + // 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') + }) + bodyScrollLock.enableBodyScroll(el) +} + +const directive = { + inserted: (el, binding) => { + if (binding.value) { + disableBodyScroll(el) + } + }, + componentUpdated: (el, binding) => { + if (binding.oldValue === binding.value) { + return + } + + if (binding.value) { + disableBodyScroll(el) + } else { + enableBodyScroll(el) + } + }, + unbind: (el) => { + enableBodyScroll(el) + } +} + +export default (Vue) => { + Vue.directive('body-scroll-lock', directive) +} diff --git a/src/i18n/en.json b/src/i18n/en.json index 20d4ed22..3d81fdca 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -536,6 +536,7 @@ "follows_you": "Follows you!", "its_you": "It's you!", "media": "Media", + "mention": "Mention", "mute": "Mute", "muted": "Muted", "per_day": "per day", diff --git a/src/i18n/es.json b/src/i18n/es.json index 009599f5..91c7f383 100644 --- a/src/i18n/es.json +++ b/src/i18n/es.json @@ -508,7 +508,9 @@ "pinned": "Fijado", "delete_confirm": "¿Realmente quieres borrar la publicación?", "reply_to": "Respondiendo a", - "replies_list": "Respuestas:" + "replies_list": "Respuestas:", + "mute_conversation": "Silenciar la conversación", + "unmute_conversation": "Mostrar la conversación" }, "user_card": { "approve": "Aprobar", @@ -606,5 +608,16 @@ "person_talking": "{count} personas hablando", "people_talking": "{count} gente hablando", "no_results": "Sin resultados" + }, + "password_reset": { + "forgot_password": "¿Contraseña olvidada?", + "password_reset": "Restablecer la contraseña", + "instruction": "Ingrese su dirección de correo electrónico o nombre de usuario. Le enviaremos un enlace para restablecer su contraseña.", + "placeholder": "Su correo electrónico o nombre de usuario", + "check_email": "Revise su correo electrónico para obtener un enlace para restablecer su contraseña.", + "return_home": "Volver a la página de inicio", + "not_found": "No pudimos encontrar ese correo electrónico o nombre de usuario.", + "too_many_requests": "Has alcanzado el límite de intentos, vuelve a intentarlo más tarde.", + "password_reset_disabled": "El restablecimiento de contraseñas está deshabilitado. Póngase en contacto con el administrador de su instancia." } }
\ No newline at end of file diff --git a/src/i18n/eu.json b/src/i18n/eu.json index 1efaa310..ad8f4c05 100644 --- a/src/i18n/eu.json +++ b/src/i18n/eu.json @@ -88,7 +88,7 @@ "followed_you": "Zu jarraitzen zaitu", "load_older": "Kargatu jakinarazpen zaharragoak", "notifications": "Jakinarazpenak", - "read": "Irakurri!", + "read": "Irakurrita!", "repeated_you": "zure mezua errepikatu du", "no_more_notifications": "Ez dago jakinarazpen gehiago" }, @@ -116,7 +116,7 @@ }, "post_status": { "new_status": "Mezu berri bat idatzi", - "account_not_locked_warning": "Zure kontua ez dago {0}. Edozeinek jarraitzen hastearekin, zure mezuak irakur dezake.", + "account_not_locked_warning": "Zure kontua ez dago {0}. Edozeinek jarraitzen hastearekin, zure mezuak irakur ditzake.", "account_not_locked_warning_link": "Blokeatuta", "attachments_sensitive": "Nabarmendu eranskinak hunkigarri gisa ", "content_type": { @@ -136,10 +136,10 @@ "unlisted": "Mezu hau ez da argitaratuko Denbora-lerro Publikoan ezta Ezagutzen den Sarean" }, "scope": { - "direct": "Zuzena - Bidali aipatutako erabiltzaileei besterik ez", - "private": "Jarraitzaileentzako bakarrik- Bidali jarraitzaileentzat bakarrik", - "public": "Publickoa - Bistaratu denbora-lerro publikoetan", - "unlisted": "Zerrendatu gabea - ez bidali denbora-lerro publikoetan" + "direct": "Zuzena: Bidali aipatutako erabiltzaileei besterik ez", + "private": "Jarraitzaileentzako bakarrik: Bidali jarraitzaileentzat bakarrik", + "public": "Publikoa: Bistaratu denbora-lerro publikoetan", + "unlisted": "Zerrendatu gabea: ez bidali denbora-lerro publikoetara" } }, "registration": { @@ -228,7 +228,7 @@ "avatar_size_instruction": "Avatar irudien gomendatutako gutxieneko tamaina 150x150 pixel dira.", "export_theme": "Gorde aurre-ezarpena", "filtering": "Iragazten", - "filtering_explanation": "Hitz hauek dituzten muzu guztiak isilduak izango dira. Lerro bakoitzeko bat", + "filtering_explanation": "Hitz hauek dituzten mezu guztiak isilduak izango dira. Lerro bakoitzeko bat", "follow_export": "Jarraitzen dituzunak esportatu", "follow_export_button": "Esportatu zure jarraitzaileak csv fitxategi batean", "follow_import": "Jarraitzen dituzunak inportatu", @@ -276,7 +276,7 @@ "no_blocks": "Ez daude erabiltzaile blokeatutak", "no_mutes": "Ez daude erabiltzaile mututuak", "hide_follows_description": "Ez erakutsi nor jarraitzen ari naizen", - "hide_followers_description": "Ez erakutsi nor ari de ni jarraitzen", + "hide_followers_description": "Ez erakutsi nor ari den ni jarraitzen", "show_admin_badge": "Erakutsi Administratzaile etiketa nire profilan", "show_moderator_badge": "Erakutsi Moderatzaile etiketa nire profilan", "nsfw_clickthrough": "Gaitu klika hunkigarri eranskinak ezkutatzeko", @@ -456,8 +456,8 @@ "time": { "day": "{0} egun", "days": "{0} egun", - "day_short": "{0}d", - "days_short": "{0}d", + "day_short": "{0}e", + "days_short": "{0}e", "hour": "{0} ordu", "hours": "{0} ordu", "hour_short": "{0}o", @@ -492,7 +492,7 @@ "conversation": "Elkarrizketa", "error_fetching": "Errorea eguneraketak eskuratzen", "load_older": "Kargatu mezu zaharragoak", - "no_retweet_hint": "Mezu hau jarraitzailentzko bakarrik markatuta dago eta ezin da errepikatu", + "no_retweet_hint": "Mezu hau jarraitzailentzako bakarrik markatuta dago eta ezin da errepikatu", "repeated": "Errepikatuta", "show_new": "Berriena erakutsi", "up_to_date": "Eguneratuta", @@ -507,8 +507,10 @@ "unpin": "Aingura ezeztatu profilatik", "pinned": "Ainguratuta", "delete_confirm": "Mezu hau benetan ezabatu nahi duzu?", - "reply_to": "Erantzun", - "replies_list": "Erantzunak:" + "reply_to": "Erantzuten", + "replies_list": "Erantzunak:", + "mute_conversation": "Elkarrizketa isilarazi", + "unmute_conversation": "Elkarrizketa aktibatu" }, "user_card": { "approve": "Onartu", @@ -581,7 +583,7 @@ }, "tool_tip": { "media_upload": "Multimedia igo", - "repeat": "Erreplikatu", + "repeat": "Errepikatu", "reply": "Erantzun", "favorite": "Gogokoa", "user_settings": "Erabiltzaile ezarpenak" @@ -601,10 +603,21 @@ } }, "search": { - "people": "Gendea", + "people": "Erabiltzaileak", "hashtags": "Traolak", "person_talking": "{count} pertsona hitzegiten", - "people_talking": "{count} gende hitzegiten", + "people_talking": "{count} jende hitzegiten", "no_results": "Emaitzarik ez" + }, + "password_reset": { + "forgot_password": "Pasahitza ahaztua?", + "password_reset": "Pasahitza berrezarri", + "instruction": "Idatzi zure helbide elektronikoa edo erabiltzaile izena. Pasahitza berrezartzeko esteka bidaliko dizugu.", + "placeholder": "Zure e-posta edo erabiltzaile izena", + "check_email": "Begiratu zure posta elektronikoa pasahitza berrezarri ahal izateko.", + "return_home": "Itzuli hasierara", + "not_found": "Ezin izan dugu helbide elektroniko edo erabiltzaile hori aurkitu.", + "too_many_requests": "Saiakera gehiegi burutu ditzu, saiatu berriro geroxeago.", + "password_reset_disabled": "Pasahitza berrezartzea debekatuta dago. Mesedez, jarri harremanetan instantzia administratzailearekin." } }
\ No newline at end of file diff --git a/src/i18n/oc.json b/src/i18n/oc.json index 6100a4d2..680ad6dd 100644 --- a/src/i18n/oc.json +++ b/src/i18n/oc.json @@ -5,7 +5,7 @@ "exporter": { "export": "Exportar", "processing": "Tractament, vos demandarem lèu de telecargar lo fichièr" - }, + }, "features_panel": { "chat": "Chat", "gopher": "Gopher", @@ -30,12 +30,12 @@ "cancel": "Anullar" }, "image_cropper": { - "crop_picture": "Talhar l’imatge", - "save": "Salvar", - "save_without_cropping": "Salvar sens talhada", - "cancel": "Anullar" + "crop_picture": "Talhar l’imatge", + "save": "Salvar", + "save_without_cropping": "Salvar sens talhada", + "cancel": "Anullar" }, - "importer": { + "importer": { "submit": "Mandar", "success": "Corrèctament importat.", "error": "Una error s’es producha pendent l’importacion d’aqueste fichièr." @@ -65,6 +65,7 @@ "timeline": "Flux d’actualitat", "twkn": "Lo malhum conegut", "user_search": "Cèrca d’utilizaires", + "search": "Cercar", "who_to_follow": "Qual seguir", "preferences": "Preferéncias" }, @@ -79,19 +80,27 @@ "no_more_notifications": "Pas mai de notificacions" }, "polls": { -"add_poll": "Ajustar un sondatge", + "add_poll": "Ajustar un sondatge", "add_option": "Ajustar d’opcions", - "option": "Opcion", - "votes": "vòtes", - "vote": "Votar", - "type": "Tipe de sondatge", - "single_choice": "Causida unica", - "multiple_choices": "Causida multipla", - "expiry": "Durada del sondatge", - "expires_in": "Lo sondatge s’acabarà {0}", - "expired": "Sondatge acabat {0}", - "not_enough_options": "I a pas pro d’opcions" - }, + "option": "Opcion", + "votes": "vòtes", + "vote": "Votar", + "type": "Tipe de sondatge", + "single_choice": "Causida unica", + "multiple_choices": "Causida multipla", + "expiry": "Durada del sondatge", + "expires_in": "Lo sondatge s’acabarà {0}", + "expired": "Sondatge acabat {0}", + "not_enough_options": "I a pas pro d’opcions" + }, + "stickers": { + "add_sticker": "Ajustar un pegasolet" + }, + "interactions": { + "favs_repeats": "Repeticions e favorits", + "follows": "Nòus seguidors", + "load_older": "Cargar d’interaccions anterioras" + }, "post_status": { "new_status": "Publicar d’estatuts novèls", "account_not_locked_warning": "Vòstre compte es pas {0}. Qual que siá pòt vos seguir per veire vòstras publicacions destinadas pas qu’a vòstres seguidors.", @@ -137,8 +146,8 @@ } }, "selectable_list": { - "select_all": "O seleccionar tot" - }, + "select_all": "O seleccionar tot" + }, "settings": { "app_name": "Nom de l’aplicacion", "attachmentRadius": "Pèças juntas", @@ -216,7 +225,6 @@ "use_contain_fit": "Talhar pas las pèças juntas per las vinhetas", "name": "Nom", "name_bio": "Nom & Bio", - "new_password": "Nòu senhal", "notification_visibility_follows": "Abonaments", "notification_visibility_likes": "Aimar", @@ -264,12 +272,12 @@ "subject_line_email": "Coma los corrièls : \"re: subjècte\"", "subject_line_mastodon": "Coma mastodon : copiar tal coma es", "subject_line_noop": "Copiar pas", -"post_status_content_type": "Publicar lo tipe de contengut dels estatuts", + "post_status_content_type": "Publicar lo tipe de contengut dels estatuts", "stop_gifs": "Lançar los GIFs al subrevòl", "streaming": "Activar lo cargament automatic dels novèls estatus en anar amont", "text": "Tèxte", "theme": "Tèma", - "theme_help_v2_1": "You can also override certain component's colors and opacity by toggling the checkbox, use \"Clear all\" button to clear all overrides.", + "theme_help_v2_1": "Podètz tanben remplaçar la color d’unes compausants en clicant la case, utilizatz lo boton \"O escafar tot\" per escafar totes las subrecargadas.", "theme_help_v2_2": "Icons underneath some entries are background/text contrast indicators, hover over for detailed info. Please keep in mind that when using transparency contrast indicators show the worst possible case.", "theme_help": "Emplegatz los còdis de color hex (#rrggbb) per personalizar vòstre tèma de color.", "tooltipRadius": "Astúcias/alèrtas", @@ -280,14 +288,14 @@ "true": "òc" }, "notifications": "Notificacions", - "notification_setting": "Receber las notificacions de :", + "notification_setting": "Recebre las notificacions de :", "notification_setting_follows": "Utilizaires que seguissètz", "notification_setting_non_follows": "Utilizaires que seguissètz pas", "notification_setting_followers": "Utilizaires que vos seguisson", "notification_setting_non_followers": "Utilizaires que vos seguisson pas", - "notification_mutes": "Per receber pas mai d’un utilizaire en particular, botatz-lo en silenci.", + "notification_mutes": "Per recebre pas mai d’un utilizaire en particular, botatz-lo en silenci.", "notification_blocks": "Blocar un utilizaire arrèsta totas las notificacions tan coma quitar de los seguir.", - "enable_web_push_notifications": "Activar las notificacions web push", + "enable_web_push_notifications": "Activar las notificacions web push", "style": { "switcher": { "keep_color": "Gardar las colors", @@ -442,7 +450,7 @@ "conversation": "Conversacion", "error_fetching": "Error en cercant de mesas a jorn", "load_older": "Ne veire mai", - "no_retweet_hint": "Las publicacions marcadas pels seguidors solament o dirèctas se pòdon pas repetir", + "no_retweet_hint": "Las publicacions marcadas pels seguidors solament o dirèctas se pòdon pas repetir", "repeated": "repetit", "show_new": "Ne veire mai", "up_to_date": "A jorn", @@ -477,6 +485,8 @@ "per_day": "per jorn", "remote_follow": "Seguir a distància", "statuses": "Estatuts", + "subscribe": "S’abonar", + "unsubscribe": "Se desabonar", "unblock": "Desblocar", "unblock_progress": "Desblocatge...", "block_progress": "Blocatge...", @@ -501,7 +511,7 @@ "quarantine": "Defendre la federacion de las publicacions de l’utilizaire", "delete_user": "Suprimir l’utilizaire", "delete_user_confirmation": "Volètz vertadièrament far aquò ? Aquesta accion se pòt pas anullar." - } + } }, "user_profile": { "timeline_title": "Flux utilizaire", @@ -532,5 +542,12 @@ "GiB": "Gio", "TiB": "Tio" } + }, + "search": { + "people": "Gent", + "hashtags": "Etiquetas", + "person_talking": "{count} persona ne parla", + "people_talking": "{count} personas ne parlan", + "no_results": "Cap de resultats" } }
\ No newline at end of file diff --git a/src/i18n/zh.json b/src/i18n/zh.json index da6dae5f..80c4e0d8 100644 --- a/src/i18n/zh.json +++ b/src/i18n/zh.json @@ -2,6 +2,10 @@ "chat": { "title": "聊天" }, + "exporter": { + "export": "导出", + "processing": "正在处理,稍后会提示您下载文件" + }, "features_panel": { "chat": "聊天", "gopher": "Gopher", @@ -17,23 +21,66 @@ }, "general": { "apply": "应用", - "submit": "提交" + "submit": "提交", + "more": "更多", + "generic_error": "发生一个错误", + "optional": "可选项", + "show_more": "显示更多", + "show_less": "显示更少", + "cancel": "取消", + "disable": "禁用", + "enable": "启用", + "confirm": "确认", + "verify": "验证" + }, + "image_cropper": { + "crop_picture": "裁剪图片", + "save": "保存", + "save_without_cropping": "保存未经裁剪的图片", + "cancel": "取消" + }, + "importer": { + "submit": "提交", + "success": "导入成功。", + "error": "导入此文件时出现一个错误。" }, "login": { "login": "登录", + "description": "用 OAuth 登录", "logout": "登出", "password": "密码", "placeholder": "例如:lain", "register": "注册", - "username": "用户名" + "username": "用户名", + "hint": "登录后加入讨论", + "authentication_code": "验证码", + "enter_recovery_code": "输入一个恢复码", + "enter_two_factor_code": "输入一个双重因素验证码", + "recovery_code": "恢复码", + "heading" : { + "totp" : "双重因素验证", + "recovery" : "双重因素恢复" + } + }, + "media_modal": { + "previous": "往前", + "next": "往后" }, "nav": { + "about": "关于", + "back": "Back", "chat": "本地聊天", "friend_requests": "关注请求", "mentions": "提及", + "interactions": "互动", + "dms": "私信", "public_tl": "公共时间线", "timeline": "时间线", - "twkn": "所有已知网络" + "twkn": "所有已知网络", + "user_search": "用户搜索", + "search": "搜索", + "who_to_follow": "推荐关注", + "preferences": "偏好设置" }, "notifications": { "broken_favorite": "未知的状态,正在搜索中...", @@ -42,24 +89,57 @@ "load_older": "加载更早的通知", "notifications": "通知", "read": "阅读!", - "repeated_you": "转发了你的状态" + "repeated_you": "转发了你的状态", + "no_more_notifications": "没有更多的通知" + }, + "polls": { + "add_poll": "增加问卷调查", + "add_option": "增加选项", + "option": "选项", + "votes": "投票", + "vote": "投票", + "type": "问卷类型", + "single_choice": "单选项", + "multiple_choices": "多选项", + "expiry": "问卷的时间", + "expires_in": "投票于 {0} 内结束", + "expired": "投票 {0} 前已结束", + "not_enough_options": "投票的选项太少" + }, + "stickers": { + "add_sticker": "添加贴纸" + }, + "interactions": { + "favs_repeats": "转发和收藏", + "follows": "新的关注着", + "load_older": "加载更早的互动" }, "post_status": { + "new_status": "发布新状态", "account_not_locked_warning": "你的帐号没有 {0}。任何人都可以关注你并浏览你的上锁内容。", "account_not_locked_warning_link": "上锁", "attachments_sensitive": "标记附件为敏感内容", "content_type": { - "text/plain": "纯文本" + "text/plain": "纯文本", + "text/html": "HTML", + "text/markdown": "Markdown", + "text/bbcode": "BBCode" }, "content_warning": "主题(可选)", "default": "刚刚抵达上海", - "direct_warning": "本条内容只有被提及的用户能够看到。", + "direct_warning_to_all": "本条内容只有被提及的用户能够看到。", + "direct_warning_to_first_only": "本条内容只有被在消息开始处提及的用户能够看到。", "posting": "发送", + "scope_notice": { + "public": "本条内容可以被所有人看到", + "private": "关注你的人才能看到本条内容", + "unlisted": "本条内容既不在公共时间线,也不会在所有已知网络上可见" + }, "scope": { "direct": "私信 - 只发送给被提及的用户", "private": "仅关注者 - 只有关注了你的人能看到", "public": "公共 - 发送到公共时间轴", - "unlisted": "不公开 - 所有人可见,但不会发送到公共时间轴" + "unlisted": "不公开 - 不会发送到公共时间轴" } }, "registration": { @@ -68,9 +148,49 @@ "fullname": "全名", "password_confirm": "确认密码", "registration": "注册", - "token": "邀请码" + "token": "邀请码", + "captcha": "CAPTCHA", + "new_captcha": "点击图片获取新的验证码", + "username_placeholder": "例如: lain", + "fullname_placeholder": "例如: Lain Iwakura", + "bio_placeholder": "例如:\n你好, 我是 Lain.\n我是一个住在上海的宅男。你可能在某处见过我。", + "validations": { + "username_required": "不能留空", + "fullname_required": "不能留空", + "email_required": "不能留空", + "password_required": "不能留空", + "password_confirmation_required": "不能留空", + "password_confirmation_match": "密码不一致" + } + }, + "selectable_list": { + "select_all": "选择全部" }, "settings": { + "app_name": "App 名称", + "security": "安全", + "enter_current_password_to_confirm": "输入你当前密码来确认你的身份", + "mfa": { + "otp" : "OTP", + "setup_otp" : "设置 OTP", + "wait_pre_setup_otp" : "预设 OTP", + "confirm_and_enable" : "确认并启用 OTP", + "title": "双因素验证", + "generate_new_recovery_codes" : "生成新的恢复码", + "warning_of_generate_new_codes" : "当你生成新的恢复码时,你的就恢复码就失效了。", + "recovery_codes" : "恢复码。", + "waiting_a_recovery_codes": "接受备份码。。。", + "recovery_codes_warning" : "抄写这些号码,或者保存在安全的地方。这些号码不会再次显示。如果你无法访问你的 2FA app,也丢失了你的恢复码,你的账号就再也无法登录了。", + "authentication_methods" : "身份验证方法", + "scan": { + "title": "扫一下", + "desc": "使用你的双因素验证 app,扫描这个二维码,或者输入这些文字密钥:", + "secret_code": "密钥" + }, + "verify": { + "desc": "要启用双因素验证,请把你的双因素验证 app 里的数字输入:" + } + }, "attachmentRadius": "附件", "attachments": "附件", "autoload": "启用滚动到底部时的自动加载", @@ -79,6 +199,12 @@ "avatarRadius": "头像", "background": "背景", "bio": "简介", + "block_export": "拉黑名单导出", + "block_export_button": "导出你的拉黑名单到一个 csv 文件", + "block_import": "拉黑名单导入", + "block_import_error": "导入拉黑名单出错", + "blocks_imported": "拉黑名单导入成功!需要一点时间来处理。", + "blocks_tab": "块", "btnRadius": "按钮", "cBlue": "蓝色(回复,关注)", "cGreen": "绿色(转发)", @@ -88,6 +214,7 @@ "change_password_error": "修改密码的时候出了点问题。", "changed_password": "成功修改了密码!", "collapse_subject": "折叠带主题的内容", + "composing": "正在书写", "confirm_new_password": "确认新密码", "current_avatar": "当前头像", "current_password": "当前密码", @@ -98,12 +225,12 @@ "delete_account_description": "永久删除你的帐号和所有消息。", "delete_account_error": "删除账户时发生错误,如果一直删除不了,请联系实例管理员。", "delete_account_instructions": "在下面输入你的密码来确认删除账户", + "avatar_size_instruction": "推荐的头像图片最小的尺寸是 150x150 像素。", "export_theme": "导出预置主题", "filtering": "过滤器", "filtering_explanation": "所有包含以下词汇的内容都会被隐藏,一行一个", "follow_export": "导出关注", "follow_export_button": "将关注导出成 csv 文件", - "follow_export_processing": "正在处理,过一会儿就可以下载你的文件了", "follow_import": "导入关注", "follow_import_error": "导入关注时错误", "follows_imported": "关注已导入!尚需要一些时间来处理。", @@ -111,12 +238,22 @@ "general": "通用", "hide_attachments_in_convo": "在对话中隐藏附件", "hide_attachments_in_tl": "在时间线上隐藏附件", + "hide_muted_posts": "不显示被隐藏的用户的帖子", + "max_thumbnails": "最多再每个帖子所能显示的缩略图数量", + "hide_isp": "隐藏指定实例的面板H", + "preload_images": "预载图片", + "use_one_click_nsfw": "点击一次以打开工作场所不适宜的附件", "hide_post_stats": "隐藏推文相关的统计数据(例如:收藏的次数)", "hide_user_stats": "隐藏用户的统计数据(例如:关注者的数量)", + "hide_filtered_statuses": "隐藏过滤的状态", + "import_blocks_from_a_csv_file": "从 csv 文件中导入拉黑名单", "import_followers_from_a_csv_file": "从 csv 文件中导入关注", "import_theme": "导入预置主题", "inputRadius": "输入框", + "checkboxRadius": "复选框", "instance_default": "(默认:{value})", + "instance_default_simple": "(默认)", + "interface": "界面", "interfaceLanguage": "界面语言", "invalid_theme_imported": "您所选择的主题文件不被 Pleroma 支持,因此主题未被修改。", "limited_availability": "在您的浏览器中无法使用", @@ -124,6 +261,9 @@ "lock_account_description": "你需要手动审核关注请求", "loop_video": "循环视频", "loop_video_silent_only": "只循环没有声音的视频(例如:Mastodon 里的“GIF”)", + "mutes_tab": "隐藏", + "play_videos_in_modal": "在弹出框内播放视频", + "use_contain_fit": "生成缩略图时不要裁剪附件。", "name": "名字", "name_bio": "名字及简介", "new_password": "新密码", @@ -133,9 +273,15 @@ "notification_visibility_mentions": "提及", "notification_visibility_repeats": "转发", "no_rich_text_description": "不显示富文本格式", + "no_blocks": "没有拉黑的", + "no_mutes": "没有隐藏", + "hide_follows_description": "不要显示我所关注的人", + "hide_followers_description": "不要显示关注我的人", + "show_admin_badge": "显示管理徽章", + "show_moderator_badge": "显示版主徽章", "nsfw_clickthrough": "将不和谐附件隐藏,点击才能打开", "oauth_tokens": "OAuth令牌", - "token": "代币", + "token": "令牌", "refresh_token": "刷新令牌", "valid_until": "有效期至", "revoke_token": "撤消", @@ -151,25 +297,196 @@ "reply_visibility_all": "显示所有回复", "reply_visibility_following": "只显示发送给我的回复/发送给我关注的用户的回复", "reply_visibility_self": "只显示发送给我的回复", + "autohide_floating_post_button": "自动隐藏新帖子的按钮(移动设备)", "saving_err": "保存设置时发生错误", "saving_ok": "设置已保存", + "search_user_to_block": "搜索你想屏蔽的用户", + "search_user_to_mute": "搜索你想要隐藏的用户", "security_tab": "安全", + "scope_copy": "回复时的复制范围(私信是总是复制的)", + "minimal_scopes_mode": "最小发文范围", "set_new_avatar": "设置新头像", "set_new_profile_background": "设置新的个人资料背景", "set_new_profile_banner": "设置新的横幅图片", "settings": "设置", + "subject_input_always_show": "总是显示主题框", + "subject_line_behavior": "回复时复制主题", + "subject_line_email": "比如电邮: \"re: 主题\"", + "subject_line_mastodon": "比如 mastodon: copy as is", + "subject_line_noop": "不要复制", + "post_status_content_type": "发文状态内容类型", "stop_gifs": "鼠标悬停时播放GIF", "streaming": "开启滚动到顶部时的自动推送", "text": "文本", "theme": "主题", "theme_help": "使用十六进制代码(#rrggbb)来设置主题颜色。", + "theme_help_v2_1": "你也可以通过切换复选框来覆盖某些组件的颜色和透明。使用“清除所有”来清楚所有覆盖设置。", + "theme_help_v2_2": "某些条目下的图标是背景或文本对比指示器,鼠标悬停可以获取详细信息。请记住,使用透明度来显示最差的情况。", "tooltipRadius": "提醒", + "upload_a_photo": "上传照片", "user_settings": "用户设置", "values": { "false": "否", "true": "是" + }, + "notifications": "通知", + "notification_setting": "通知来源:", + "notification_setting_follows": "你所关注的用户", + "notification_setting_non_follows": "你没有关注的用户", + "notification_setting_followers": "关注你的用户", + "notification_setting_non_followers": "没有关注你的用户", + "notification_mutes": "要停止收到某个指定的用户的通知,请使用隐藏功能。", + "notification_blocks": "拉黑一个用户会停掉所有他的通知,等同于取消关注。", + "enable_web_push_notifications": "启用 web 推送通知", + "style": { + "switcher": { + "keep_color": "保留颜色", + "keep_shadows": "保留阴影", + "keep_opacity": "保留透明度", + "keep_roundness": "保留圆角", + "keep_fonts": "保留字体", + "save_load_hint": "\"保留\" 选项在选择或加载主题时保留当前设置的选项,在导出主题时还会存储上述选项。当所有复选框未设置时,导出主题将保存所有内容。", + "reset": "重置", + "clear_all": "清除全部", + "clear_opacity": "清除透明度" + }, + "common": { + "color": "颜色", + "opacity": "透明度", + "contrast": { + "hint": "对比度是 {ratio}, 它 {level} {context}", + "level": { + "aa": "符合 AA 等级准则(最低)", + "aaa": "符合 AAA 等级准则(推荐)", + "bad": "不符合任何辅助功能指南" + }, + "context": { + "18pt": "大字文本 (18pt+)", + "text": "文本" + } + } + }, + "common_colors": { + "_tab_label": "常规", + "main": "常用颜色", + "foreground_hint": "点击”高级“ 标签进行细致的控制", + "rgbo": "图标,口音,徽章" + }, + "advanced_colors": { + "_tab_label": "高级", + "alert": "提醒或警告背景色", + "alert_error": "错误", + "badge": "徽章背景", + "badge_notification": "通知", + "panel_header": "面板标题", + "top_bar": "顶栏", + "borders": "边框", + "buttons": "按钮", + "inputs": "输入框", + "faint_text": "灰度文字" + }, + "radii": { + "_tab_label": "圆角" + }, + "shadows": { + "_tab_label": "阴影和照明", + "component": "组件", + "override": "覆盖", + "shadow_id": "阴影 #{value}", + "blur": "模糊", + "spread": "扩散", + "inset": "插入内部", + "hint": "对于阴影你还可以使用 --variable 作为颜色值来使用 CSS3 变量。请注意,这种情况下,透明设置将不起作用。", + "filter_hint": { + "always_drop_shadow": "警告,此阴影设置会总是使用 {0} ,如果浏览器支持的话。", + "drop_shadow_syntax": "{0} 不支持参数 {1} 和关键词 {2} 。", + "avatar_inset": "请注意组合两个内部和非内部的阴影到头像上,在透明头像上可能会有意料之外的效果。", + "spread_zero": "阴影的扩散 > 0 会同设置成零一样", + "inset_classic": "插入内部的阴影会使用 {0}" + }, + "components": { + "panel": "面板", + "panelHeader": "面板标题", + "topBar": "顶栏", + "avatar": "用户头像(在个人资料栏)", + "avatarStatus": "用户头像(在帖子显示栏)", + "popup": "弹窗和工具提示", + "button": "按钮", + "buttonHover": "按钮(悬停)", + "buttonPressed": "按钮(按下)", + "buttonPressedHover": "按钮(按下和悬停)", + "input": "输入框" + } + }, + "fonts": { + "_tab_label": "字体", + "help": "给用户界面的元素选择字体。选择 “自选”的你必须输入确切的字体名称。", + "components": { + "interface": "界面", + "input": "输入框", + "post": "发帖文字", + "postCode": "帖子中使用等间距文字(富文本)" + }, + "family": "字体名称", + "size": "大小 (in px)", + "weight": "字重 (粗体))", + "custom": "自选" + }, + "preview": { + "header": "预览", + "content": "内容", + "error": "例子错误", + "button": "按钮", + "text": "有堆 {0} 和 {1}", + "mono": "内容", + "input": "刚刚抵达上海", + "faint_link": "帮助菜单", + "fine_print": "阅读我们的 {0} 学不到什么东东!", + "header_faint": "这很正常", + "checkbox": "我已经浏览了 TOC", + "link": "一个很棒的摇滚链接" + } + }, + "version": { + "title": "版本", + "backend_version": "后端版本", + "frontend_version": "前端版本" } }, + "time": { + "day": "{0} 天", + "days": "{0} 天", + "day_short": "{0}d", + "days_short": "{0}d", + "hour": "{0} 小时", + "hours": "{0} 小时", + "hour_short": "{0}h", + "hours_short": "{0}h", + "in_future": "还有 {0}", + "in_past": "{0} 之前", + "minute": "{0} 分钟", + "minutes": "{0} 分钟", + "minute_short": "{0}min", + "minutes_short": "{0}min", + "month": "{0} 月", + "months": "{0} 月", + "month_short": "{0}mo", + "months_short": "{0}mo", + "now": "刚刚", + "now_short": "刚刚", + "second": "{0} 秒", + "seconds": "{0} 秒", + "second_short": "{0}s", + "seconds_short": "{0}s", + "week": "{0} 周", + "weeks": "{0} 周", + "week_short": "{0}w", + "weeks_short": "{0}w", + "year": "{0} 年", + "years": "{0} 年", + "year_short": "{0}y", + "years_short": "{0}y" + }, "timeline": { "collapse": "折叠", "conversation": "对话", @@ -178,29 +495,129 @@ "no_retweet_hint": "这条内容仅关注者可见,或者是私信,因此不能转发。", "repeated": "已转发", "show_new": "显示新内容", - "up_to_date": "已是最新" + "up_to_date": "已是最新", + "no_more_statuses": "没有更多的状态", + "no_statuses": "没有状态更新" + }, + "status": { + "favorites": "收藏", + "repeats": "转发", + "delete": "删除状态", + "pin": "在个人资料置顶", + "unpin": "取消在个人资料置顶", + "pinned": "置顶", + "delete_confirm": "你真的想要删除这条状态吗?", + "reply_to": "回复", + "replies_list": "回复:", + "mute_conversation": "隐藏对话", + "unmute_conversation": "对话取消隐藏" }, "user_card": { "approve": "允许", "block": "屏蔽", "blocked": "已屏蔽!", "deny": "拒绝", + "favorites": "收藏", "follow": "关注", + "follow_sent": "请求已发送!", + "follow_progress": "请求中", + "follow_again": "再次发送请求?", + "follow_unfollow": "取消关注", "followees": "正在关注", "followers": "关注者", "following": "正在关注!", "follows_you": "关注了你!", + "its_you": "就是你!!", + "media": "媒体", "mute": "隐藏", "muted": "已隐藏", "per_day": "每天", "remote_follow": "跨站关注", - "statuses": "状态" + "report": "报告", + "statuses": "状态", + "subscribe": "订阅", + "unsubscribe": "退订", + "unblock": "取消拉黑", + "unblock_progress": "取消拉黑中...", + "block_progress": "拉黑中...", + "unmute": "取消隐藏", + "unmute_progress": "取消隐藏中...", + "mute_progress": "隐藏中...", + "admin_menu": { + "moderation": "权限", + "grant_admin": "赋予管理权限", + "revoke_admin": "撤销管理权限", + "grant_moderator": "赋予版主权限", + "revoke_moderator": "撤销版主权限", + "activate_account": "激活账号", + "deactivate_account": "关闭账号", + "delete_account": "删除账号", + "force_nsfw": "标记所有的帖子都是 - 工作场合不适", + "strip_media": "从帖子里删除媒体文件", + "force_unlisted": "强制帖子为不公开", + "sandbox": "强制帖子为只有关注者可看", + "disable_remote_subscription": "禁止从远程实例关注用户", + "disable_any_subscription": "完全禁止关注用户", + "quarantine": "从联合实例中禁止用户帖子", + "delete_user": "删除用户", + "delete_user_confirmation": "你确认吗?此操作无法撤销。" + } }, "user_profile": { - "timeline_title": "用户时间线" + "timeline_title": "用户时间线", + "profile_does_not_exist": "抱歉,此个人资料不存在。", + "profile_loading_error": "抱歉,载入个人资料时出错。" + }, + "user_reporting": { + "title": "报告 {0}", + "add_comment_description": "此报告会发送给你的实例管理员。你可以在下面提供更多详细信息解释报告的缘由:", + "additional_comments": "其它信息", + "forward_description": "这个账号是从另外一个服务器。同时发送一个副本到那里?", + "forward_to": "转发 {0}", + "submit": "提交", + "generic_error": "当处理你的请求时,发生了一个错误。" }, "who_to_follow": { "more": "更多", "who_to_follow": "推荐关注" + }, + "tool_tip": { + "media_upload": "上传多媒体", + "repeat": "转发", + "reply": "回复", + "favorite": "收藏", + "user_settings": "用户设置" + }, + "upload":{ + "error": { + "base": "上传不成功。", + "file_too_big": "文件太大了 [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", + "default": "迟些再试" + }, + "file_size_units": { + "B": "B", + "KiB": "KiB", + "MiB": "MiB", + "GiB": "GiB", + "TiB": "TiB" + } + }, + "search": { + "people": "人", + "hashtags": "Hashtags", + "person_talking": "{count} 人谈论", + "people_talking": "{count} 人谈论", + "no_results": "没有搜索结果" + }, + "password_reset": { + "forgot_password": "忘记密码了?", + "password_reset": "重置密码", + "instruction": "输入你的电邮地址或者用户名,我们将发送一个链接到你的邮箱,用于重置密码。", + "placeholder": "你的电邮地址或者用户名", + "check_email": "检查你的邮箱,会有一个链接用于重置密码。", + "return_home": "回到首页", + "not_found": "我们无法找到匹配的邮箱地址或者用户名。", + "too_many_requests": "你触发了尝试的限制,请稍后再试。", + "password_reset_disabled": "密码重置已经被禁用。请联系你的实例管理员。" } } diff --git a/src/main.js b/src/main.js index b3256e8e..7923ffe8 100644 --- a/src/main.js +++ b/src/main.js @@ -15,6 +15,7 @@ import mediaViewerModule from './modules/media_viewer.js' import oauthTokensModule from './modules/oauth_tokens.js' import reportsModule from './modules/reports.js' import pollsModule from './modules/polls.js' +import postStatusModule from './modules/postStatus.js' import VueI18n from 'vue-i18n' @@ -26,6 +27,7 @@ import messages from './i18n/messages.js' import VueChatScroll from 'vue-chat-scroll' import VueClickOutside from 'v-click-outside' import PortalVue from 'portal-vue' +import VBodyScrollLock from './directives/body_scroll_lock' import VTooltip from 'v-tooltip' import afterStoreSetup from './boot/after_store.js' @@ -38,6 +40,7 @@ Vue.use(VueI18n) Vue.use(VueChatScroll) Vue.use(VueClickOutside) Vue.use(PortalVue) +Vue.use(VBodyScrollLock) Vue.use(VTooltip) const i18n = new VueI18n({ @@ -76,7 +79,8 @@ const persistedStateOptions = { mediaViewer: mediaViewerModule, oauthTokens: oauthTokensModule, reports: reportsModule, - polls: pollsModule + polls: pollsModule, + postStatus: postStatusModule }, plugins: [persistedState, pushNotifications], strict: false // Socket modifies itself, let's ignore this for now. diff --git a/src/modules/postStatus.js b/src/modules/postStatus.js new file mode 100644 index 00000000..638c1fb2 --- /dev/null +++ b/src/modules/postStatus.js @@ -0,0 +1,25 @@ +const postStatus = { + state: { + params: null, + modalActivated: false + }, + mutations: { + openPostStatusModal (state, params) { + state.params = params + state.modalActivated = true + }, + closePostStatusModal (state) { + state.modalActivated = false + } + }, + actions: { + openPostStatusModal ({ commit }, params) { + commit('openPostStatusModal', params) + }, + closePostStatusModal ({ commit }) { + commit('closePostStatusModal') + } + } +} + +export default postStatus diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 4356d0a7..918065d2 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -426,9 +426,13 @@ export const mutations = { newStatus.favoritedBy.push(user) } }, - setMuted (state, status) { + setMutedStatus (state, status) { const newStatus = state.allStatusesObject[status.id] - newStatus.muted = status.muted + newStatus.thread_muted = status.thread_muted + + if (newStatus.thread_muted !== undefined) { + state.conversationsObject[newStatus.statusnet_conversation_id].forEach(status => { status.thread_muted = newStatus.thread_muted }) + } }, setRetweeted (state, { status, value }) { const newStatus = state.allStatusesObject[status.id] @@ -566,11 +570,11 @@ const statuses = { }, muteConversation ({ rootState, commit }, statusId) { return rootState.api.backendInteractor.muteConversation(statusId) - .then((status) => commit('setMuted', status)) + .then((status) => commit('setMutedStatus', status)) }, unmuteConversation ({ rootState, commit }, statusId) { return rootState.api.backendInteractor.unmuteConversation(statusId) - .then((status) => commit('setMuted', status)) + .then((status) => commit('setMutedStatus', status)) }, retweet ({ rootState, commit }, status) { // Optimistic retweeting... diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index 6cc1851d..7438cd90 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -224,6 +224,7 @@ export const parseStatus = (data) => { output.statusnet_conversation_id = data.pleroma.conversation_id output.is_local = pleroma.local output.in_reply_to_screen_name = data.pleroma.in_reply_to_account_acct + output.thread_muted = pleroma.thread_muted } else { output.text = data.content output.summary = data.spoiler_text diff --git a/src/services/follow_manipulate/follow_manipulate.js b/src/services/follow_manipulate/follow_manipulate.js index 529fdb9b..d82ce593 100644 --- a/src/services/follow_manipulate/follow_manipulate.js +++ b/src/services/follow_manipulate/follow_manipulate.js @@ -9,10 +9,7 @@ const fetchUser = (attempt, user, store) => new Promise((resolve, reject) => { if (!following && !(locked && sent) && attempt <= 3) { // If we BE reports that we still not following that user - retry, // increment attempts by one - return fetchUser(++attempt, user, store) - } else { - // If we run out of attempts, just return whatever status is. - return sent + fetchUser(++attempt, user, store) } }) @@ -23,7 +20,7 @@ export const requestFollow = (user, store) => new Promise((resolve, reject) => { if (updated.following || (user.locked && user.requested)) { // If we get result immediately or the account is locked, just stop. - resolve({ sent: updated.requested }) + resolve() return } @@ -35,8 +32,8 @@ export const requestFollow = (user, store) => new Promise((resolve, reject) => { // Recursive Promise, it will call itself up to 3 times. return fetchUser(1, user, store) - .then((sent) => { - resolve({ sent }) + .then(() => { + resolve() }) }) }) diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js index f9ec3f6e..b6c4cf80 100644 --- a/src/services/notifications_fetcher/notifications_fetcher.service.js +++ b/src/services/notifications_fetcher/notifications_fetcher.service.js @@ -10,6 +10,11 @@ const fetchAndUpdate = ({ store, credentials, older = false }) => { const args = { credentials } const rootState = store.rootState || store.state const timelineData = rootState.statuses.notifications + const hideMutedPosts = typeof rootState.config.hideMutedPosts === 'undefined' + ? rootState.instance.hideMutedPosts + : rootState.config.hideMutedPosts + + args['withMuted'] = !hideMutedPosts args['timeline'] = 'notifications' if (older) { diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index f186d202..1cf7edc3 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -22,7 +22,7 @@ const setStyle = (href, commit) => { ***/ const head = document.head const body = document.body - body.style.display = 'none' + body.classList.add('hidden') const cssEl = document.createElement('link') cssEl.setAttribute('rel', 'stylesheet') cssEl.setAttribute('href', href) @@ -46,7 +46,7 @@ const setStyle = (href, commit) => { head.appendChild(styleEl) // const styleSheet = styleEl.sheet - body.style.display = 'initial' + body.classList.remove('hidden') } cssEl.addEventListener('load', setDynamic) @@ -75,7 +75,7 @@ const applyTheme = (input, commit) => { const { rules, theme } = generatePreset(input) const head = document.head const body = document.body - body.style.display = 'none' + body.classList.add('hidden') const styleEl = document.createElement('style') head.appendChild(styleEl) @@ -86,7 +86,7 @@ const applyTheme = (input, commit) => { styleSheet.insertRule(`body { ${rules.colors} }`, 'index-max') styleSheet.insertRule(`body { ${rules.shadows} }`, 'index-max') styleSheet.insertRule(`body { ${rules.fonts} }`, 'index-max') - body.style.display = 'initial' + body.classList.remove('hidden') // commit('setOption', { name: 'colors', value: htmlColors }) // commit('setOption', { name: 'radii', value: radii }) @@ -1196,6 +1196,11 @@ body-parser@1.18.3, body-parser@^1.16.1: raw-body "2.3.3" type-is "~1.6.16" +body-scroll-lock@^2.6.4: + version "2.6.4" + resolved "https://registry.yarnpkg.com/body-scroll-lock/-/body-scroll-lock-2.6.4.tgz#567abc60ef4d656a79156781771398ef40462e94" + integrity sha512-NP08WsovlmxEoZP9pdlqrE+AhNaivlTrz9a0FF37BQsnOrpN48eNqivKkE7SYpM9N+YIPjsdVzfLAUQDBm6OQw== + boolbase@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" |
