diff options
author | Lauris BH <lauris@nix.lv> | 2021-07-13 21:09:19 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-13 20:09:19 +0200 |
commit | d26551bd0c2780d4e58fd21e3856f36eed62779d (patch) | |
tree | 4b9a7a398bcfabb8152a8e484979e20add341039 /web_src/js | |
parent | 3dba75fb9700548f27dbe2f61dd148f392ae13a0 (diff) | |
download | gitea-d26551bd0c2780d4e58fd21e3856f36eed62779d.tar.gz gitea-d26551bd0c2780d4e58fd21e3856f36eed62779d.zip |
Load issue/PR context popup data only when needed (#15955)
* Load issue/PR context popup data only when needed
* Add SVG icon Vue component
* Remove unneeded check
Diffstat (limited to 'web_src/js')
-rw-r--r-- | web_src/js/components/ContextPopup.vue | 112 | ||||
-rw-r--r-- | web_src/js/features/contextpopup.js | 77 | ||||
-rw-r--r-- | web_src/js/svg.js | 18 |
3 files changed, 152 insertions, 55 deletions
diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue new file mode 100644 index 0000000000..df04f0d8d8 --- /dev/null +++ b/web_src/js/components/ContextPopup.vue @@ -0,0 +1,112 @@ +<template> + <div> + <div v-if="loading" class="ui active centered inline loader"/> + <div v-if="!loading && issue !== null"> + <p><small>{{ issue.repository.full_name }} on {{ createdAt }}</small></p> + <p><svg-icon :name="icon" :class="[color]" /> <strong>{{ issue.title }}</strong> #{{ issue.number }}</p> + <p>{{ body }}</p> + <div> + <div + v-for="label in labels" + :key="label.name" + class="ui label" + :style="{ color: label.textColor, backgroundColor: label.color }" + > + {{ label.name }} + </div> + </div> + </div> + </div> +</template> + +<script> +import {SvgIcon} from '../svg.js'; + +const {AppSubUrl} = window.config; + +export default { + name: 'ContextPopup', + + components: { + SvgIcon, + }, + + data: () => ({ + loading: false, + issue: null + }), + + computed: { + createdAt() { + return new Date(this.issue.created_at).toLocaleDateString(undefined, {year: 'numeric', month: 'short', day: 'numeric'}); + }, + + body() { + const body = this.issue.body.replace(/\n+/g, ' '); + if (body.length > 85) { + return `${body.substring(0, 85)}…`; + } + return body; + }, + + icon() { + if (this.issue.pull_request !== null) { + if (this.issue.state === 'open') { + return 'octicon-git-pull-request'; // Open PR + } else if (this.issue.pull_request.merged === true) { + return 'octicon-git-merge'; // Merged PR + } + return 'octicon-git-pull-request'; // Closed PR + } else if (this.issue.state === 'open') { + return 'octicon-issue-opened'; // Open Issue + } + return 'octicon-issue-closed'; // Closed Issue + }, + + color() { + if (this.issue.state === 'open') { + return 'green'; + } else if (this.issue.pull_request !== null && this.issue.pull_request.merged === true) { + return 'purple'; + } + return 'red'; + }, + + labels() { + return this.issue.labels.map((label) => { + const red = parseInt(label.color.substring(0, 2), 16); + const green = parseInt(label.color.substring(2, 4), 16); + const blue = parseInt(label.color.substring(4, 6), 16); + let color = '#ffffff'; + if ((red * 0.299 + green * 0.587 + blue * 0.114) > 125) { + color = '#000000'; + } + return {name: label.name, color: `#${label.color}`, textColor: color}; + }); + } + }, + + mounted() { + this.$root.$on('load-context-popup', (data, callback) => { + if (!this.loading && this.issue === null) { + this.load(data, callback); + } + }); + }, + + methods: { + load(data, callback) { + this.loading = true; + $.get(`${AppSubUrl}/api/v1/repos/${data.owner}/${data.repo}/issues/${data.index}`, (issue) => { + this.issue = issue; + this.loading = false; + this.$nextTick(() => { + if (callback) { + callback(); + } + }); + }); + } + } +}; +</script> diff --git a/web_src/js/features/contextpopup.js b/web_src/js/features/contextpopup.js index c16820cf1f..8583c6253c 100644 --- a/web_src/js/features/contextpopup.js +++ b/web_src/js/features/contextpopup.js @@ -1,7 +1,6 @@ -import {htmlEscape} from 'escape-goat'; -import {svg} from '../svg.js'; +import Vue from 'vue'; -const {AppSubUrl} = window.config; +import ContextPopup from '../components/ContextPopup.vue'; export default function initContextPopups() { const refIssues = $('.ref-issue'); @@ -9,68 +8,36 @@ export default function initContextPopups() { refIssues.each(function () { const [index, _issues, repo, owner] = $(this).attr('href').replace(/[#?].*$/, '').split('/').reverse(); - issuePopup(owner, repo, index, $(this)); - }); -} -function issuePopup(owner, repo, index, $element) { - $.get(`${AppSubUrl}/api/v1/repos/${owner}/${repo}/issues/${index}`, (issue) => { - const createdAt = new Date(issue.created_at).toLocaleDateString(undefined, {year: 'numeric', month: 'short', day: 'numeric'}); + const el = document.createElement('div'); + el.className = 'ui custom popup hidden'; + el.innerHTML = '<div></div>'; + this.parentNode.insertBefore(el, this.nextSibling); - let body = issue.body.replace(/\n+/g, ' '); - if (body.length > 85) { - body = `${body.substring(0, 85)}...`; - } + const View = Vue.extend({ + render: (createElement) => createElement(ContextPopup), + }); - let labels = ''; - for (let i = 0; i < issue.labels.length; i++) { - const label = issue.labels[i]; - const red = parseInt(label.color.substring(0, 2), 16); - const green = parseInt(label.color.substring(2, 4), 16); - const blue = parseInt(label.color.substring(4, 6), 16); - let color = '#ffffff'; - if ((red * 0.299 + green * 0.587 + blue * 0.114) > 125) { - color = '#000000'; - } - labels += `<div class="ui label" style="color: ${color}; background-color:#${label.color};">${htmlEscape(label.name)}</div>`; - } - if (labels.length > 0) { - labels = `<p>${labels}</p>`; - } + const view = new View(); - let octicon, color; - if (issue.pull_request !== null) { - if (issue.state === 'open') { - color = 'green'; - octicon = 'octicon-git-pull-request'; // Open PR - } else if (issue.pull_request.merged === true) { - color = 'purple'; - octicon = 'octicon-git-merge'; // Merged PR - } else { - color = 'red'; - octicon = 'octicon-git-pull-request'; // Closed PR - } - } else if (issue.state === 'open') { - color = 'green'; - octicon = 'octicon-issue-opened'; // Open Issue - } else { - color = 'red'; - octicon = 'octicon-issue-closed'; // Closed Issue + try { + view.$mount(el.firstChild); + } catch (err) { + console.error(err); + el.textContent = 'ContextPopup failed to load'; } - $element.popup({ + $(this).popup({ variation: 'wide', delay: { show: 250 }, - html: ` -<div> - <p><small>${htmlEscape(issue.repository.full_name)} on ${createdAt}</small></p> - <p><span class="${color}">${svg(octicon)}</span> <strong>${htmlEscape(issue.title)}</strong> #${index}</p> - <p>${htmlEscape(body)}</p> - ${labels} -</div> -` + onShow: () => { + view.$emit('load-context-popup', {owner, repo, index}, () => { + $(this).popup('reposition'); + }); + }, + popup: $(el), }); }); } diff --git a/web_src/js/svg.js b/web_src/js/svg.js index 0960256e21..185c23c245 100644 --- a/web_src/js/svg.js +++ b/web_src/js/svg.js @@ -14,6 +14,8 @@ import octiconRepo from '../../public/img/svg/octicon-repo.svg'; import octiconRepoForked from '../../public/img/svg/octicon-repo-forked.svg'; import octiconRepoTemplate from '../../public/img/svg/octicon-repo-template.svg'; +import Vue from 'vue'; + export const svgs = { 'octicon-chevron-down': octiconChevronDown, 'octicon-chevron-right': octiconChevronRight, @@ -47,3 +49,19 @@ export function svg(name, size = 16, className = '') { if (className) svgNode.classList.add(...className.split(/\s+/)); return serializer.serializeToString(svgNode); } + +export const SvgIcon = Vue.component('SvgIcon', { + props: { + name: {type: String, required: true}, + size: {type: Number, default: 16}, + className: {type: String, default: ''}, + }, + + computed: { + svg() { + return svg(this.name, this.size, this.className); + }, + }, + + template: `<span v-html="svg" />` +}); |