src/com.jetbrains.youtrack.sdPlugin/app.js (201 lines of code) (raw):
/// <reference path="libs/js/action.js" />
/// <reference path="libs/js/stream-deck.js" />
const myAction = new Action('com.jetbrains.youtrack.get-ticket-count');
myAction.activeTiles = {}
class PeriodicYouTrackRequest {
settings
context
//todo: convert youTrack to class
youTrack = {
Ready: false,
token: "",
url: "",
searchQuery: "",
GetTicketsCount: async function () {
try {
if (this.Ready) {
return await this.RunQuery(this.url, this.token, this.searchQuery);
} else {
return -1;
}
} catch (e) {
console.log(e.stack);
return -1;
}
},
RunQuery: async function (url, token, query) {
url = url + '/api/issues?fields=idReadable,summary,votes,customFields(projectCustomField(field(name)),value(name))&$top=99999&query=' + this.EscapeQuery(query)
console.log("executing: " + query);
let response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
}
});
const data = await response.json();
return data.length;
},
EscapeQuery: function (query) {
return query.replace(/ /g, '+').replace(/#/g, '%23');
},
OpenPage: function () {
let url = this.url + '/issues?q=' + this.EscapeQuery(this.searchQuery)
$SD.openUrl(url);
}
};
timers = {
activeTimers: [],
addTimer: function (name, timer) {
this.activeTimers.push({name: name, timer: timer});
},
newPeriodicTask: function (name, callback, delay, initialDelay = Math.floor(Math.random() * 1000)) {
let tick = async () => {
await callback();
if (this.timerIdExists(timer)) {
setTimeout(tick, delay);
}
}
let timer = setTimeout(tick, initialDelay);
this.addTimer(name, timer);
},
timerIdExists: function (id) {
return this.activeTimers.some(x => x.timer === id);
},
timerNameExists: function (name) {
return this.activeTimers.some(x => x.name === name);
},
stopAll: function () {
this.activeTimers.forEach(timer => {
clearInterval(timer.timer);
clearTimeout(timer.timer);
})
this.activeTimers = [];
},
stopByName: function (name) {
for (let activeTimersKey in this.activeTimers) {
if (this.activeTimers[activeTimersKey].name === name) {
clearInterval(this.activeTimers[activeTimersKey].timer);
clearTimeout(this.activeTimers[activeTimersKey].timer);
this.activeTimers.splice(activeTimersKey, 1);
}
}
},
}
updateSettings = (new_settings) => {
this.settings = new_settings;
this.settings["last-result"] = "";
this.restart()
}
destroy() {
this.timers.stopAll();
console.log("destroyed all timers. Active timers:" + this.timers.activeTimers);
}
restart() {
this.destroy()
this.start();
}
start() {
let restartPeriodicPoll = () => {
this.destroy();
IndicateOngoingRefresh();
this.timers.newPeriodicTask("periodic-poll", refreshValue, this.settings["refresh-interval"] * 1000 || 60 * 1000);
}
let sendRequest = async () => {
let ticketCount = await this.youTrack.GetTicketsCount();
let count = -1;
try {
count = ticketCount.toString();
} catch (e) {
console.error("Could not get ticket count from response: ", +ticketCount);
}
if (this.settings["hide-zero"] === "on" && ticketCount === 0) {
count = "";
}
return count;
}
let refreshValue = async () => {
let ticketCount = await sendRequest()
this.timers.stopByName("refreshIndicator")
if (ticketCount !== -1) {
updateLastResult(ticketCount)
setTileValue(ticketCount)
} else {
setTileValue("!ERR")
}
}
let updateLastResult = (ticketCount) => {
this.settings["last-result"] = ticketCount;
$SD.setSettings(this.context, this.settings);
}
let IndicateOngoingRefresh = (wait = 500) => {
let dots = "";
let title = "";
let indicatorFunction = () => {
dots += ".";
if (dots.length > 3) {
dots = "";
}
// If there is a cached result, blinks the title while polling (indicating that the cached result is shown)
if (this.settings["last-result"] && this.settings["last-result"] !== "" && this.settings["last-result"] !== undefined) {
if (dots.length % 2 !== 0) {
title = this.settings["last-result"];
} else {
title = "♻"
}
} else {
title = dots;
}
if (this.timers.timerNameExists("refreshIndicator")) {
setTileValue(title);
}
}
this.timers.newPeriodicTask("refreshIndicator", indicatorFunction, wait);
}
let setTileValue = (str) => {
let name = this.settings["yt-search-name"] || "";
let title = name.length > 0 ? name + "\n" + str : str;
// console.log("setting title to: " + title.replace(/\n/g, "\\n"));
$SD.setTitle(this.context, title);
}
this.fillYTSettings(this.settings);
restartPeriodicPoll()
}
//todo: move fillYTSettings to youTrack object
fillYTSettings = () => {
this.youTrack.token = this.settings["yt-token"];
this.youTrack.url = this.settings["yt-url"];
this.youTrack.searchQuery = this.settings["yt-search-query"];
this.youTrack.Ready = !!(this.youTrack.url && this.youTrack.token && this.youTrack.searchQuery);
}
constructor(context, settings) {
this.settings = settings;
this.context = context;
this.start();
}
}
/**
* The first event fired when Stream Deck starts
*/
$SD.onConnected(({actionInfo, appInfo, connection, messageType, port, uuid}) => {
console.log('Stream Deck connected!');
});
myAction.onKeyUp(({action, context, device, event, payload}) => {
const api_request = myAction.activeTiles[context];
if (!api_request || !api_request.youTrack.Ready)
$SD.showAlert(context);
else
api_request.youTrack.OpenPage();
});
myAction.onDidReceiveSettings(({action, context, device, event, payload}) => {
const settings = payload.settings;
const api_request = myAction.activeTiles[context];
if (!settings || !api_request) return;
api_request.updateSettings(settings);
})
myAction.onSendToPlugin(async ({action, context, device, event, payload}) => {
if (action === 'com.jetbrains.youtrack.get-ticket-count') {
myAction.activeTiles[context] = new PeriodicYouTrackRequest(context, payload.settings);
}
});
myAction.onWillAppear(async ({action, context, device, event, payload}) => {
myAction.activeTiles[context] = new PeriodicYouTrackRequest(context, payload.settings);
})
myAction.onWillDisappear(async ({action, context, device, event, payload}) => {
let yt_request = myAction.activeTiles[context];
if (yt_request) {
yt_request.destroy();
delete myAction.activeTiles[context];
}
})