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