Merge branch 'develop' into 'muting-fixes'
# Conflicts: # src/components/status/status.js
This commit is contained in:
commit
1306fac38f
|
@ -16,7 +16,7 @@
|
|||
background-position: 0 50%;
|
||||
}
|
||||
|
||||
i {
|
||||
i[class^='icon-'] {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,10 @@ body {
|
|||
overflow-x: hidden;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
|
|
|
@ -246,6 +246,7 @@ const getNodeInfo = async ({ store }) => {
|
|||
store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') })
|
||||
store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: features.includes('polls') })
|
||||
store.dispatch('setInstanceOption', { name: 'pollLimits', value: metadata.pollLimits })
|
||||
store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled })
|
||||
|
||||
store.dispatch('setInstanceOption', { name: 'restrictedNicknames', value: metadata.restrictedNicknames })
|
||||
store.dispatch('setInstanceOption', { name: 'postFormats', value: metadata.postFormats })
|
||||
|
|
|
@ -9,6 +9,7 @@ import UserProfile from 'components/user_profile/user_profile.vue'
|
|||
import Search from 'components/search/search.vue'
|
||||
import Settings from 'components/settings/settings.vue'
|
||||
import Registration from 'components/registration/registration.vue'
|
||||
import PasswordReset from 'components/password_reset/password_reset.vue'
|
||||
import UserSettings from 'components/user_settings/user_settings.vue'
|
||||
import FollowRequests from 'components/follow_requests/follow_requests.vue'
|
||||
import OAuthCallback from 'components/oauth_callback/oauth_callback.vue'
|
||||
|
@ -46,6 +47,7 @@ export default (store) => {
|
|||
{ name: 'dms', path: '/users/:username/dms', component: DMs, beforeEnter: validateAuthenticatedRoute },
|
||||
{ name: 'settings', path: '/settings', component: Settings },
|
||||
{ name: 'registration', path: '/registration', component: Registration },
|
||||
{ name: 'password-reset', path: '/password-reset', component: PasswordReset },
|
||||
{ name: 'registration-token', path: '/registration/:token', component: Registration },
|
||||
{ name: 'friend-requests', path: '/friend-requests', component: FollowRequests, beforeEnter: validateAuthenticatedRoute },
|
||||
{ name: 'user-settings', path: '/user-settings', component: UserSettings, beforeEnter: validateAuthenticatedRoute },
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -33,6 +33,11 @@
|
|||
type="password"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<router-link :to="{name: 'password-reset'}">
|
||||
{{ $t('password_reset.forgot_password') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
import { mapState } from 'vuex'
|
||||
import passwordResetApi from '../../services/new_api/password_reset.js'
|
||||
|
||||
const passwordReset = {
|
||||
data: () => ({
|
||||
user: {
|
||||
email: ''
|
||||
},
|
||||
isPending: false,
|
||||
success: false,
|
||||
throttled: false,
|
||||
error: null
|
||||
}),
|
||||
computed: {
|
||||
...mapState({
|
||||
signedIn: (state) => !!state.users.currentUser,
|
||||
instance: state => state.instance
|
||||
}),
|
||||
mailerEnabled () {
|
||||
return this.instance.mailerEnabled
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (this.signedIn) {
|
||||
this.$router.push({ name: 'root' })
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
dismissError () {
|
||||
this.error = null
|
||||
},
|
||||
submit () {
|
||||
this.isPending = true
|
||||
const email = this.user.email
|
||||
const instance = this.instance.server
|
||||
|
||||
passwordResetApi({ instance, email }).then(({ status }) => {
|
||||
this.isPending = false
|
||||
this.user.email = ''
|
||||
|
||||
if (status === 204) {
|
||||
this.success = true
|
||||
this.error = null
|
||||
} else if (status === 404 || status === 400) {
|
||||
this.error = this.$t('password_reset.not_found')
|
||||
this.$nextTick(() => {
|
||||
this.$refs.email.focus()
|
||||
})
|
||||
} else if (status === 429) {
|
||||
this.throttled = true
|
||||
this.error = this.$t('password_reset.too_many_requests')
|
||||
}
|
||||
}).catch(() => {
|
||||
this.isPending = false
|
||||
this.user.email = ''
|
||||
this.error = this.$t('general.generic_error')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default passwordReset
|
|
@ -0,0 +1,116 @@
|
|||
<template>
|
||||
<div class="settings panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{{ $t('password_reset.password_reset') }}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form
|
||||
class="password-reset-form"
|
||||
@submit.prevent="submit"
|
||||
>
|
||||
<div class="container">
|
||||
<div v-if="!mailerEnabled">
|
||||
<p>
|
||||
{{ $t('password_reset.password_reset_disabled') }}
|
||||
</p>
|
||||
</div>
|
||||
<div v-else-if="success || throttled">
|
||||
<p v-if="success">
|
||||
{{ $t('password_reset.check_email') }}
|
||||
</p>
|
||||
<div class="form-group text-center">
|
||||
<router-link :to="{name: 'root'}">
|
||||
{{ $t('password_reset.return_home') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p>
|
||||
{{ $t('password_reset.instruction') }}
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<input
|
||||
ref="email"
|
||||
v-model="user.email"
|
||||
:disabled="isPending"
|
||||
:placeholder="$t('password_reset.placeholder')"
|
||||
class="form-control"
|
||||
type="input"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button
|
||||
:disabled="isPending"
|
||||
type="submit"
|
||||
class="btn btn-default btn-block"
|
||||
>
|
||||
{{ $t('general.submit') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p
|
||||
v-if="error"
|
||||
class="alert error notice-dismissible"
|
||||
>
|
||||
<span>{{ error }}</span>
|
||||
<a
|
||||
class="button-icon dismiss"
|
||||
@click.prevent="dismissError()"
|
||||
>
|
||||
<i class="icon-cancel" />
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./password_reset.js"></script>
|
||||
<style lang="scss">
|
||||
@import '../../_variables.scss';
|
||||
|
||||
.password-reset-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0.6em;
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex: 1 0;
|
||||
flex-direction: column;
|
||||
margin-top: 0.6em;
|
||||
max-width: 18rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 1em;
|
||||
padding: 0.3em 0.0em 0.3em;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.error {
|
||||
text-align: center;
|
||||
animation-name: shakeError;
|
||||
animation-duration: 0.4s;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 0.5em;
|
||||
margin: 0.3em 0.0em 1em;
|
||||
}
|
||||
|
||||
.notice-dismissible {
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
.icon-cancel {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
|
@ -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.status.thread_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
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
v-if="animated"
|
||||
ref="canvas"
|
||||
/>
|
||||
<!-- NOTE: key is required to force to re-render img tag when src is changed -->
|
||||
<img
|
||||
ref="src"
|
||||
:key="src"
|
||||
:src="src"
|
||||
:referrerpolicy="referrerpolicy"
|
||||
@load="onLoad"
|
||||
|
|
|
@ -25,7 +25,8 @@ const Timeline = {
|
|||
'tag',
|
||||
'embedded',
|
||||
'count',
|
||||
'pinnedStatusIds'
|
||||
'pinnedStatusIds',
|
||||
'inProfile'
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
@ -112,9 +111,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 () {
|
||||
|
|
|
@ -135,13 +135,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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -26,7 +26,7 @@ const WhoToFollow = {
|
|||
}
|
||||
this.users.push(user)
|
||||
|
||||
this.$store.state.api.backendInteractor.externalProfile(user.screen_name)
|
||||
this.$store.state.api.backendInteractor.fetchUser({ id: user.screen_name })
|
||||
.then((externalUser) => {
|
||||
if (!externalUser.error) {
|
||||
this.$store.commit('addNewUsers', [externalUser])
|
||||
|
|
|
@ -13,7 +13,7 @@ function showWhoToFollow (panel, reply) {
|
|||
toFollow.img = img
|
||||
toFollow.name = name
|
||||
|
||||
panel.$store.state.api.backendInteractor.externalProfile(name)
|
||||
panel.$store.state.api.backendInteractor.fetchUser({ id: name })
|
||||
.then((externalUser) => {
|
||||
if (!externalUser.error) {
|
||||
panel.$store.commit('addNewUsers', [externalUser])
|
||||
|
|
|
@ -608,5 +608,16 @@
|
|||
"person_talking": "{count} person talking",
|
||||
"people_talking": "{count} people talking",
|
||||
"no_results": "No results"
|
||||
},
|
||||
"password_reset": {
|
||||
"forgot_password": "Forgot password?",
|
||||
"password_reset": "Password reset",
|
||||
"instruction": "Enter your email address or username. We will send you a link to reset your password.",
|
||||
"placeholder": "Your email or username",
|
||||
"check_email": "Check your email for a link to reset your password.",
|
||||
"return_home": "Return to the home page",
|
||||
"not_found": "We couldn't find that email or username.",
|
||||
"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."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -389,5 +389,16 @@
|
|||
"person_talking": "Популярно у {count} человека",
|
||||
"people_talking": "Популярно у {count} человек",
|
||||
"no_results": "Ничего не найдено"
|
||||
},
|
||||
"password_reset": {
|
||||
"forgot_password": "Забыли пароль?",
|
||||
"password_reset": "Сброс пароля",
|
||||
"instruction": "Введите ваш email или имя пользователя, и мы отправим вам ссылку для сброса пароля.",
|
||||
"placeholder": "Ваш email или имя пользователя",
|
||||
"check_email": "Проверьте ваш email и перейдите по ссылке для сброса пароля.",
|
||||
"return_home": "Вернуться на главную страницу",
|
||||
"not_found": "Мы не смогли найти аккаунт с таким email-ом или именем пользователя.",
|
||||
"too_many_requests": "Вы исчерпали допустимое количество попыток, попробуйте позже.",
|
||||
"password_reset_disabled": "Сброс пароля отключен. Cвяжитесь с администратором вашего сервера."
|
||||
}
|
||||
}
|
||||
|
|
443
src/i18n/zh.json
443
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,24 +297,195 @@
|
|||
"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": "折叠",
|
||||
|
@ -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": "密码重置已经被禁用。请联系你的实例管理员。"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import 'whatwg-fetch'
|
|||
import { RegistrationError, StatusCodeError } from '../errors/errors'
|
||||
|
||||
/* eslint-env browser */
|
||||
const EXTERNAL_PROFILE_URL = '/api/externalprofile/show.json'
|
||||
const QVITTER_USER_NOTIFICATIONS_READ_URL = '/api/qvitter/statuses/notifications/read.json'
|
||||
const BLOCKS_IMPORT_URL = '/api/pleroma/blocks_import'
|
||||
const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import'
|
||||
|
@ -220,14 +219,6 @@ const authHeaders = (accessToken) => {
|
|||
}
|
||||
}
|
||||
|
||||
const externalProfile = ({ profileUrl, credentials }) => {
|
||||
let url = `${EXTERNAL_PROFILE_URL}?profileurl=${profileUrl}`
|
||||
return fetch(url, {
|
||||
headers: authHeaders(credentials),
|
||||
method: 'GET'
|
||||
}).then((data) => data.json())
|
||||
}
|
||||
|
||||
const followUser = ({ id, credentials }) => {
|
||||
let url = MASTODON_FOLLOW_URL(id)
|
||||
return fetch(url, {
|
||||
|
@ -966,7 +957,6 @@ const apiService = {
|
|||
updateBg,
|
||||
updateProfile,
|
||||
updateBanner,
|
||||
externalProfile,
|
||||
importBlocks,
|
||||
importFollows,
|
||||
deleteAccount,
|
||||
|
|
|
@ -127,8 +127,6 @@ const backendInteractorService = credentials => {
|
|||
const updateBanner = ({ banner }) => apiService.updateBanner({ credentials, banner })
|
||||
const updateProfile = ({ params }) => apiService.updateProfile({ credentials, params })
|
||||
|
||||
const externalProfile = (profileUrl) => apiService.externalProfile({ profileUrl, credentials })
|
||||
|
||||
const importBlocks = (file) => apiService.importBlocks({ file, credentials })
|
||||
const importFollows = (file) => apiService.importFollows({ file, credentials })
|
||||
|
||||
|
@ -194,7 +192,6 @@ const backendInteractorService = credentials => {
|
|||
updateBg,
|
||||
updateBanner,
|
||||
updateProfile,
|
||||
externalProfile,
|
||||
importBlocks,
|
||||
importFollows,
|
||||
deleteAccount,
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import { reduce } from 'lodash'
|
||||
|
||||
const MASTODON_PASSWORD_RESET_URL = `/auth/password`
|
||||
|
||||
const resetPassword = ({ instance, email }) => {
|
||||
const params = { email }
|
||||
const query = reduce(params, (acc, v, k) => {
|
||||
const encoded = `${k}=${encodeURIComponent(v)}`
|
||||
return `${acc}&${encoded}`
|
||||
}, '')
|
||||
const url = `${instance}${MASTODON_PASSWORD_RESET_URL}?${query}`
|
||||
|
||||
return window.fetch(url, {
|
||||
method: 'POST'
|
||||
})
|
||||
}
|
||||
|
||||
export default resetPassword
|
|
@ -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 })
|
||||
|
|
Loading…
Reference in New Issue