diff options
Diffstat (limited to 'app/assets/javascripts/issues/show')
4 files changed, 78 insertions, 63 deletions
diff --git a/app/assets/javascripts/issues/show/components/app.vue b/app/assets/javascripts/issues/show/components/app.vue index 15f97222971..bc32a15a420 100644 --- a/app/assets/javascripts/issues/show/components/app.vue +++ b/app/assets/javascripts/issues/show/components/app.vue @@ -14,6 +14,7 @@ import Poll from '~/lib/utils/poll'; import { visitUrl } from '~/lib/utils/url_utility'; import { __, sprintf } from '~/locale'; import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue'; +import { containsSensitiveToken, confirmSensitiveAction, i18n } from '~/lib/utils/secret_detection'; import { ISSUE_TYPE_PATH, INCIDENT_TYPE_PATH, POLLING_DELAY } from '../constants'; import eventHub from '../event_hub'; import getIssueStateQuery from '../queries/get_issue_state.query.graphql'; @@ -95,10 +96,10 @@ export default { required: false, default: '', }, - initialTaskStatus: { - type: String, + initialTaskCompletionStatus: { + type: Object, required: false, - default: '', + default: () => ({}), }, updatedAt: { type: String, @@ -197,7 +198,7 @@ export default { updatedAt: this.updatedAt, updatedByName: this.updatedByName, updatedByPath: this.updatedByPath, - taskStatus: this.initialTaskStatus, + taskCompletionStatus: this.initialTaskCompletionStatus, lock_version: this.lockVersion, }); @@ -222,9 +223,6 @@ export default { formState() { return this.store.formState; }, - hasUpdated() { - return Boolean(this.state.updatedAt); - }, issueChanged() { const { store: { @@ -379,7 +377,7 @@ export default { this.showForm = false; }, - updateIssuable() { + async updateIssuable() { this.setFormState({ updateLoading: true }); const { @@ -392,6 +390,14 @@ export default { this.alert?.dismiss(); + if (containsSensitiveToken(issuablePayload.description)) { + const confirmed = await confirmSensitiveAction(i18n.descriptionPrompt); + if (!confirmed) { + this.setFormState({ updateLoading: false }); + return false; + } + } + return this.service .updateIssuable(issuablePayload) .then((res) => res.data) @@ -557,7 +563,6 @@ export default { :description-html="state.descriptionHtml" :description-text="state.descriptionText" :updated-at="state.updatedAt" - :task-status="state.taskStatus" :issuable-type="issuableType" :update-url="updateEndpoint" :lock-version="state.lock_version" @@ -570,7 +575,7 @@ export default { /> <edited-component - v-if="hasUpdated" + :task-completion-status="state.taskCompletionStatus" :updated-at="state.updatedAt" :updated-by-name="state.updatedByName" :updated-by-path="state.updatedByPath" diff --git a/app/assets/javascripts/issues/show/components/description.vue b/app/assets/javascripts/issues/show/components/description.vue index bdee6c5fe9a..3721f224d5e 100644 --- a/app/assets/javascripts/issues/show/components/description.vue +++ b/app/assets/javascripts/issues/show/components/description.vue @@ -1,6 +1,5 @@ <script> import { GlToast } from '@gitlab/ui'; -import $ from 'jquery'; import Sortable from 'sortablejs'; import Vue from 'vue'; import getIssueDetailsQuery from 'ee_else_ce/work_items/graphql/get_issue_details.query.graphql'; @@ -59,11 +58,6 @@ export default { required: false, default: '', }, - taskStatus: { - type: String, - required: false, - default: '', - }, issuableType: { type: String, required: false, @@ -138,7 +132,10 @@ export default { }, watch: { descriptionHtml(newDescription, oldDescription) { - if (!this.initialUpdate && newDescription !== oldDescription) { + if ( + !this.initialUpdate && + this.stripClientState(newDescription) !== this.stripClientState(oldDescription) + ) { this.animateChange(); } else { this.initialUpdate = false; @@ -148,16 +145,12 @@ export default { this.renderGFM(); }); }, - taskStatus() { - this.updateTaskStatusText(); - }, }, mounted() { eventHub.$on('convert-task-list-item', this.convertTaskListItem); eventHub.$on('delete-task-list-item', this.deleteTaskListItem); this.renderGFM(); - this.updateTaskStatusText(); }, beforeDestroy() { eventHub.$off('convert-task-list-item', this.convertTaskListItem); @@ -282,24 +275,6 @@ export default { this.$emit('taskListUpdateFailed'); }, - updateTaskStatusText() { - const taskRegexMatches = this.taskStatus.match(/(\d+) of ((?!0)\d+)/); - const $issuableHeader = $('.issuable-meta'); - const $tasks = $('#task_status', $issuableHeader); - const $tasksShort = $('#task_status_short', $issuableHeader); - - if (taskRegexMatches) { - $tasks.text(this.taskStatus); - $tasksShort.text( - `${taskRegexMatches[1]}/${taskRegexMatches[2]} checklist item${ - taskRegexMatches[2] > 1 ? 's' : '' - }`, - ); - } else { - $tasks.text(''); - $tasksShort.text(''); - } - }, createTaskListItemActions(provide) { const app = new Vue({ el: document.createElement('div'), @@ -349,6 +324,9 @@ export default { listItem.append(element); } }, + stripClientState(description) { + return description.replaceAll('<details open="true">', '<details>'); + }, async createTask({ taskTitle, taskDescription, oldDescription }) { try { const { title, description } = extractTaskTitleAndDescription(taskTitle, taskDescription); diff --git a/app/assets/javascripts/issues/show/components/edited.vue b/app/assets/javascripts/issues/show/components/edited.vue index 5138a4530e9..6a0edb59b65 100644 --- a/app/assets/javascripts/issues/show/components/edited.vue +++ b/app/assets/javascripts/issues/show/components/edited.vue @@ -1,13 +1,20 @@ <script> -import { GlSprintf } from '@gitlab/ui'; +import { GlLink, GlSprintf } from '@gitlab/ui'; +import { n__, sprintf } from '~/locale'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; export default { components: { TimeAgoTooltip, + GlLink, GlSprintf, }, props: { + taskCompletionStatus: { + type: Object, + required: false, + default: () => ({}), + }, updatedAt: { type: String, required: false, @@ -25,36 +32,61 @@ export default { }, }, computed: { + completedCount() { + return this.taskCompletionStatus.completed_count; + }, + count() { + return this.taskCompletionStatus.count; + }, hasUpdatedBy() { return this.updatedByName && this.updatedByPath; }, + showCheck() { + return this.completedCount === this.count; + }, + taskStatus() { + const { completedCount, count } = this; + if (!count) { + return undefined; + } + + return sprintf( + n__( + '%{completedCount} of %{count} checklist item completed', + '%{completedCount} of %{count} checklist items completed', + count, + ), + { completedCount, count }, + ); + }, }, }; </script> <template> <small class="edited-text js-issue-widgets"> - <gl-sprintf v-if="!hasUpdatedBy" :message="__('Edited %{timeago}')"> - <template #timeago> - <time-ago-tooltip :time="updatedAt" tooltip-placement="bottom" /> - </template> - </gl-sprintf> - <gl-sprintf v-else-if="!updatedAt" :message="__('Edited by %{author}')"> - <template #author> - <a :href="updatedByPath" class="author-link gl-hover-text-decoration-underline"> - <span>{{ updatedByName }}</span> - </a> - </template> - </gl-sprintf> - <gl-sprintf v-else :message="__('Edited %{timeago} by %{author}')"> - <template #timeago> - <time-ago-tooltip :time="updatedAt" tooltip-placement="bottom" /> - </template> - <template #author> - <a :href="updatedByPath" class="author-link gl-hover-text-decoration-underline"> - <span>{{ updatedByName }}</span> - </a> - </template> - </gl-sprintf> + <template v-if="taskStatus"> + <template v-if="showCheck">✓</template> + {{ taskStatus }} + <template v-if="updatedAt">·</template> + </template> + + <template v-if="updatedAt"> + <gl-sprintf v-if="!hasUpdatedBy" :message="__('Edited %{timeago}')"> + <template #timeago> + <time-ago-tooltip :time="updatedAt" tooltip-placement="bottom" /> + </template> + </gl-sprintf> + <gl-sprintf v-else :message="__('Edited %{timeago} by %{author}')"> + <template #timeago> + <time-ago-tooltip :time="updatedAt" tooltip-placement="bottom" /> + </template> + <template #author> + <gl-link :href="updatedByPath" class="gl-font-sm gl-hover-text-gray-900 gl-text-gray-700"> + {{ updatedByName }} + </gl-link> + </template> + </gl-sprintf> + </template> </small> </template> diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue index 243666b2323..8267c0130a3 100644 --- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue +++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue @@ -199,7 +199,7 @@ export default { <p class="gl-ml-3 gl-align-self-end gl-line-height-32">{{ __('UTC') }}</p> </div> </div> - <gl-form-group v-if="glFeatures.incidentEventTags"> + <gl-form-group> <label class="gl-display-flex gl-align-items-center gl-gap-3" for="timeline-input-tags"> {{ $options.i18n.tagsLabel }} <timeline-events-tags-popover /> |