Merge remote-tracking branch 'origin/develop' into disjointed-popovers
* origin/develop: (25 commits) force panel headers to be square on mobile (for now?) fix gap between panel heading and timeline menu Fix Open Chat button fix? fix Revert "Merge branch 'revert-a88abc7e' into 'develop'" Revert "Merge branch 'from/develop/tusooa/lang-opts' into 'develop'" Fix registration error stick chat scroll to bottom to help with OSK resizing the viewport Pass file name of cropped avatar to form data Add English translation for filtering end-of-poll notifications Add settings for filtering end-of-poll notifications Add English translations for poll-end notifications Show poll-end notifications Fix virtual scrolling when the user has a lot of pinned statuses Update dependency @vuelidate/core to v2.0.0-alpha.41 Make lint happy Make lint happy Add English translation for language options Add email language option to registration form ...
This commit is contained in:
commit
1154a6514b
|
@ -23,7 +23,7 @@
|
||||||
"@fortawesome/free-solid-svg-icons": "5.15.4",
|
"@fortawesome/free-solid-svg-icons": "5.15.4",
|
||||||
"@fortawesome/vue-fontawesome": "3.0.0-5",
|
"@fortawesome/vue-fontawesome": "3.0.0-5",
|
||||||
"@kazvmoe-infra/pinch-zoom-element": "1.2.0",
|
"@kazvmoe-infra/pinch-zoom-element": "1.2.0",
|
||||||
"@vuelidate/core": "2.0.0-alpha.35",
|
"@vuelidate/core": "2.0.0-alpha.41",
|
||||||
"@vuelidate/validators": "2.0.0-alpha.27",
|
"@vuelidate/validators": "2.0.0-alpha.27",
|
||||||
"body-scroll-lock": "2.7.1",
|
"body-scroll-lock": "2.7.1",
|
||||||
"chromatism": "3.0.0",
|
"chromatism": "3.0.0",
|
||||||
|
@ -31,6 +31,7 @@
|
||||||
"cropperjs": "1.5.12",
|
"cropperjs": "1.5.12",
|
||||||
"diff": "3.5.0",
|
"diff": "3.5.0",
|
||||||
"escape-html": "1.0.3",
|
"escape-html": "1.0.3",
|
||||||
|
"js-cookie": "^3.0.1",
|
||||||
"localforage": "1.10.0",
|
"localforage": "1.10.0",
|
||||||
"parse-link-header": "1.0.1",
|
"parse-link-header": "1.0.1",
|
||||||
"phoenix": "1.6.2",
|
"phoenix": "1.6.2",
|
||||||
|
@ -106,7 +107,7 @@
|
||||||
"sass": "1.20.1",
|
"sass": "1.20.1",
|
||||||
"sass-loader": "7.2.0",
|
"sass-loader": "7.2.0",
|
||||||
"selenium-server": "2.53.1",
|
"selenium-server": "2.53.1",
|
||||||
"semver": "5.6.0",
|
"semver": "5.7.1",
|
||||||
"serviceworker-webpack-plugin": "1.0.1",
|
"serviceworker-webpack-plugin": "1.0.1",
|
||||||
"shelljs": "0.8.5",
|
"shelljs": "0.8.5",
|
||||||
"sinon": "2.4.1",
|
"sinon": "2.4.1",
|
||||||
|
@ -120,7 +121,7 @@
|
||||||
"webpack": "4.46.0",
|
"webpack": "4.46.0",
|
||||||
"webpack-dev-middleware": "3.7.3",
|
"webpack-dev-middleware": "3.7.3",
|
||||||
"webpack-hot-middleware": "2.24.3",
|
"webpack-hot-middleware": "2.24.3",
|
||||||
"webpack-merge": "0.14.1"
|
"webpack-merge": "0.20.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 4.0.0",
|
"node": ">= 4.0.0",
|
||||||
|
|
|
@ -301,6 +301,15 @@ nav {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.panel-heading,
|
||||||
|
.panel-heading::after,
|
||||||
|
.panel-heading::before,
|
||||||
|
.panel,
|
||||||
|
.panel::after {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.underlay,
|
.underlay,
|
||||||
#sidebar,
|
#sidebar,
|
||||||
#notifs-column {
|
#notifs-column {
|
||||||
|
|
|
@ -40,7 +40,7 @@ const AccountActions = {
|
||||||
openChat () {
|
openChat () {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'chat',
|
name: 'chat',
|
||||||
params: { recipient_id: this.user.id }
|
params: { username: this.$store.state.users.currentUser.screen_name, recipient_id: this.user.id }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -43,6 +43,7 @@ const Chat = {
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
this.startFetching()
|
this.startFetching()
|
||||||
|
window.addEventListener('resize', this.handleResize)
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
window.addEventListener('scroll', this.handleScroll)
|
window.addEventListener('scroll', this.handleScroll)
|
||||||
|
@ -132,7 +133,7 @@ const Chat = {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// Preserves the scroll position when OSK appears or the posting form changes its height.
|
// "Sticks" scroll to bottom instead of top, helps with OSK resizing the viewport
|
||||||
handleResize (opts = {}) {
|
handleResize (opts = {}) {
|
||||||
const { expand = false, delayed = false } = opts
|
const { expand = false, delayed = false } = opts
|
||||||
|
|
||||||
|
@ -144,15 +145,14 @@ const Chat = {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
const { scrollHeight = undefined } = this.lastScrollPosition
|
const { offsetHeight = undefined } = getScrollPosition()
|
||||||
this.lastScrollPosition = getScrollPosition()
|
const diff = this.lastScrollPosition.offsetHeight - offsetHeight
|
||||||
|
if (diff !== 0 || (!this.bottomedOut() && expand)) {
|
||||||
const diff = this.lastScrollPosition.scrollHeight - scrollHeight
|
|
||||||
if (diff > 0 || (!this.bottomedOut() && expand)) {
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
window.scrollTo({ top: window.scrollY + diff })
|
window.scrollTo({ top: window.scrollY + diff })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
this.lastScrollPosition = getScrollPosition()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
scrollDown (options = {}) {
|
scrollDown (options = {}) {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<label for="interface-language-switcher">
|
<label for="interface-language-switcher">
|
||||||
{{ $t('settings.interfaceLanguage') }}
|
{{ promptText }}
|
||||||
</label>
|
</label>
|
||||||
{{ ' ' }}
|
{{ ' ' }}
|
||||||
<Select
|
<Select
|
||||||
id="interface-language-switcher"
|
id="interface-language-switcher"
|
||||||
v-model="language"
|
v-model="controlledLanguage"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="lang in languages"
|
v-for="lang in languages"
|
||||||
|
@ -20,39 +20,43 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import languagesObject from '../../i18n/messages'
|
|
||||||
import localeService from '../../services/locale/locale.service.js'
|
import localeService from '../../services/locale/locale.service.js'
|
||||||
import ISO6391 from 'iso-639-1'
|
|
||||||
import _ from 'lodash'
|
|
||||||
import Select from '../select/select.vue'
|
import Select from '../select/select.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Select
|
Select
|
||||||
},
|
},
|
||||||
|
props: {
|
||||||
|
promptText: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
language: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
setLanguage: {
|
||||||
|
type: Function,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
languages () {
|
languages () {
|
||||||
return _.map(languagesObject.languages, (code) => ({ code: code, name: this.getLanguageName(code) })).sort((a, b) => a.name.localeCompare(b.name))
|
return localeService.languages
|
||||||
},
|
},
|
||||||
|
|
||||||
language: {
|
controlledLanguage: {
|
||||||
get: function () { return this.$store.getters.mergedConfig.interfaceLanguage },
|
get: function () { return this.language },
|
||||||
set: function (val) {
|
set: function (val) {
|
||||||
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
|
this.setLanguage(val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
getLanguageName (code) {
|
getLanguageName (code) {
|
||||||
const specialLanguageNames = {
|
return localeService.getLanguageName(code)
|
||||||
'ja_easy': 'やさしいにほんご',
|
|
||||||
'zh': '简体中文',
|
|
||||||
'zh_Hant': '繁體中文'
|
|
||||||
}
|
|
||||||
const languageName = specialLanguageNames[code] || ISO6391.getNativeName(code)
|
|
||||||
const browserLocale = localeService.internalToBrowserLocale(code)
|
|
||||||
return languageName.charAt(0).toLocaleUpperCase(browserLocale) + languageName.slice(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,14 @@
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</small>
|
</small>
|
||||||
</span>
|
</span>
|
||||||
|
<span v-if="notification.type === 'poll'">
|
||||||
|
<FAIcon
|
||||||
|
class="type-icon"
|
||||||
|
icon="poll-h"
|
||||||
|
/>
|
||||||
|
{{ ' ' }}
|
||||||
|
<small>{{ $t('notifications.poll_ended') }}</small>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="isStatusNotification"
|
v-if="isStatusNotification"
|
||||||
|
|
|
@ -61,6 +61,15 @@
|
||||||
:class="{ 'menu-checkbox-checked': filters.moves }"
|
:class="{ 'menu-checkbox-checked': filters.moves }"
|
||||||
/>{{ $t('settings.notification_visibility_moves') }}
|
/>{{ $t('settings.notification_visibility_moves') }}
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
class="button-default dropdown-item"
|
||||||
|
@click="toggleNotificationFilter('polls')"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="menu-checkbox"
|
||||||
|
:class="{ 'menu-checkbox-checked': filters.polls }"
|
||||||
|
/>{{ $t('settings.notification_visibility_polls') }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:trigger>
|
<template v-slot:trigger>
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
v-for="notification in notificationsToDisplay"
|
v-for="notification in notificationsToDisplay"
|
||||||
:key="notification.id"
|
:key="notification.id"
|
||||||
class="notification"
|
class="notification"
|
||||||
:class="{"unseen": !minimalMode && !notification.seen}"
|
:class="{unseen: !minimalMode && !notification.seen}"
|
||||||
>
|
>
|
||||||
<div class="notification-overlay" />
|
<div class="notification-overlay" />
|
||||||
<notification :notification="notification" />
|
<notification :notification="notification" />
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import useVuelidate from '@vuelidate/core'
|
import useVuelidate from '@vuelidate/core'
|
||||||
import { required, requiredIf, sameAs } from '@vuelidate/validators'
|
import { required, requiredIf, sameAs } from '@vuelidate/validators'
|
||||||
import { mapActions, mapState } from 'vuex'
|
import { mapActions, mapState } from 'vuex'
|
||||||
|
import InterfaceLanguageSwitcher from '../interface_language_switcher/interface_language_switcher.vue'
|
||||||
|
import localeService from '../../services/locale/locale.service.js'
|
||||||
|
|
||||||
const registration = {
|
const registration = {
|
||||||
setup () { return { v$: useVuelidate() } },
|
setup () { return { v$: useVuelidate() } },
|
||||||
|
@ -11,10 +13,14 @@ const registration = {
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
confirm: '',
|
confirm: '',
|
||||||
reason: ''
|
reason: '',
|
||||||
|
language: ''
|
||||||
},
|
},
|
||||||
captcha: {}
|
captcha: {}
|
||||||
}),
|
}),
|
||||||
|
components: {
|
||||||
|
InterfaceLanguageSwitcher
|
||||||
|
},
|
||||||
validations () {
|
validations () {
|
||||||
return {
|
return {
|
||||||
user: {
|
user: {
|
||||||
|
@ -26,7 +32,8 @@ const registration = {
|
||||||
required,
|
required,
|
||||||
sameAs: sameAs(this.user.password)
|
sameAs: sameAs(this.user.password)
|
||||||
},
|
},
|
||||||
reason: { required: requiredIf(() => this.accountApprovalRequired) }
|
reason: { required: requiredIf(() => this.accountApprovalRequired) },
|
||||||
|
language: {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -64,6 +71,9 @@ const registration = {
|
||||||
this.user.captcha_solution = this.captcha.solution
|
this.user.captcha_solution = this.captcha.solution
|
||||||
this.user.captcha_token = this.captcha.token
|
this.user.captcha_token = this.captcha.token
|
||||||
this.user.captcha_answer_data = this.captcha.answer_data
|
this.user.captcha_answer_data = this.captcha.answer_data
|
||||||
|
if (this.user.language) {
|
||||||
|
this.user.language = localeService.internalToBackendLocale(this.user.language)
|
||||||
|
}
|
||||||
|
|
||||||
this.v$.$touch()
|
this.v$.$touch()
|
||||||
|
|
||||||
|
|
|
@ -162,6 +162,18 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="form-group"
|
||||||
|
:class="{ 'form-group--error': v$.user.language.$error }"
|
||||||
|
>
|
||||||
|
<interface-language-switcher
|
||||||
|
for="email-language"
|
||||||
|
:prompt-text="$t('registration.email_language')"
|
||||||
|
:language="v$.user.language.$model"
|
||||||
|
:set-language="val => v$.user.language.$model = val"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="accountApprovalRequired"
|
v-if="accountApprovalRequired"
|
||||||
class="form-group"
|
class="form-group"
|
||||||
|
|
|
@ -77,6 +77,12 @@ const GeneralTab = {
|
||||||
!this.$store.state.users.currentUser.background_image
|
!this.$store.state.users.currentUser.background_image
|
||||||
},
|
},
|
||||||
instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable },
|
instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable },
|
||||||
|
language: {
|
||||||
|
get: function () { return this.$store.getters.mergedConfig.interfaceLanguage },
|
||||||
|
set: function (val) {
|
||||||
|
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
|
||||||
|
}
|
||||||
|
},
|
||||||
...SharedComputedObject()
|
...SharedComputedObject()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -4,7 +4,11 @@
|
||||||
<h2>{{ $t('settings.interface') }}</h2>
|
<h2>{{ $t('settings.interface') }}</h2>
|
||||||
<ul class="setting-list">
|
<ul class="setting-list">
|
||||||
<li>
|
<li>
|
||||||
<interface-language-switcher />
|
<interface-language-switcher
|
||||||
|
:prompt-text="$t('settings.interfaceLanguage')"
|
||||||
|
:language="language"
|
||||||
|
:set-language="val => language = val"
|
||||||
|
/>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="instanceSpecificPanelPresent">
|
<li v-if="instanceSpecificPanelPresent">
|
||||||
<BooleanSetting path="hideISP">
|
<BooleanSetting path="hideISP">
|
||||||
|
|
|
@ -41,6 +41,11 @@
|
||||||
{{ $t('settings.notification_visibility_emoji_reactions') }}
|
{{ $t('settings.notification_visibility_emoji_reactions') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<BooleanSetting path="notificationVisibility.polls">
|
||||||
|
{{ $t('settings.notification_visibility_polls') }}
|
||||||
|
</BooleanSetting>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -8,8 +8,10 @@ import EmojiInput from 'src/components/emoji_input/emoji_input.vue'
|
||||||
import suggestor from 'src/components/emoji_input/suggestor.js'
|
import suggestor from 'src/components/emoji_input/suggestor.js'
|
||||||
import Autosuggest from 'src/components/autosuggest/autosuggest.vue'
|
import Autosuggest from 'src/components/autosuggest/autosuggest.vue'
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
|
import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
|
||||||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||||
|
import localeService from 'src/services/locale/locale.service.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
|
@ -40,7 +42,8 @@ const ProfileTab = {
|
||||||
banner: null,
|
banner: null,
|
||||||
bannerPreview: null,
|
bannerPreview: null,
|
||||||
background: null,
|
background: null,
|
||||||
backgroundPreview: null
|
backgroundPreview: null,
|
||||||
|
emailLanguage: this.$store.state.users.currentUser.language || ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
@ -50,7 +53,8 @@ const ProfileTab = {
|
||||||
Autosuggest,
|
Autosuggest,
|
||||||
ProgressButton,
|
ProgressButton,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
BooleanSetting
|
BooleanSetting,
|
||||||
|
InterfaceLanguageSwitcher
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
user () {
|
user () {
|
||||||
|
@ -111,19 +115,25 @@ const ProfileTab = {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateProfile () {
|
updateProfile () {
|
||||||
|
const params = {
|
||||||
|
note: this.newBio,
|
||||||
|
locked: this.newLocked,
|
||||||
|
// Backend notation.
|
||||||
|
/* eslint-disable camelcase */
|
||||||
|
display_name: this.newName,
|
||||||
|
fields_attributes: this.newFields.filter(el => el != null),
|
||||||
|
bot: this.bot,
|
||||||
|
show_role: this.showRole
|
||||||
|
/* eslint-enable camelcase */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.emailLanguage) {
|
||||||
|
params.language = localeService.internalToBackendLocale(this.emailLanguage)
|
||||||
|
}
|
||||||
|
|
||||||
this.$store.state.api.backendInteractor
|
this.$store.state.api.backendInteractor
|
||||||
.updateProfile({
|
.updateProfile({ params })
|
||||||
params: {
|
.then((user) => {
|
||||||
note: this.newBio,
|
|
||||||
locked: this.newLocked,
|
|
||||||
// Backend notation.
|
|
||||||
/* eslint-disable camelcase */
|
|
||||||
display_name: this.newName,
|
|
||||||
fields_attributes: this.newFields.filter(el => el != null),
|
|
||||||
bot: this.bot,
|
|
||||||
show_role: this.showRole
|
|
||||||
/* eslint-enable camelcase */
|
|
||||||
} }).then((user) => {
|
|
||||||
this.newFields.splice(user.fields.length)
|
this.newFields.splice(user.fields.length)
|
||||||
merge(this.newFields, user.fields)
|
merge(this.newFields, user.fields)
|
||||||
this.$store.commit('addNewUsers', [user])
|
this.$store.commit('addNewUsers', [user])
|
||||||
|
@ -193,8 +203,8 @@ const ProfileTab = {
|
||||||
submitAvatar (cropper, file) {
|
submitAvatar (cropper, file) {
|
||||||
const that = this
|
const that = this
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
function updateAvatar (avatar) {
|
function updateAvatar (avatar, avatarName) {
|
||||||
that.$store.state.api.backendInteractor.updateProfileImages({ avatar })
|
that.$store.state.api.backendInteractor.updateProfileImages({ avatar, avatarName })
|
||||||
.then((user) => {
|
.then((user) => {
|
||||||
that.$store.commit('addNewUsers', [user])
|
that.$store.commit('addNewUsers', [user])
|
||||||
that.$store.commit('setCurrentUser', user)
|
that.$store.commit('setCurrentUser', user)
|
||||||
|
@ -207,9 +217,9 @@ const ProfileTab = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cropper) {
|
if (cropper) {
|
||||||
cropper.getCroppedCanvas().toBlob(updateAvatar, file.type)
|
cropper.getCroppedCanvas().toBlob((data) => updateAvatar(data, file.name), file.type)
|
||||||
} else {
|
} else {
|
||||||
updateAvatar(file)
|
updateAvatar(file, file.name)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -89,6 +89,13 @@
|
||||||
{{ $t('settings.bot') }}
|
{{ $t('settings.bot') }}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<interface-language-switcher
|
||||||
|
:prompt-text="$t('settings.email_language')"
|
||||||
|
:language="emailLanguage"
|
||||||
|
:set-language="val => emailLanguage = val"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
<button
|
<button
|
||||||
:disabled="newName && newName.length === 0"
|
:disabled="newName && newName.length === 0"
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 1.4;
|
height: 1.4em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,8 +77,9 @@ const Timeline = {
|
||||||
statusesToDisplay () {
|
statusesToDisplay () {
|
||||||
const amount = this.timeline.visibleStatuses.length
|
const amount = this.timeline.visibleStatuses.length
|
||||||
const statusesPerSide = Math.ceil(Math.max(3, window.innerHeight / 80))
|
const statusesPerSide = Math.ceil(Math.max(3, window.innerHeight / 80))
|
||||||
const min = Math.max(0, this.virtualScrollIndex - statusesPerSide)
|
const nonPinnedIndex = this.virtualScrollIndex - this.filteredPinnedStatusIds.length
|
||||||
const max = Math.min(amount, this.virtualScrollIndex + statusesPerSide)
|
const min = Math.max(0, nonPinnedIndex - statusesPerSide)
|
||||||
|
const max = Math.min(amount, nonPinnedIndex + statusesPerSide)
|
||||||
return this.timeline.visibleStatuses.slice(min, max).map(_ => _.id)
|
return this.timeline.visibleStatuses.slice(min, max).map(_ => _.id)
|
||||||
},
|
},
|
||||||
virtualScrollingEnabled () {
|
virtualScrollingEnabled () {
|
||||||
|
|
|
@ -93,7 +93,6 @@
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
||||||
.TimelineQuickSettings {
|
.TimelineQuickSettings {
|
||||||
align-self: stretch;
|
|
||||||
|
|
||||||
> button {
|
> button {
|
||||||
line-height: 100%;
|
line-height: 100%;
|
||||||
|
|
|
@ -43,6 +43,10 @@
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
width: 24rem;
|
width: 24rem;
|
||||||
|
|
||||||
|
.popover-trigger-button {
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
.timeline-menu-popover-wrap {
|
.timeline-menu-popover-wrap {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
// Match panel heading padding to line up menu with bottom of heading
|
// Match panel heading padding to line up menu with bottom of heading
|
||||||
|
|
|
@ -160,7 +160,8 @@
|
||||||
"repeated_you": "repeated your status",
|
"repeated_you": "repeated your status",
|
||||||
"no_more_notifications": "No more notifications",
|
"no_more_notifications": "No more notifications",
|
||||||
"migrated_to": "migrated to",
|
"migrated_to": "migrated to",
|
||||||
"reacted_with": "reacted with {0}"
|
"reacted_with": "reacted with {0}",
|
||||||
|
"poll_ended": "poll has ended"
|
||||||
},
|
},
|
||||||
"polls": {
|
"polls": {
|
||||||
"add_poll": "Add poll",
|
"add_poll": "Add poll",
|
||||||
|
@ -254,7 +255,8 @@
|
||||||
"password_required": "cannot be left blank",
|
"password_required": "cannot be left blank",
|
||||||
"password_confirmation_required": "cannot be left blank",
|
"password_confirmation_required": "cannot be left blank",
|
||||||
"password_confirmation_match": "should be the same as password"
|
"password_confirmation_match": "should be the same as password"
|
||||||
}
|
},
|
||||||
|
"email_language": "In which language do you want to receive emails from the server?"
|
||||||
},
|
},
|
||||||
"remote_user_resolver": {
|
"remote_user_resolver": {
|
||||||
"remote_user_resolver": "Remote user resolver",
|
"remote_user_resolver": "Remote user resolver",
|
||||||
|
@ -303,6 +305,7 @@
|
||||||
"avatarRadius": "Avatars",
|
"avatarRadius": "Avatars",
|
||||||
"background": "Background",
|
"background": "Background",
|
||||||
"bio": "Bio",
|
"bio": "Bio",
|
||||||
|
"email_language": "Language for receiving emails from the server",
|
||||||
"block_export": "Block export",
|
"block_export": "Block export",
|
||||||
"block_export_button": "Export your blocks to a csv file",
|
"block_export_button": "Export your blocks to a csv file",
|
||||||
"block_import": "Block import",
|
"block_import": "Block import",
|
||||||
|
@ -427,6 +430,7 @@
|
||||||
"notification_visibility_repeats": "Repeats",
|
"notification_visibility_repeats": "Repeats",
|
||||||
"notification_visibility_moves": "User Migrates",
|
"notification_visibility_moves": "User Migrates",
|
||||||
"notification_visibility_emoji_reactions": "Reactions",
|
"notification_visibility_emoji_reactions": "Reactions",
|
||||||
|
"notification_visibility_polls": "Ends of polls you voted in",
|
||||||
"no_rich_text_description": "Strip rich text formatting from all posts",
|
"no_rich_text_description": "Strip rich text formatting from all posts",
|
||||||
"no_blocks": "No blocks",
|
"no_blocks": "No blocks",
|
||||||
"no_mutes": "No mutes",
|
"no_mutes": "No mutes",
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
|
import Cookies from 'js-cookie'
|
||||||
import { setPreset, applyTheme } from '../services/style_setter/style_setter.js'
|
import { setPreset, applyTheme } from '../services/style_setter/style_setter.js'
|
||||||
import messages from '../i18n/messages'
|
import messages from '../i18n/messages'
|
||||||
|
import localeService from '../services/locale/locale.service.js'
|
||||||
|
|
||||||
|
const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage'
|
||||||
|
|
||||||
const browserLocale = (window.navigator.language || 'en').split('-')[0]
|
const browserLocale = (window.navigator.language || 'en').split('-')[0]
|
||||||
|
|
||||||
|
@ -55,7 +59,8 @@ export const defaultState = {
|
||||||
moves: true,
|
moves: true,
|
||||||
emojiReactions: true,
|
emojiReactions: true,
|
||||||
followRequest: true,
|
followRequest: true,
|
||||||
chatMention: true
|
chatMention: true,
|
||||||
|
polls: true
|
||||||
},
|
},
|
||||||
webPushNotifications: false,
|
webPushNotifications: false,
|
||||||
muteWords: [],
|
muteWords: [],
|
||||||
|
@ -165,6 +170,7 @@ const config = {
|
||||||
break
|
break
|
||||||
case 'interfaceLanguage':
|
case 'interfaceLanguage':
|
||||||
messages.setLanguage(this.getters.i18n, value)
|
messages.setLanguage(this.getters.i18n, value)
|
||||||
|
Cookies.set(BACKEND_LANGUAGE_COOKIE_NAME, localeService.internalToBackendLocale(value))
|
||||||
break
|
break
|
||||||
case 'thirdColumnMode':
|
case 'thirdColumnMode':
|
||||||
dispatch('setLayoutWidth', undefined)
|
dispatch('setLayoutWidth', undefined)
|
||||||
|
|
|
@ -55,7 +55,10 @@ export const settingsMap = {
|
||||||
get: 'pleroma.allow_following_move',
|
get: 'pleroma.allow_following_move',
|
||||||
set: 'allow_following_move'
|
set: 'allow_following_move'
|
||||||
},
|
},
|
||||||
'discoverable': 'source.discoverable',
|
'discoverable': {
|
||||||
|
get: 'source.pleroma.discoverable',
|
||||||
|
set: 'discoverable'
|
||||||
|
},
|
||||||
'hideFavorites': {
|
'hideFavorites': {
|
||||||
get: 'pleroma.hide_favorites',
|
get: 'pleroma.hide_favorites',
|
||||||
set: 'hide_favorites'
|
set: 'hide_favorites'
|
||||||
|
|
|
@ -151,9 +151,15 @@ const updateNotificationSettings = ({ credentials, settings }) => {
|
||||||
}).then((data) => data.json())
|
}).then((data) => data.json())
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateProfileImages = ({ credentials, avatar = null, banner = null, background = null }) => {
|
const updateProfileImages = ({ credentials, avatar = null, avatarName = null, banner = null, background = null }) => {
|
||||||
const form = new FormData()
|
const form = new FormData()
|
||||||
if (avatar !== null) form.append('avatar', avatar)
|
if (avatar !== null) {
|
||||||
|
if (avatarName !== null) {
|
||||||
|
form.append('avatar', avatar, avatarName)
|
||||||
|
} else {
|
||||||
|
form.append('avatar', avatar)
|
||||||
|
}
|
||||||
|
}
|
||||||
if (banner !== null) form.append('header', banner)
|
if (banner !== null) form.append('header', banner)
|
||||||
if (background !== null) form.append('pleroma_background_image', background)
|
if (background !== null) form.append('pleroma_background_image', background)
|
||||||
return fetch(MASTODON_PROFILE_UPDATE_URL, {
|
return fetch(MASTODON_PROFILE_UPDATE_URL, {
|
||||||
|
@ -191,6 +197,7 @@ const updateProfile = ({ credentials, params }) => {
|
||||||
// homepage
|
// homepage
|
||||||
// location
|
// location
|
||||||
// token
|
// token
|
||||||
|
// language
|
||||||
const register = ({ params, credentials }) => {
|
const register = ({ params, credentials }) => {
|
||||||
const { nickname, ...rest } = params
|
const { nickname, ...rest } = params
|
||||||
return fetch(MASTODON_REGISTRATION_URL, {
|
return fetch(MASTODON_REGISTRATION_URL, {
|
||||||
|
|
|
@ -1,12 +1,35 @@
|
||||||
|
import languagesObject from '../../i18n/messages'
|
||||||
|
import ISO6391 from 'iso-639-1'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
const specialLanguageCodes = {
|
const specialLanguageCodes = {
|
||||||
'ja_easy': 'ja',
|
'ja_easy': 'ja',
|
||||||
'zh_Hant': 'zh-HANT'
|
'zh_Hant': 'zh-HANT',
|
||||||
|
'zh': 'zh-Hans'
|
||||||
}
|
}
|
||||||
|
|
||||||
const internalToBrowserLocale = code => specialLanguageCodes[code] || code
|
const internalToBrowserLocale = code => specialLanguageCodes[code] || code
|
||||||
|
|
||||||
|
const internalToBackendLocale = code => internalToBrowserLocale(code).replace('_', '-')
|
||||||
|
|
||||||
|
const getLanguageName = (code) => {
|
||||||
|
const specialLanguageNames = {
|
||||||
|
'ja_easy': 'やさしいにほんご',
|
||||||
|
'zh': '简体中文',
|
||||||
|
'zh_Hant': '繁體中文'
|
||||||
|
}
|
||||||
|
const languageName = specialLanguageNames[code] || ISO6391.getNativeName(code)
|
||||||
|
const browserLocale = internalToBrowserLocale(code)
|
||||||
|
return languageName.charAt(0).toLocaleUpperCase(browserLocale) + languageName.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const languages = _.map(languagesObject.languages, (code) => ({ code: code, name: getLanguageName(code) })).sort((a, b) => a.name.localeCompare(b.name))
|
||||||
|
|
||||||
const localeService = {
|
const localeService = {
|
||||||
internalToBrowserLocale
|
internalToBrowserLocale,
|
||||||
|
internalToBackendLocale,
|
||||||
|
languages,
|
||||||
|
getLanguageName
|
||||||
}
|
}
|
||||||
|
|
||||||
export default localeService
|
export default localeService
|
||||||
|
|
|
@ -14,11 +14,12 @@ export const visibleTypes = store => {
|
||||||
rootState.config.notificationVisibility.follows && 'follow',
|
rootState.config.notificationVisibility.follows && 'follow',
|
||||||
rootState.config.notificationVisibility.followRequest && 'follow_request',
|
rootState.config.notificationVisibility.followRequest && 'follow_request',
|
||||||
rootState.config.notificationVisibility.moves && 'move',
|
rootState.config.notificationVisibility.moves && 'move',
|
||||||
rootState.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reaction'
|
rootState.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reaction',
|
||||||
|
rootState.config.notificationVisibility.polls && 'poll'
|
||||||
].filter(_ => _))
|
].filter(_ => _))
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusNotifications = ['like', 'mention', 'repeat', 'pleroma:emoji_reaction']
|
const statusNotifications = ['like', 'mention', 'repeat', 'pleroma:emoji_reaction', 'poll']
|
||||||
|
|
||||||
export const isStatusNotification = (type) => includes(statusNotifications, type)
|
export const isStatusNotification = (type) => includes(statusNotifications, type)
|
||||||
|
|
||||||
|
@ -98,6 +99,9 @@ export const prepareNotificationObject = (notification, i18n) => {
|
||||||
case 'follow_request':
|
case 'follow_request':
|
||||||
i18nString = 'follow_request'
|
i18nString = 'follow_request'
|
||||||
break
|
break
|
||||||
|
case 'poll':
|
||||||
|
i18nString = 'poll_ended'
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notification.type === 'pleroma:emoji_reaction') {
|
if (notification.type === 'pleroma:emoji_reaction') {
|
||||||
|
|
39
yarn.lock
39
yarn.lock
|
@ -1559,10 +1559,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.0.0-rc.17.tgz#e6dcf5b5bd3ae23595bdb154b9b578ebcdffd698"
|
resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.0.0-rc.17.tgz#e6dcf5b5bd3ae23595bdb154b9b578ebcdffd698"
|
||||||
integrity sha512-7LHZKsFRV/HqDoMVY+cJamFzgHgsrmQFalROHC5FMWrzPzd+utG5e11krj1tVsnxYufGA2ABShX4nlcHXED+zQ==
|
integrity sha512-7LHZKsFRV/HqDoMVY+cJamFzgHgsrmQFalROHC5FMWrzPzd+utG5e11krj1tVsnxYufGA2ABShX4nlcHXED+zQ==
|
||||||
|
|
||||||
"@vuelidate/core@2.0.0-alpha.35":
|
"@vuelidate/core@2.0.0-alpha.41":
|
||||||
version "2.0.0-alpha.35"
|
version "2.0.0-alpha.41"
|
||||||
resolved "https://registry.yarnpkg.com/@vuelidate/core/-/core-2.0.0-alpha.35.tgz#22d91787147b0883d31585fab44d0218622b7560"
|
resolved "https://registry.yarnpkg.com/@vuelidate/core/-/core-2.0.0-alpha.41.tgz#eb4644aa45755c20901b4b8d20e1fecfd5389142"
|
||||||
integrity sha512-BSGQElu5lyI0GzqehFzUWy7GXhEUC7Z8oEpdxgCyGGN5gOFlAQ5Zr4dDFzfIOhU4jik3CfPHK+i+Juqg2OCKNw==
|
integrity sha512-fST7s5wiLW8ZNTexe8+7fDdBZYT7HjbuA43/XDtKTlHs1BMRDDaBoFLZbHSqmHisQvGXa7zLG9bvG8X5cHZaxg==
|
||||||
dependencies:
|
dependencies:
|
||||||
vue-demi "^0.12.0"
|
vue-demi "^0.12.0"
|
||||||
|
|
||||||
|
@ -5722,6 +5722,11 @@ js-base64@^2.1.9:
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.0.tgz#42255ba183ab67ce59a0dee640afdc00ab5ae93e"
|
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.0.tgz#42255ba183ab67ce59a0dee640afdc00ab5ae93e"
|
||||||
|
|
||||||
|
js-cookie@^3.0.1:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.1.tgz#9e39b4c6c2f56563708d7d31f6f5f21873a92414"
|
||||||
|
integrity sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==
|
||||||
|
|
||||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^3.0.2:
|
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^3.0.2:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
|
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
|
||||||
|
@ -6223,6 +6228,11 @@ lodash.isequal@^4.2.0:
|
||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||||
|
|
||||||
|
lodash.isfunction@^3.0.8:
|
||||||
|
version "3.0.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051"
|
||||||
|
integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==
|
||||||
|
|
||||||
lodash.isplainobject@^3.0.0, lodash.isplainobject@^3.2.0:
|
lodash.isplainobject@^3.0.0, lodash.isplainobject@^3.2.0:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-3.2.0.tgz#9a8238ae16b200432960cd7346512d0123fbf4c5"
|
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-3.2.0.tgz#9a8238ae16b200432960cd7346512d0123fbf4c5"
|
||||||
|
@ -8671,20 +8681,20 @@ selenium-server@2.53.1:
|
||||||
version "2.53.1"
|
version "2.53.1"
|
||||||
resolved "https://registry.yarnpkg.com/selenium-server/-/selenium-server-2.53.1.tgz#d681528812f3c2e0531a6b7e613e23bb02cce8a6"
|
resolved "https://registry.yarnpkg.com/selenium-server/-/selenium-server-2.53.1.tgz#d681528812f3c2e0531a6b7e613e23bb02cce8a6"
|
||||||
|
|
||||||
"semver@2 || 3 || 4 || 5", semver@5.6.0, semver@^5.3.0, semver@^5.5.0, semver@^5.6.0:
|
"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.6.0:
|
||||||
version "5.6.0"
|
version "5.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
|
||||||
|
|
||||||
|
semver@5.7.1, semver@^5.4.1:
|
||||||
|
version "5.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||||
|
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||||
|
|
||||||
semver@7.0.0:
|
semver@7.0.0:
|
||||||
version "7.0.0"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
|
||||||
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
|
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
|
||||||
|
|
||||||
semver@^5.4.1:
|
|
||||||
version "5.7.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
|
||||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
|
||||||
|
|
||||||
semver@^5.5.1:
|
semver@^5.5.1:
|
||||||
version "5.7.0"
|
version "5.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
|
||||||
|
@ -10093,13 +10103,14 @@ webpack-log@^2.0.0:
|
||||||
ansi-colors "^3.0.0"
|
ansi-colors "^3.0.0"
|
||||||
uuid "^3.3.2"
|
uuid "^3.3.2"
|
||||||
|
|
||||||
webpack-merge@0.14.1:
|
webpack-merge@0.20.0:
|
||||||
version "0.14.1"
|
version "0.20.0"
|
||||||
resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-0.14.1.tgz#d6bfe6d9360a024e1e7f8e6383ae735f1737cd23"
|
resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-0.20.0.tgz#e4b73429517181a287c59c8cafef5fc9eb1d9705"
|
||||||
integrity sha1-1r/m2TYKAk4ef45jg65zXxc3zSM=
|
integrity sha1-5Lc0KVFxgaKHxZyMr+9fyesdlwU=
|
||||||
dependencies:
|
dependencies:
|
||||||
lodash.find "^3.2.1"
|
lodash.find "^3.2.1"
|
||||||
lodash.isequal "^4.2.0"
|
lodash.isequal "^4.2.0"
|
||||||
|
lodash.isfunction "^3.0.8"
|
||||||
lodash.isplainobject "^3.2.0"
|
lodash.isplainobject "^3.2.0"
|
||||||
lodash.merge "^3.3.2"
|
lodash.merge "^3.3.2"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue