Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Load issue/PR context popup data only when needed
  • Loading branch information
lafriks committed Jul 7, 2021
commit db2bcf04ee73f245f154a263172b5ccb72734375
109 changes: 109 additions & 0 deletions web_src/js/components/ContextPopup.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<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><span :class="color" v-html="icon" /> <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 {svg} from '../svg.js';
const {AppSubUrl} = window.config;

export default {
name: 'ContextPopup',

data: () => ({
loading: false,
issue: null
}),

computed: {
createdAt() {
return this.issue !== null ?
new Date(this.issue.created_at).toLocaleDateString(undefined, {year: 'numeric', month: 'short', day: 'numeric'}) :
null;
},

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 svg('octicon-git-pull-request'); // Open PR
} else if (this.issue.pull_request.merged === true) {
return svg('octicon-git-merge'); // Merged PR
}
return svg('octicon-git-pull-request'); // Closed PR
} else if (this.issue.state === 'open') {
return svg('octicon-issue-opened'); // Open Issue
}
return svg('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>
77 changes: 22 additions & 55 deletions web_src/js/features/contextpopup.js
Original file line number Diff line number Diff line change
@@ -1,76 +1,43 @@
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');
if (!refIssues.length) return;

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),
});
});
}