diff options
Diffstat (limited to 'app/assets/javascripts/design_management')
10 files changed, 120 insertions, 8 deletions
diff --git a/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue b/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue index 78ba586ce37..813f87452d8 100644 --- a/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue +++ b/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue @@ -4,13 +4,16 @@ import { ApolloMutation } from 'vue-apollo'; import createFlash from '~/flash'; import { s__ } from '~/locale'; import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue'; +import { updateGlobalTodoCount } from '~/vue_shared/components/sidebar/todo_toggle/utils'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import { ACTIVE_DISCUSSION_SOURCE_TYPES } from '../../constants'; import createNoteMutation from '../../graphql/mutations/create_note.mutation.graphql'; import toggleResolveDiscussionMutation from '../../graphql/mutations/toggle_resolve_discussion.mutation.graphql'; import activeDiscussionQuery from '../../graphql/queries/active_discussion.query.graphql'; +import getDesignQuery from '../../graphql/queries/get_design.query.graphql'; import allVersionsMixin from '../../mixins/all_versions'; import { hasErrors } from '../../utils/cache_update'; +import { extractDesign } from '../../utils/design_management_utils'; import { ADD_DISCUSSION_COMMENT_ERROR } from '../../utils/error_messages'; import DesignNote from './design_note.vue'; import DesignReplyForm from './design_reply_form.vue'; @@ -161,6 +164,19 @@ export default { }, toggleResolvedStatus() { this.isResolving = true; + + /** + * Get previous todo count + */ + const { defaultClient: client } = this.$apollo.provider.clients; + const sourceData = client.readQuery({ + query: getDesignQuery, + variables: this.designVariables, + }); + + const design = extractDesign(sourceData); + const prevTodoCount = design.currentUserTodos?.nodes?.length || 0; + this.$apollo .mutate({ mutation: toggleResolveDiscussionMutation, @@ -170,6 +186,10 @@ export default { if (data.errors?.length > 0) { this.$emit('resolve-discussion-error', data.errors[0]); } + const newTodoCount = + data?.discussionToggleResolve?.discussion?.noteable?.currentUserTodos?.nodes?.length || + 0; + updateGlobalTodoCount(newTodoCount - prevTodoCount); }) .catch((err) => { this.$emit('resolve-discussion-error', err); diff --git a/app/assets/javascripts/design_management/components/image.vue b/app/assets/javascripts/design_management/components/image.vue index e64ee4a5a34..8ab94cd2c4b 100644 --- a/app/assets/javascripts/design_management/components/image.vue +++ b/app/assets/javascripts/design_management/components/image.vue @@ -1,6 +1,8 @@ <script> import { GlIcon } from '@gitlab/ui'; import { throttle } from 'lodash'; +import { DESIGN_MARK_APP_START, DESIGN_MAIN_IMAGE_OUTPUT } from '~/performance/constants'; +import { performanceMarkAndMeasure } from '~/performance/utils'; export default { components: { @@ -39,7 +41,9 @@ export default { window.removeEventListener('resize', this.resizeThrottled, false); }, mounted() { - this.onImgLoad(); + if (!this.image) { + this.onImgLoad(); + } this.resizeThrottled = throttle(() => { // NOTE: if imageStyle is set, then baseImageSize @@ -53,6 +57,14 @@ export default { methods: { onImgLoad() { requestIdleCallback(this.setBaseImageSize, { timeout: 1000 }); + performanceMarkAndMeasure({ + measures: [ + { + name: DESIGN_MAIN_IMAGE_OUTPUT, + start: DESIGN_MARK_APP_START, + }, + ], + }); }, onImgError() { this.imageError = true; diff --git a/app/assets/javascripts/design_management/components/upload/design_version_dropdown.vue b/app/assets/javascripts/design_management/components/upload/design_version_dropdown.vue index 750f16bbe57..816d7ac7abf 100644 --- a/app/assets/javascripts/design_management/components/upload/design_version_dropdown.vue +++ b/app/assets/javascripts/design_management/components/upload/design_version_dropdown.vue @@ -1,6 +1,8 @@ <script> import { GlDropdown, GlDropdownItem, GlSprintf } from '@gitlab/ui'; +import defaultAvatarUrl from 'images/no_avatar.png'; import { __, sprintf } from '~/locale'; +import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; import allVersionsMixin from '../../mixins/all_versions'; import { findVersionId } from '../../utils/design_management_utils'; @@ -9,6 +11,7 @@ export default { GlDropdown, GlDropdownItem, GlSprintf, + TimeAgo, }, mixins: [allVersionsMixin], computed: { @@ -58,6 +61,9 @@ export default { } return __('Version %{versionNumber}'); }, + getAvatarUrl(version) { + return version?.author?.avatarUrl || defaultAvatarUrl; + }, }, }; </script> @@ -68,14 +74,28 @@ export default { v-for="(version, index) in allVersions" :key="version.id" :is-check-item="true" + :is-check-centered="true" :is-checked="findVersionId(version.id) === currentVersionId" + :avatar-url="getAvatarUrl(version)" @click="routeToVersion(version.id)" > - <gl-sprintf :message="versionText(version.id)"> - <template #versionNumber> - {{ allVersions.length - index }} - </template> - </gl-sprintf> + <strong> + <gl-sprintf :message="versionText(version.id)"> + <template #versionNumber> + {{ allVersions.length - index }} + </template> + </gl-sprintf> + </strong> + + <div v-if="version.author" class="gl-text-gray-600 gl-mt-1"> + <div>{{ version.author.name }}</div> + <time-ago + v-if="version.createdAt" + class="text-1" + :time="version.createdAt" + tooltip-placement="bottom" + /> + </div> </gl-dropdown-item> </gl-dropdown> </template> diff --git a/app/assets/javascripts/design_management/graphql.js b/app/assets/javascripts/design_management/graphql.js index 9a0547ee9db..fa57537f74e 100644 --- a/app/assets/javascripts/design_management/graphql.js +++ b/app/assets/javascripts/design_management/graphql.js @@ -1,10 +1,11 @@ -import { defaultDataIdFromObject } from 'apollo-cache-inmemory'; +import { defaultDataIdFromObject, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; import produce from 'immer'; import { uniqueId } from 'lodash'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; import axios from '~/lib/utils/axios_utils'; +import introspectionQueryResultData from './graphql/fragmentTypes.json'; import activeDiscussionQuery from './graphql/queries/active_discussion.query.graphql'; import getDesignQuery from './graphql/queries/get_design.query.graphql'; import typeDefs from './graphql/typedefs.graphql'; @@ -12,6 +13,10 @@ import { addPendingTodoToStore } from './utils/cache_update'; import { extractTodoIdFromDeletePath, createPendingTodo } from './utils/design_management_utils'; import { CREATE_DESIGN_TODO_EXISTS_ERROR } from './utils/error_messages'; +const fragmentMatcher = new IntrospectionFragmentMatcher({ + introspectionQueryResultData, +}); + Vue.use(VueApollo); const resolvers = { @@ -80,6 +85,7 @@ const defaultClient = createDefaultClient( } return defaultDataIdFromObject(object); }, + fragmentMatcher, }, typeDefs, assumeImmutableResults: true, diff --git a/app/assets/javascripts/design_management/graphql/fragmentTypes.json b/app/assets/javascripts/design_management/graphql/fragmentTypes.json new file mode 100644 index 00000000000..0953231ea4c --- /dev/null +++ b/app/assets/javascripts/design_management/graphql/fragmentTypes.json @@ -0,0 +1 @@ +{"__schema":{"types":[{"kind":"INTERFACE","name":"User","possibleTypes":[{"name":"UserCore"}]},{"kind":"UNION","name":"NoteableType","possibleTypes":[{"name":"Design"},{"name":"Issue"},{"name":"MergeRequest"}]}]}} diff --git a/app/assets/javascripts/design_management/graphql/fragments/design_todo_item.fragment.graphql b/app/assets/javascripts/design_management/graphql/fragments/design_todo_item.fragment.graphql new file mode 100644 index 00000000000..3fe20705ce2 --- /dev/null +++ b/app/assets/javascripts/design_management/graphql/fragments/design_todo_item.fragment.graphql @@ -0,0 +1,11 @@ +fragment DesignTodoItem on Design { + id + image + __typename + currentUserTodos(state: pending) { + nodes { + id + __typename + } + } +} diff --git a/app/assets/javascripts/design_management/graphql/mutations/create_image_diff_note.mutation.graphql b/app/assets/javascripts/design_management/graphql/mutations/create_image_diff_note.mutation.graphql index 0b8400ac040..41c3f56f477 100644 --- a/app/assets/javascripts/design_management/graphql/mutations/create_image_diff_note.mutation.graphql +++ b/app/assets/javascripts/design_management/graphql/mutations/create_image_diff_note.mutation.graphql @@ -1,4 +1,5 @@ #import "../fragments/design_note.fragment.graphql" +#import "../fragments/design_todo_item.fragment.graphql" mutation createImageDiffNote($input: CreateImageDiffNoteInput!) { createImageDiffNote(input: $input) { @@ -7,6 +8,11 @@ mutation createImageDiffNote($input: CreateImageDiffNoteInput!) { discussion { id replyId + noteable { + ... on Design { + ...DesignTodoItem + } + } notes { nodes { ...DesignNote diff --git a/app/assets/javascripts/design_management/graphql/mutations/toggle_resolve_discussion.mutation.graphql b/app/assets/javascripts/design_management/graphql/mutations/toggle_resolve_discussion.mutation.graphql index 1157fc05d5f..124f12ef018 100644 --- a/app/assets/javascripts/design_management/graphql/mutations/toggle_resolve_discussion.mutation.graphql +++ b/app/assets/javascripts/design_management/graphql/mutations/toggle_resolve_discussion.mutation.graphql @@ -1,11 +1,17 @@ #import "../fragments/design_note.fragment.graphql" #import "../fragments/discussion_resolved_status.fragment.graphql" +#import "../fragments/design_todo_item.fragment.graphql" mutation toggleResolveDiscussion($id: ID!, $resolve: Boolean!) { discussionToggleResolve(input: { id: $id, resolve: $resolve }) { discussion { id ...ResolvedStatus + noteable { + ... on Design { + ...DesignTodoItem + } + } notes { nodes { ...DesignNote diff --git a/app/assets/javascripts/design_management/index.js b/app/assets/javascripts/design_management/index.js index aa9f377ef16..11666587265 100644 --- a/app/assets/javascripts/design_management/index.js +++ b/app/assets/javascripts/design_management/index.js @@ -1,4 +1,6 @@ import Vue from 'vue'; +import { DESIGN_MARK_APP_START, DESIGN_MEASURE_BEFORE_APP } from '~/performance/constants'; +import { performanceMarkAndMeasure } from '~/performance/utils'; import App from './components/app.vue'; import apolloProvider from './graphql'; import activeDiscussionQuery from './graphql/queries/active_discussion.query.graphql'; @@ -28,6 +30,16 @@ export default () => { projectPath, issueIid, }, + mounted() { + performanceMarkAndMeasure({ + mark: DESIGN_MARK_APP_START, + measures: [ + { + name: DESIGN_MEASURE_BEFORE_APP, + }, + ], + }); + }, render(createElement) { return createElement(App); }, diff --git a/app/assets/javascripts/design_management/pages/design/index.vue b/app/assets/javascripts/design_management/pages/design/index.vue index 19bfa123487..48ee7068809 100644 --- a/app/assets/javascripts/design_management/pages/design/index.vue +++ b/app/assets/javascripts/design_management/pages/design/index.vue @@ -1,10 +1,12 @@ <script> import { GlLoadingIcon, GlAlert } from '@gitlab/ui'; +import { isNull } from 'lodash'; import Mousetrap from 'mousetrap'; import { ApolloMutation } from 'vue-apollo'; import { keysFor, ISSUE_CLOSE_DESIGN } from '~/behaviors/shortcuts/keybindings'; import createFlash from '~/flash'; import { fetchPolicies } from '~/lib/graphql'; +import { updateGlobalTodoCount } from '~/vue_shared/components/sidebar/todo_toggle/utils'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import DesignDestroyer from '../../components/design_destroyer.vue'; import DesignReplyForm from '../../components/design_notes/design_reply_form.vue'; @@ -93,6 +95,7 @@ export default { errorMessage: '', scale: DEFAULT_SCALE, resolvedDiscussionsExpanded: false, + prevCurrentUserTodos: null, }; }, apollo: { @@ -163,6 +166,13 @@ export default { resolvedDiscussions() { return this.discussions.filter((discussion) => discussion.resolved); }, + currentUserTodos() { + if (!this.design || !this.design.currentUserTodos) { + return null; + } + + return this.design.currentUserTodos?.nodes?.length; + }, }, watch: { resolvedDiscussions(val) { @@ -170,6 +180,9 @@ export default { this.resolvedDiscussionsExpanded = false; } }, + currentUserTodos(_, prevCurrentUserTodos) { + this.prevCurrentUserTodos = prevCurrentUserTodos; + }, }, mounted() { Mousetrap.bind(keysFor(ISSUE_CLOSE_DESIGN), this.closeDesign); @@ -272,9 +285,14 @@ export default { this.$refs.newDiscussionForm.focusInput(); } }, - closeCommentForm() { + closeCommentForm(data) { this.comment = ''; this.annotationCoordinates = null; + + if (data?.data && !isNull(this.prevCurrentUserTodos)) { + updateGlobalTodoCount(this.currentUserTodos - this.prevCurrentUserTodos); + this.prevCurrentUserTodos = this.currentUserTodos; + } }, closeDesign() { this.$router.push({ |