diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-11 03:10:11 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-11 03:10:11 +0300 |
commit | 7303dddd09bf28d28e36666f0cc2775e9fa27872 (patch) | |
tree | c97552993de10857894e26c7f0c6b412496a063b /app/assets | |
parent | 997968e3fa7d1f623f7bd9e2581c58dc7afc5aa5 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets')
-rw-r--r-- | app/assets/javascripts/groups/components/app.vue | 9 | ||||
-rw-r--r-- | app/assets/javascripts/groups/components/group_item.vue | 10 | ||||
-rw-r--r-- | app/assets/javascripts/groups/groups_list.js | 18 | ||||
-rw-r--r-- | app/assets/javascripts/groups/index.js | 61 | ||||
-rw-r--r-- | app/assets/javascripts/pages/explore/groups/index.js | 2 | ||||
-rw-r--r-- | app/assets/javascripts/profile/edit/components/profile_edit_app.vue | 83 | ||||
-rw-r--r-- | app/assets/javascripts/profile/edit/constants.js | 7 | ||||
-rw-r--r-- | app/assets/javascripts/profile/edit/index.js | 16 |
8 files changed, 116 insertions, 90 deletions
diff --git a/app/assets/javascripts/groups/components/app.vue b/app/assets/javascripts/groups/components/app.vue index c6fe16b13b5..3440bd87e6b 100644 --- a/app/assets/javascripts/groups/components/app.vue +++ b/app/assets/javascripts/groups/components/app.vue @@ -28,11 +28,6 @@ export default { required: false, default: '', }, - containerId: { - type: String, - required: false, - default: '', - }, store: { type: Object, required: true, @@ -94,10 +89,6 @@ export default { }, mounted() { this.fetchAllGroups(); - - if (this.containerId) { - this.containerEl = document.getElementById(this.containerId); - } }, beforeDestroy() { eventHub.$off(`${this.action}fetchPage`, this.fetchPage); diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue index befb96034f5..af1af86d0c4 100644 --- a/app/assets/javascripts/groups/components/group_item.vue +++ b/app/assets/javascripts/groups/components/group_item.vue @@ -21,7 +21,7 @@ import { VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE, } from '~/visibility_level/constants'; -import { ITEM_TYPE } from '../constants'; +import { ITEM_TYPE, ACTIVE_TAB_SHARED } from '../constants'; import eventHub from '../event_hub'; @@ -50,7 +50,11 @@ export default { ItemActions, ItemStats, }, - inject: ['currentGroupVisibility'], + inject: { + currentGroupVisibility: { + default: '', + }, + }, props: { parentGroup: { type: Object, @@ -114,7 +118,7 @@ export default { }, shouldShowVisibilityWarning() { return ( - this.action === 'shared' && + this.action === ACTIVE_TAB_SHARED && VISIBILITY_LEVELS_STRING_TO_INTEGER[this.group.visibility] > VISIBILITY_LEVELS_STRING_TO_INTEGER[this.currentGroupVisibility] ); diff --git a/app/assets/javascripts/groups/groups_list.js b/app/assets/javascripts/groups/groups_list.js deleted file mode 100644 index 866dd7a61ff..00000000000 --- a/app/assets/javascripts/groups/groups_list.js +++ /dev/null @@ -1,18 +0,0 @@ -import FilterableList from '~/filterable_list'; - -/** - * Makes search request for groups when user types a value in the search input. - * Updates the html content of the page with the received one. - */ -export default class GroupsList { - constructor() { - const form = document.querySelector('form#group-filter-form'); - const filter = document.querySelector('.js-groups-list-filter'); - const holder = document.querySelector('.js-groups-list-holder'); - - if (form && filter && holder) { - const list = new FilterableList(form, filter, holder); - list.initSearch(); - } - } -} diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js index df2a23dc0f7..e71ff6d9107 100644 --- a/app/assets/javascripts/groups/index.js +++ b/app/assets/javascripts/groups/index.js @@ -7,92 +7,49 @@ import Translate from '../vue_shared/translate'; import GroupsApp from './components/app.vue'; import GroupFolderComponent from './components/group_folder.vue'; -import { GROUPS_LIST_HOLDER_CLASS, CONTENT_LIST_CLASS } from './constants'; import GroupFilterableList from './groups_filterable_list'; import GroupsService from './service/groups_service'; import GroupsStore from './store/groups_store'; Vue.use(Translate); -export default (containerId = 'js-groups-tree', endpoint, action = '') => { - const containerEl = document.getElementById(containerId); - let dataEl; +export default () => { + const el = document.getElementById('js-groups-tree'); // eslint-disable-next-line no-new new UserCallout(); - // Don't do anything if element doesn't exist (No groups) - // This is for when the user enters directly to the page via URL - if (!containerEl) { + if (!el) { return; } - const el = action ? containerEl.querySelector(GROUPS_LIST_HOLDER_CLASS) : containerEl; - - if (action) { - dataEl = containerEl.querySelector(CONTENT_LIST_CLASS); - } - Vue.component('GroupFolder', GroupFolderComponent); Vue.component('GroupItem', GroupItemComponent); Vue.use(GlToast); + const { dataset } = el; + // eslint-disable-next-line no-new new Vue({ el, components: { GroupsApp, }, - provide() { - const { - dataset: { - newSubgroupPath, - newProjectPath, - newSubgroupIllustration, - newProjectIllustration, - emptyProjectsIllustration, - emptySubgroupIllustration, - canCreateSubgroups, - canCreateProjects, - currentGroupVisibility, - }, - } = this.$options.el; - - return { - newSubgroupPath, - newProjectPath, - newSubgroupIllustration, - newProjectIllustration, - emptyProjectsIllustration, - emptySubgroupIllustration, - canCreateSubgroups: parseBoolean(canCreateSubgroups), - canCreateProjects: parseBoolean(canCreateProjects), - currentGroupVisibility, - }; - }, data() { - const { dataset } = dataEl || this.$options.el; const showSchemaMarkup = parseBoolean(dataset.showSchemaMarkup); const renderEmptyState = parseBoolean(dataset.renderEmptyState); - const service = new GroupsService(endpoint || dataset.endpoint); + const service = new GroupsService(dataset.endpoint); const store = new GroupsStore({ hideProjects: true, showSchemaMarkup }); return { - action, store, service, renderEmptyState, loading: true, - containerId, }; }, beforeMount() { - if (this.action) { - return; - } - - const { dataset } = dataEl || this.$options.el; let groupFilterList = null; const form = document.querySelector(dataset.formSel); const filter = document.querySelector(dataset.filterSel); @@ -102,11 +59,11 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => { form, filter, holder, - filterEndpoint: endpoint || dataset.endpoint, + filterEndpoint: dataset.endpoint, pagePath: dataset.path, dropdownSel: dataset.dropdownSel, filterInputField: 'filter', - action: this.action, + action: '', }; groupFilterList = new GroupFilterableList(opts); @@ -115,11 +72,9 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => { render(createElement) { return createElement('groups-app', { props: { - action: this.action, store: this.store, service: this.service, renderEmptyState: this.renderEmptyState, - containerId: this.containerId, }, }); }, diff --git a/app/assets/javascripts/pages/explore/groups/index.js b/app/assets/javascripts/pages/explore/groups/index.js index 05078191e5c..9b60b1f51a8 100644 --- a/app/assets/javascripts/pages/explore/groups/index.js +++ b/app/assets/javascripts/pages/explore/groups/index.js @@ -1,9 +1,7 @@ import initGroupsList from '~/groups'; -import GroupsList from '~/groups/groups_list'; import Landing from '~/groups/landing'; function exploreGroups() { - new GroupsList(); // eslint-disable-line no-new initGroupsList(); const landingElement = document.querySelector('.js-explore-groups-landing'); if (!landingElement) return; diff --git a/app/assets/javascripts/profile/edit/components/profile_edit_app.vue b/app/assets/javascripts/profile/edit/components/profile_edit_app.vue index df7f9a5be81..6b39f137880 100644 --- a/app/assets/javascripts/profile/edit/components/profile_edit_app.vue +++ b/app/assets/javascripts/profile/edit/components/profile_edit_app.vue @@ -4,8 +4,12 @@ import { GlForm, GlButton } from '@gitlab/ui'; import { VARIANT_DANGER, VARIANT_INFO, createAlert } from '~/alert'; import axios from '~/lib/utils/axios_utils'; import { readFileAsDataURL } from '~/lib/utils/file_utility'; +import SetStatusForm from '~/set_status_modal/set_status_form.vue'; +import SettingsBlock from '~/packages_and_registries/shared/components/settings_block.vue'; +import { isUserBusy, computedClearStatusAfterValue } from '~/set_status_modal/utils'; +import { AVAILABILITY_STATUS } from '~/set_status_modal/constants'; -import { i18n } from '../constants'; +import { i18n, statusI18n } from '../constants'; import UserAvatar from './user_avatar.vue'; export default { @@ -13,7 +17,16 @@ export default { UserAvatar, GlForm, GlButton, + SettingsBlock, + SetStatusForm, }, + inject: [ + 'currentEmoji', + 'currentMessage', + 'currentAvailability', + 'defaultEmoji', + 'currentClearStatusAfter', + ], props: { profilePath: { type: String, @@ -28,14 +41,44 @@ export default { return { uploadingProfile: false, avatarBlob: null, + status: { + emoji: this.currentEmoji, + message: this.currentMessage, + availability: isUserBusy(this.currentAvailability), + clearStatusAfter: null, + }, }; }, + computed: { + shouldIncludeClearStatusAfterInApiRequest() { + return this.status.clearStatusAfter !== null; + }, + clearStatusAfterApiRequestValue() { + return computedClearStatusAfterValue(this.status.clearStatusAfter); + }, + }, methods: { async onSubmit() { // TODO: Do validation before organizing data. this.uploadingProfile = true; const formData = new FormData(); + // Setting up status data + const statusFieldNameBase = 'user[status]'; + formData.append(`${statusFieldNameBase}[emoji]`, this.status.emoji); + formData.append(`${statusFieldNameBase}[message]`, this.status.message); + formData.append( + `${statusFieldNameBase}[availability]`, + this.status.availability ? AVAILABILITY_STATUS.BUSY : AVAILABILITY_STATUS.NOT_SET, + ); + + if (this.shouldIncludeClearStatusAfterInApiRequest) { + formData.append( + `${statusFieldNameBase}[clear_status_after]`, + this.clearStatusAfterApiRequestValue, + ); + } + if (this.avatarBlob) { formData.append('user[avatar]', this.avatarBlob, 'avatar.png'); } @@ -46,7 +89,6 @@ export default { if (this.avatarBlob) { this.syncHeaderAvatars(); } - createAlert({ message: data.message, variant: data.status === 'error' ? VARIANT_DANGER : VARIANT_INFO, @@ -79,14 +121,47 @@ export default { onBlobChange(blob) { this.avatarBlob = blob; }, + onMessageInput(value) { + this.status.message = value; + }, + onEmojiClick(emoji) { + this.status.emoji = emoji; + }, + onClearStatusAfterClick(after) { + this.status.clearStatusAfter = after; + }, + onAvailabilityInput(value) { + this.status.availability = value; + }, + }, + i18n: { + ...i18n, + ...statusI18n, }, - i18n, }; </script> <template> - <gl-form @submit.prevent="onSubmit"> + <gl-form class="edit-user" @submit.prevent="onSubmit"> <user-avatar @blob-change="onBlobChange" /> + <settings-block class="js-search-settings-section"> + <template #title>{{ $options.i18n.setStatusTitle }}</template> + <template #description>{{ $options.i18n.setStatusDescription }}</template> + <div class="gl-max-w-80"> + <set-status-form + :default-emoji="defaultEmoji" + :emoji="status.emoji" + :message="status.message" + :availability="status.availability" + :clear-status-after="status.clearStatusAfter" + :current-clear-status-after="currentClearStatusAfter" + @message-input="onMessageInput" + @emoji-click="onEmojiClick" + @clear-status-after-click="onClearStatusAfterClick" + @availability-input="onAvailabilityInput" + /> + </div> + </settings-block> <!-- TODO: to implement profile editing form fields --> <!-- It will be implemented in the upcoming MRs --> <!-- Related issue: https://gitlab.com/gitlab-org/gitlab/-/issues/389918 --> diff --git a/app/assets/javascripts/profile/edit/constants.js b/app/assets/javascripts/profile/edit/constants.js index ecd6274e39b..e07615273f7 100644 --- a/app/assets/javascripts/profile/edit/constants.js +++ b/app/assets/javascripts/profile/edit/constants.js @@ -21,6 +21,13 @@ export const avatarI18n = { cropAvatarSetAsNewAvatar: s__('Profiles|Set new profile picture'), }; +export const statusI18n = { + setStatusTitle: s__('Profiles|Current status'), + setStatusDescription: s__( + 'Profiles|This emoji and message will appear on your profile and throughout the interface.', + ), +}; + export const i18n = { updateProfileSettings: s__('Profiles|Update profile settings'), cancel: __('Cancel'), diff --git a/app/assets/javascripts/profile/edit/index.js b/app/assets/javascripts/profile/edit/index.js index 1466eede864..27b410c3a12 100644 --- a/app/assets/javascripts/profile/edit/index.js +++ b/app/assets/javascripts/profile/edit/index.js @@ -7,13 +7,27 @@ export const initProfileEdit = () => { if (!mountEl) return false; - const { profilePath, userPath, ...provides } = mountEl.dataset; + const { + profilePath, + userPath, + currentEmoji, + currentMessage, + currentAvailability, + defaultEmoji, + currentClearStatusAfter, + ...provides + } = mountEl.dataset; return new Vue({ el: mountEl, name: 'ProfileEditRoot', provide: { ...provides, + currentEmoji, + currentMessage, + currentAvailability, + defaultEmoji, + currentClearStatusAfter, hasAvatar: parseBoolean(provides.hasAvatar), gravatarEnabled: parseBoolean(provides.gravatarEnabled), gravatarLink: JSON.parse(provides.gravatarLink), |