1<script> 2import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; 3import $ from 'jquery'; 4import { isEmpty } from 'lodash'; 5import { scrollToElement } from '~/lib/utils/common_utils'; 6import { slugify } from '~/lib/utils/text_utility'; 7import { getLocationHash } from '~/lib/utils/url_utility'; 8import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; 9import '~/behaviors/markdown/render_gfm'; 10import EvidenceBlock from './evidence_block.vue'; 11import ReleaseBlockAssets from './release_block_assets.vue'; 12import ReleaseBlockFooter from './release_block_footer.vue'; 13import ReleaseBlockHeader from './release_block_header.vue'; 14import ReleaseBlockMilestoneInfo from './release_block_milestone_info.vue'; 15 16export default { 17 name: 'ReleaseBlock', 18 components: { 19 EvidenceBlock, 20 ReleaseBlockAssets, 21 ReleaseBlockFooter, 22 ReleaseBlockHeader, 23 ReleaseBlockMilestoneInfo, 24 }, 25 directives: { 26 SafeHtml, 27 }, 28 mixins: [glFeatureFlagsMixin()], 29 props: { 30 release: { 31 type: Object, 32 required: true, 33 default: () => ({}), 34 }, 35 }, 36 data() { 37 return { 38 isHighlighted: false, 39 }; 40 }, 41 computed: { 42 htmlId() { 43 if (!this.release.tagName) { 44 return null; 45 } 46 47 return slugify(this.release.tagName); 48 }, 49 assets() { 50 return this.release.assets || {}; 51 }, 52 hasEvidence() { 53 return Boolean(this.release.evidences && this.release.evidences.length); 54 }, 55 milestones() { 56 return this.release.milestones || []; 57 }, 58 shouldRenderAssets() { 59 return Boolean( 60 this.assets.links.length || (this.assets.sources && this.assets.sources.length), 61 ); 62 }, 63 shouldRenderMilestoneInfo() { 64 return Boolean(!isEmpty(this.release.milestones)); 65 }, 66 }, 67 68 mounted() { 69 this.renderGFM(); 70 71 const hash = getLocationHash(); 72 if (hash && slugify(hash) === this.htmlId) { 73 this.isHighlighted = true; 74 setTimeout(() => { 75 this.isHighlighted = false; 76 }, 2000); 77 78 scrollToElement(this.$el); 79 } 80 }, 81 methods: { 82 renderGFM() { 83 $(this.$refs['gfm-content']).renderGFM(); 84 }, 85 }, 86 safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] }, 87}; 88</script> 89<template> 90 <div :id="htmlId" :class="{ 'bg-line-target-blue': isHighlighted }" class="card release-block"> 91 <release-block-header :release="release" /> 92 <div class="card-body"> 93 <div v-if="shouldRenderMilestoneInfo"> 94 <!-- TODO: Switch open* links to opened* once fields have been updated in GraphQL --> 95 <release-block-milestone-info 96 :milestones="milestones" 97 :opened-issues-path="release._links.openedIssuesUrl" 98 :closed-issues-path="release._links.closedIssuesUrl" 99 :opened-merge-requests-path="release._links.openedMergeRequestsUrl" 100 :merged-merge-requests-path="release._links.mergedMergeRequestsUrl" 101 :closed-merge-requests-path="release._links.closedMergeRequestsUrl" 102 /> 103 <hr class="mb-3 mt-0" /> 104 </div> 105 106 <release-block-assets v-if="shouldRenderAssets" :assets="assets" /> 107 <evidence-block v-if="hasEvidence" :release="release" /> 108 109 <div ref="gfm-content" class="card-text gl-mt-3"> 110 <div v-safe-html:[$options.safeHtmlConfig]="release.descriptionHtml" class="md"></div> 111 </div> 112 </div> 113 114 <release-block-footer 115 class="card-footer" 116 :commit="release.commit" 117 :commit-path="release.commitPath" 118 :tag-name="release.tagName" 119 :tag-path="release.tagPath" 120 :author="release.author" 121 :released-at="release.releasedAt" 122 /> 123 </div> 124</template> 125