diff options
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/account_actions/account_actions.vue | 1 | ||||
| -rw-r--r-- | src/components/emoji_input/emoji_input.js | 1 | ||||
| -rw-r--r-- | src/components/emoji_input/suggestor.js | 6 | ||||
| -rw-r--r-- | src/components/extra_buttons/extra_buttons.vue | 1 | ||||
| -rw-r--r-- | src/components/poll/poll.vue | 2 | ||||
| -rw-r--r-- | src/components/popover/popover.js | 11 | ||||
| -rw-r--r-- | src/components/settings_modal/tabs/profile_tab.js | 29 | ||||
| -rw-r--r-- | src/components/settings_modal/tabs/profile_tab.scss | 17 | ||||
| -rw-r--r-- | src/components/settings_modal/tabs/profile_tab.vue | 53 | ||||
| -rw-r--r-- | src/components/status/status.vue | 2 | ||||
| -rw-r--r-- | src/components/status_content/status_content.vue | 24 | ||||
| -rw-r--r-- | src/components/user_card/user_card.vue | 20 | ||||
| -rw-r--r-- | src/components/user_profile/user_profile.js | 8 | ||||
| -rw-r--r-- | src/components/user_profile/user_profile.vue | 74 |
14 files changed, 225 insertions, 24 deletions
diff --git a/src/components/account_actions/account_actions.vue b/src/components/account_actions/account_actions.vue index 744b77d5..029e7096 100644 --- a/src/components/account_actions/account_actions.vue +++ b/src/components/account_actions/account_actions.vue @@ -3,6 +3,7 @@ <Popover trigger="click" placement="bottom" + :bound-to="{ x: 'container' }" > <div slot="content" diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js index f4c3479c..7974a66d 100644 --- 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/emoji_input/suggestor.js b/src/components/emoji_input/suggestor.js index 15a71eff..8330345b 100644 --- a/src/components/emoji_input/suggestor.js +++ b/src/components/emoji_input/suggestor.js @@ -13,7 +13,7 @@ import { debounce } from 'lodash' const debounceUserSearch = debounce((data, input) => { data.updateUsersList(input) -}, 500, { leading: true, trailing: false }) +}, 500) export default data => input => { const firstChar = input[0] @@ -97,8 +97,8 @@ export const suggestUsers = data => input => { replacement: '@' + screen_name + ' ' })) - // BE search users if there are no matches - if (newUsers.length === 0 && data.updateUsersList) { + // BE search users to get more comprehensive results + if (data.updateUsersList) { debounceUserSearch(data, noPrefix) } return newUsers diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue index bca93ea7..68db6fd8 100644 --- a/src/components/extra_buttons/extra_buttons.vue +++ b/src/components/extra_buttons/extra_buttons.vue @@ -3,6 +3,7 @@ trigger="click" placement="top" class="extra-button-popover" + :bound-to="{ x: 'container' }" > <div slot="content" diff --git a/src/components/poll/poll.vue b/src/components/poll/poll.vue index 56e91cca..adbb0555 100644 --- a/src/components/poll/poll.vue +++ b/src/components/poll/poll.vue @@ -17,7 +17,7 @@ <span class="result-percentage"> {{ percentageForOption(option.votes_count) }}% </span> - <span>{{ option.title }}</span> + <span v-html="option.title_html"></span> </div> <div class="result-fill" diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js index 5881d266..a40a9195 100644 --- a/src/components/popover/popover.js +++ b/src/components/popover/popover.js @@ -1,4 +1,3 @@ - const Popover = { name: 'Popover', props: { @@ -10,6 +9,9 @@ const Popover = { // 'container' for using offsetParent as boundaries for either axis // or 'viewport' boundTo: Object, + // Takes a selector to use as a replacement for the parent container + // for getting boundaries for x an y axis + boundToSelector: String, // Takes a top/bottom/left/right object, how much space to leave // between boundary and popover element margin: Object, @@ -27,6 +29,10 @@ const Popover = { } }, methods: { + containerBoundingClientRect () { + const container = this.boundToSelector ? this.$el.closest(this.boundToSelector) : this.$el.offsetParent + return container.getBoundingClientRect() + }, updateStyles () { if (this.hidden) { this.styles = { @@ -45,7 +51,8 @@ const Popover = { // Minor optimization, don't call a slow reflow call if we don't have to const parentBounds = this.boundTo && (this.boundTo.x === 'container' || this.boundTo.y === 'container') && - this.$el.offsetParent.getBoundingClientRect() + this.containerBoundingClientRect() + const margin = this.margin || {} // What are the screen bounds for the popover? Viewport vs container diff --git a/src/components/settings_modal/tabs/profile_tab.js b/src/components/settings_modal/tabs/profile_tab.js index 8658b097..e6db802d 100644 --- 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, @@ -23,6 +25,7 @@ const ProfileTab = { showRole: this.$store.state.users.currentUser.show_role, role: this.$store.state.users.currentUser.role, discoverable: this.$store.state.users.currentUser.discoverable, + bot: this.$store.state.users.currentUser.bot, allowFollowingMove: this.$store.state.users.currentUser.allow_following_move, pickAvatarBtnVisible: true, bannerUploading: false, @@ -62,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: { @@ -74,17 +89,21 @@ 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, hide_followers: this.hideFollowers, discoverable: this.discoverable, + bot: this.bot, allow_following_move: this.allowFollowingMove, hide_follows_count: this.hideFollowsCount, hide_followers_count: this.hideFollowersCount, 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) }) @@ -92,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 index 4aab81eb..b3dcf42c 100644 --- 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 index fff4f970..0f9210a6 100644 --- a/src/components/settings_modal/tabs/profile_tab.vue +++ b/src/components/settings_modal/tabs/profile_tab.vue @@ -95,6 +95,59 @@ {{ $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') }} + </Checkbox> + </p> <button :disabled="newName && newName.length === 0" class="btn btn-default" diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 336f912a..7ec29b28 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -418,7 +418,7 @@ $status-margin: 0.75em; max-width: 85%; font-weight: bold; - img { + img.emoji { width: 14px; height: 14px; vertical-align: middle; diff --git a/src/components/status_content/status_content.vue b/src/components/status_content/status_content.vue index 7adb67ae..efc2485e 100644 --- a/src/components/status_content/status_content.vue +++ b/src/components/status_content/status_content.vue @@ -164,23 +164,23 @@ $status-margin: 0.75em; word-break: break-all; } + img, video { + max-width: 100%; + max-height: 400px; + vertical-align: middle; + object-fit: contain; + + &.emoji { + width: 32px; + height: 32px; + } + } + .status-content { font-family: var(--postFont, sans-serif); line-height: 1.4em; white-space: pre-wrap; - img, video { - max-width: 100%; - max-height: 400px; - vertical-align: middle; - object-fit: contain; - - &.emoji { - width: 32px; - height: 32px; - } - } - blockquote { margin: 0.2em 0 0.2em 2em; font-style: italic; diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue index c4a5ce9d..9529d7f6 100644 --- a/src/components/user_card/user_card.vue +++ b/src/components/user_card/user_card.vue @@ -70,10 +70,20 @@ > @{{ user.screen_name }} </router-link> - <span - v-if="!hideBio && !!visibleRole" - class="alert staff" - >{{ visibleRole }}</span> + <template v-if="!hideBio"> + <span + v-if="!!visibleRole" + class="alert user-role" + > + {{ visibleRole }} + </span> + <span + v-if="user.bot" + class="alert user-role" + > + bot + </span> + </template> <span v-if="user.locked"><i class="icon icon-lock" /></span> <span v-if="!mergedConfig.hideUserStats && !hideBio" @@ -458,7 +468,7 @@ color: var(--text, $fallback--text); } - .staff { + .user-role { flex: none; text-transform: capitalize; color: $fallback--text; diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index 95760bf8..201727d4 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -124,6 +124,14 @@ const UserProfile = { onTabSwitch (tab) { this.tab = tab this.$router.replace({ query: { tab } }) + }, + linkClicked ({ target }) { + if (target.tagName === 'SPAN') { + target = target.parentNode + } + if (target.tagName === 'A') { + window.open(target.href, '_blank') + } } }, watch: { diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index 1871d46c..361a3b5c 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -11,6 +11,31 @@ :allow-zooming-avatar="true" rounded="top" /> + <div + v-if="user.fields_html && user.fields_html.length > 0" + class="user-profile-fields" + > + <dl + v-for="(field, index) in user.fields_html" + :key="index" + class="user-profile-field" + > + <!-- eslint-disable vue/no-v-html --> + <dt + :title="user.fields_text[index].name" + class="user-profile-field-name" + @click.prevent="linkClicked" + v-html="field.name" + /> + <dd + :title="user.fields_text[index].value" + class="user-profile-field-value" + @click.prevent="linkClicked" + v-html="field.value" + /> + <!-- eslint-enable vue/no-v-html --> + </dl> + </div> <tab-switcher :active-tab="tab" :render-only-focused="true" @@ -108,11 +133,60 @@ <script src="./user_profile.js"></script> <style lang="scss"> +@import '../../_variables.scss'; .user-profile { flex: 2; flex-basis: 500px; + .user-profile-fields { + margin: 0 0.5em; + img { + object-fit: contain; + vertical-align: middle; + max-width: 100%; + max-height: 400px; + + &.emoji { + width: 18px; + height: 18px; + } + } + + .user-profile-field { + display: flex; + margin: 0.25em auto; + max-width: 32em; + border: 1px solid var(--border, $fallback--border); + border-radius: $fallback--inputRadius; + border-radius: var(--inputRadius, $fallback--inputRadius); + + .user-profile-field-name { + flex: 0 1 30%; + font-weight: 500; + text-align: right; + color: var(--lightText); + min-width: 120px; + border-right: 1px solid var(--border, $fallback--border); + } + + .user-profile-field-value { + flex: 1 1 70%; + color: var(--text); + margin: 0 0 0 0.25em; + } + + .user-profile-field-name, .user-profile-field-value { + line-height: 18px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + padding: 0.5em 1.5em; + box-sizing: border-box; + } + } + } + .userlist-placeholder { display: flex; justify-content: center; |
