commit: c658f57abbf1409a18a54c6259aa44d429caf080
parent: 8150899a737648cb8c8852fa380c3ff4a98b132f
Author: Shpuld Shpludson <shp@cock.li>
Date: Sat, 27 Jun 2020 07:32:59 +0000
Merge branch 'iss-149/profile-fields-setting' into 'develop'
Profile fields setting
See merge request pleroma/pleroma-fe!997
Diffstat:
8 files changed, 147 insertions(+), 3 deletions(-)
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
@@ -207,6 +207,8 @@ const getNodeInfo = async ({ store }) => {
store.dispatch('setInstanceOption', { name: 'pollLimits', value: metadata.pollLimits })
store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled })
+ store.dispatch('setInstanceOption', { name: 'fieldsLimits', value: metadata.fieldsLimits })
+
store.dispatch('setInstanceOption', { name: 'restrictedNicknames', value: metadata.restrictedNicknames })
store.dispatch('setInstanceOption', { name: 'postFormats', value: metadata.postFormats })
diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js
@@ -431,6 +431,7 @@ const EmojiInput = {
const offsetBottom = offsetTop + offsetHeight
panel.style.top = offsetBottom + 'px'
+ if (!picker) return
picker.$el.style.top = offsetBottom + 'px'
picker.$el.style.bottom = 'auto'
}
diff --git a/src/components/settings_modal/tabs/profile_tab.js b/src/components/settings_modal/tabs/profile_tab.js
@@ -1,4 +1,5 @@
import unescape from 'lodash/unescape'
+import merge from 'lodash/merge'
import ImageCropper from 'src/components/image_cropper/image_cropper.vue'
import ScopeSelector from 'src/components/scope_selector/scope_selector.vue'
import fileSizeFormatService from 'src/components/../services/file_size_format/file_size_format.js'
@@ -16,6 +17,7 @@ const ProfileTab = {
newLocked: this.$store.state.users.currentUser.locked,
newNoRichText: this.$store.state.users.currentUser.no_rich_text,
newDefaultScope: this.$store.state.users.currentUser.default_scope,
+ newFields: this.$store.state.users.currentUser.fields.map(field => ({ name: field.name, value: field.value })),
hideFollows: this.$store.state.users.currentUser.hide_follows,
hideFollowers: this.$store.state.users.currentUser.hide_followers,
hideFollowsCount: this.$store.state.users.currentUser.hide_follows_count,
@@ -63,6 +65,18 @@ const ProfileTab = {
...this.$store.state.instance.emoji,
...this.$store.state.instance.customEmoji
] })
+ },
+ userSuggestor () {
+ return suggestor({
+ users: this.$store.state.users.users,
+ updateUsersList: (query) => this.$store.dispatch('searchUsers', { query })
+ })
+ },
+ fieldsLimits () {
+ return this.$store.state.instance.fieldsLimits
+ },
+ maxFields () {
+ return this.fieldsLimits ? this.fieldsLimits.maxFields : 0
}
},
methods: {
@@ -75,6 +89,7 @@ const ProfileTab = {
// Backend notation.
/* eslint-disable camelcase */
display_name: this.newName,
+ fields_attributes: this.newFields.filter(el => el != null),
default_scope: this.newDefaultScope,
no_rich_text: this.newNoRichText,
hide_follows: this.hideFollows,
@@ -87,6 +102,8 @@ const ProfileTab = {
show_role: this.showRole
/* eslint-enable camelcase */
} }).then((user) => {
+ this.newFields.splice(user.fields.length)
+ merge(this.newFields, user.fields)
this.$store.commit('addNewUsers', [user])
this.$store.commit('setCurrentUser', user)
})
@@ -94,6 +111,16 @@ const ProfileTab = {
changeVis (visibility) {
this.newDefaultScope = visibility
},
+ addField () {
+ if (this.newFields.length < this.maxFields) {
+ this.newFields.push({ name: '', value: '' })
+ return true
+ }
+ return false
+ },
+ deleteField (index, event) {
+ this.$delete(this.newFields, index)
+ },
uploadFile (slot, e) {
const file = e.target.files[0]
if (!file) { return }
diff --git a/src/components/settings_modal/tabs/profile_tab.scss b/src/components/settings_modal/tabs/profile_tab.scss
@@ -79,4 +79,21 @@
.setting-subitem {
margin-left: 1.75em;
}
+
+ .profile-fields {
+ display: flex;
+
+ &>.emoji-input {
+ flex: 1 1 auto;
+ margin: 0 .2em .5em;
+ }
+
+ &>.icon-container {
+ width: 20px;
+
+ &>.icon-cancel {
+ vertical-align: sub;
+ }
+ }
+ }
}
diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue
@@ -95,6 +95,54 @@
{{ $t('settings.discoverable') }}
</Checkbox>
</p>
+ <div v-if="maxFields > 0">
+ <p>{{ $t('settings.profile_fields.label') }}</p>
+ <div
+ v-for="(_, i) in newFields"
+ :key="i"
+ class="profile-fields"
+ >
+ <EmojiInput
+ v-model="newFields[i].name"
+ enable-emoji-picker
+ hide-emoji-button
+ :suggest="userSuggestor"
+ >
+ <input
+ v-model="newFields[i].name"
+ :placeholder="$t('settings.profile_fields.name')"
+ >
+ </EmojiInput>
+ <EmojiInput
+ v-model="newFields[i].value"
+ enable-emoji-picker
+ hide-emoji-button
+ :suggest="userSuggestor"
+ >
+ <input
+ v-model="newFields[i].value"
+ :placeholder="$t('settings.profile_fields.value')"
+ >
+ </EmojiInput>
+ <div
+ class="icon-container"
+ >
+ <i
+ v-show="newFields.length > 1"
+ class="icon-cancel"
+ @click="deleteField(i)"
+ />
+ </div>
+ </div>
+ <a
+ v-if="newFields.length < maxFields"
+ class="add-field faint"
+ @click="addField"
+ >
+ <i class="icon-plus" />
+ {{ $t("settings.profile_fields.add_field") }}
+ </a>
+ </div>
<p>
<Checkbox v-model="bot">
{{ $t('settings.bot') }}
diff --git a/src/i18n/en.json b/src/i18n/en.json
@@ -334,6 +334,12 @@
"loop_video_silent_only": "Loop only videos without sound (i.e. Mastodon's \"gifs\")",
"mutes_tab": "Mutes",
"play_videos_in_modal": "Play videos in a popup frame",
+ "profile_fields": {
+ "label": "Profile metadata",
+ "add_field": "Add Field",
+ "name": "Label",
+ "value": "Content"
+ },
"use_contain_fit": "Don't crop the attachment in thumbnails",
"name": "Name",
"name_bio": "Name & Bio",
diff --git a/src/modules/users.js b/src/modules/users.js
@@ -1,6 +1,6 @@
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
import oauthApi from '../services/new_api/oauth.js'
-import { compact, map, each, merge, last, concat, uniq } from 'lodash'
+import { compact, map, each, mergeWith, last, concat, uniq, isArray } from 'lodash'
import { set } from 'vue'
import { registerPushNotifications, unregisterPushNotifications } from '../services/push/push.js'
@@ -10,7 +10,7 @@ export const mergeOrAdd = (arr, obj, item) => {
const oldItem = obj[item.id]
if (oldItem) {
// We already have this, so only merge the new info.
- merge(oldItem, item)
+ mergeWith(oldItem, item, mergeArrayLength)
return { item: oldItem, new: false }
} else {
// This is a new item, prepare it
@@ -23,6 +23,13 @@ export const mergeOrAdd = (arr, obj, item) => {
}
}
+const mergeArrayLength = (oldValue, newValue) => {
+ if (isArray(oldValue) && isArray(newValue)) {
+ oldValue.length = newValue.length
+ return mergeWith(oldValue, newValue, mergeArrayLength)
+ }
+}
+
const getNotificationPermission = () => {
const Notification = window.Notification
@@ -116,7 +123,7 @@ export const mutations = {
},
setCurrentUser (state, user) {
state.lastLoginName = user.screen_name
- state.currentUser = merge(state.currentUser || {}, user)
+ state.currentUser = mergeWith(state.currentUser || {}, user, mergeArrayLength)
},
clearCurrentUser (state) {
state.currentUser = false
diff --git a/test/unit/specs/modules/users.spec.js b/test/unit/specs/modules/users.spec.js
@@ -18,6 +18,42 @@ describe('The users module', () => {
expect(state.users).to.eql([user])
expect(state.users[0].name).to.eql('Dude')
})
+
+ it('merging array field in new information for old users', () => {
+ const state = cloneDeep(defaultState)
+ const user = {
+ id: '1',
+ fields: [
+ { name: 'Label 1', value: 'Content 1' }
+ ]
+ }
+ const firstModUser = {
+ id: '1',
+ fields: [
+ { name: 'Label 2', value: 'Content 2' },
+ { name: 'Label 3', value: 'Content 3' }
+ ]
+ }
+ const secondModUser = {
+ id: '1',
+ fields: [
+ { name: 'Label 4', value: 'Content 4' }
+ ]
+ }
+
+ mutations.addNewUsers(state, [user])
+ expect(state.users[0].fields).to.have.length(1)
+ expect(state.users[0].fields[0].name).to.eql('Label 1')
+
+ mutations.addNewUsers(state, [firstModUser])
+ expect(state.users[0].fields).to.have.length(2)
+ expect(state.users[0].fields[0].name).to.eql('Label 2')
+ expect(state.users[0].fields[1].name).to.eql('Label 3')
+
+ mutations.addNewUsers(state, [secondModUser])
+ expect(state.users[0].fields).to.have.length(1)
+ expect(state.users[0].fields[0].name).to.eql('Label 4')
+ })
})
describe('findUser', () => {