v1.2.0.1-beta1
Fetch and Notify Example - GitHub Release Version Check

See the published documentation for a properly formatted version of this README.

This example demonstrates serveral things:

  1. Getting some data from a Web site using Net.fetch(), which in this case is in JSON format.
  2. Extracting useful information from the JSON data.
  3. Sending a Notification message to Touch Portal with TP.showNotification(), which a user can interact with.
  4. Handle any choice (click) the user made in the notification dialog and open a Web browser with Process.startCommand().

It does this in the form of a version/update checker which contacts the GitHub server to get the latest release version information of a particular repository (they have an API for that).

It can then compare the latest release version against a current version, and if a newer version is found it can send the Notification to Touch Portal for display.

The Notification contains a link to download the latest version. If the user clicks on that link, a callback function is executed in this code which opens the default Web browser to the desired URL.

Note
Assets for this example, including the code and sample button shown below, can be found in the project's repository at
https://github.com/mpaperno/DSEP4TP/tree/main/resources/examples/FetchAndNotify/
1
2// Function to check a GitHub repository's releases for the latest version tag, compare that to a current version, log the result
3// and optionally send a Touch Portal notification if a new version is found.
4// This can also process a user click in the notification to open the download link for a new version in the default web browser.
5//
6// - `user_repo` is a GitHub URL path in the form of 'user_name/repository_name'.
7// - `current_version` is the current version string to check against, in "semantic versioning" format, eg. "1.22.3-beta2".
8// - If `show_notification` is true, a Touch Portal Notification will be sent if a newer version is detected.
9//
10// In any case it will log what happened to 'console.log()' which will show up in the plugin's console.log.
11//
12// Example usage, check for updates of this very DSEP4TP plugin.
13// This uses the current plugin version number constant from the global environment (DSE.PLUGIN_VERSION_STR) which is something like "1.1.0.4".
14// checkReleaseVersion("mpaperno/DSEP4TP", DSE.PLUGIN_VERSION_STR, true);
15
16var checkReleaseVersion = function(user_repo, current_version, show_notification = false)
17{
18 // URL to get JSON data about a repository's latest Release.
19 const url = "https://api.github.com/repos/" + user_repo + "/releases/latest";
20 // Initiate the request here with fetch(). It returns a "promise". Here we set the 'rejectOnError' option to 'true' so anything
21 // besides a successful HTTP result (200-299) will simply result in an error, instead of having to check the response in a
22 // separate step.
23 Net.fetch(url, { rejectOnError: true })
24 // When the response is received, we want to parse it as JSON into a JavaScript object (`JSON.parse()`) and then work
25 // on the result. `response.json()` returns another promise which resolves with the actual de-serialized JS object.
26 .then(response => response.json())
27 // At the next step we get a 'result' which is the JSON from the response body parsed into a JavaScript Object (just like from JSON.parse()).
28 .then((result) => {
29 // Now we can actually work with the data. First check that a 'tag_name' field is even present, otherwise something didn't go right.
30 if (result.tag_name !== undefined) {
31 // make numeric version number values from both the passed-in current version and the new tag version.
32 const currVersion = versionStringToNumber(current_version);
33 const newVersion = versionStringToNumber(result.tag_name);
34 const hasNewVersion = newVersion > currVersion;
35
36 // Prepare the message, this will go to the console log and optionally as a TP Notification.
37 let logText = `The latest release date is ${result.published_at};\n`;
38 logText += `The latest tag is ${result.tag_name}; Current version: ${current_version};\n`;
39 logText += `Latest release version ${newVersion.toString(16)}; Current version: ${currVersion.toString(16)}\n\n`;
40
41 if (hasNewVersion)
42 logText += "The latest released version is newer!\n" +
43 `Click below or go to\n<${result.html_url}>\nfor downloads.`;
44 else if (newVersion == currVersion)
45 logText += "The latest release version is same as current.";
46 else
47 logText += "Congratulations, you're running a development version! (-8";
48
49 // log it
50 console.log('Version Check Report:\n' + logText);
51
52 // Send the notification message if the option was enabled and a new version detected.
53 if (show_notification && hasNewVersion)
54 {
55 // We are going to put a "Download" link in the notification message. If the user clicks that link, we can get a callback
56 // about that event, and then do something useful based on what the user chose. There could be multiple choices, but we just have one.
57 // The choices are defined as an array of objects with `id` and `title` keys. The `id` is sent back to our callback handler
58 // while the `title` is what the user sees and can click on.
59 let options = [
60 {
61 id: 'goto',
62 title: "Go To Download"
63 }
64 ];
65
66 // This function will handle the callback.
67 // The callback is given the ID of the notification and of the option which was clicked.
68 let callback = function(choiceId, notificationId) {
69 console.log(`The option ID '${choiceId}' was selected for notification ID '${notificationId}'.`);
70 // This should open the default browser with the download URL. The command depends on the operating system.
71 if (DSE.PLATFORM_OS === "windows")
72 new Process().startCommand(`explorer "${result.html_url}"`); // also possibly 'start firefox' or 'start chrome'
73 else if (DSE.PLATFORM_OS === "osx")
74 new Process().startCommand(`open "${result.html_url}"`);
75 else if (DSE.PLATFORM_OS === "linux")
76 new Process().startCommand(`xdg-open "${result.html_url}"`); // or possibly 'sensible-browser' on Ubuntu
77 }
78
80 result.tag_name, // notification ID, should be unique per event so version tag makes sense here.
81 "New Version Available", // notification title
82 logText, // main message body
83 options, // array of choices which will show in the notification
84 callback // callback for reacting to option choice clicks
85 );
86 }
87
88 // Optionally dump the whole JSON document to console log to see what's in it.
89 //console.log(JSON.stringify(result, null, 2));
90 }
91 else {
92 // Something went wrong with the request.
93 console.warn("Could not find tag name in returned data :-( ");
94 // we could show the data or do some other diagnostic...
95 // console.log(JSON.stringify(result, null, 2));
96 }
97 })
98 // Catch is where any errors will end up, from either the network request itself (eg. couldn't connect),
99 // or potentially from parsing the JSON data. Here we'll just log the error to keep it simple.
100 // Check the `Net.fetch()` documentation for possible error types.
101 .catch((e) => {
102 console.error(e);
103 });
104}
105
106// Helper function to create integer version number from dotted notation string by shifting and ORing the numeric parts.
107// eg. "1.20.3.44" -> ((1 << 24) | (20 << 16) | (3 << 8) | 44)
108// Each version part is limited to the range of 0-99. Up to 4 parts can be used (or fewer).
109// Anything besides the dotted numerals should be ignored (eg. "v" prefix or "-beta1" suffix).
110function versionStringToNumber(version)
111{
112 var iVersion = 0;
113 version = version.replace(/([\d\.]+)/, '$1');
114 for (const part of version.split('.', 4))
115 iVersion = iVersion << 8 | ((parseInt(part) || 0) & 0xFF);
116 return iVersion;
117}
The DSE object contains constants and functions related to the plugin environment....
String PLATFORM_OS
This property contains the name of the operating system running the plugin.
Definition: DSE.h:95
The Process class allows interaction with external processes, such as launching a system command or r...
Definition: Process.h:62
The global TP namespace is used as a container for all Touch Portal API methods, which are invoked wi...
Definition: touchportal.dox:7
void showNotification(String notificationId, String title, String message, Array< Object > options=[],< Function|String|Array > callback=null)
Creates or raises a user notification in the Touch Portal window.
The Net namespace contains static functions for working with network requests.
Definition: fetch.dox:4

Example usage on a button