Quantcast
Channel: TMS Software
Viewing all articles
Browse latest Browse all 1006

Extend TMS WEB Core with JS Libraries with Andrew:HexaGongs Part 5 of 5: Deployment

$
0
0



Photo of Andrew Simard

In our fifth and final post covering our TMS WEB Core and TMS XData project, HexaGongs, we'll start by wrapping up a few loose ends from last time.  Then we'll have a look at our final set of main buttons - used for saving, loading, and sharing HexaGongs projects.  And then we'll finish up by covering several items related to deploying these kinds of projects.

Contents.

  1. HexaGong Sets.
  2. Saving HexaGongs Projects.
  3. Loading HexaGongs Projects.
  4. Deployment - TMS XData Project
  5. Deployment - TMS WEB Core
  6. All Done - For Now.


HexaGong Sets.

We left out the last item in our list of audio sources last time, to focus on the UI for making audio adjustments.  That was in part because the last audio source doesn't need any audio adjustments.  It is used to select a set of HexaGongs, so that they can all be played together, either in series or in parallel.  In order to select which HexaGongs are to be included, we'll need some kind of component that can list all the HexaGongs that are in the project, and then also allow us to select them individually as well as provide the means to select the order they will be accessed, if used in series.

Standard HTML components for selecting entries from a list, like the <select> element, tend to not work all that great for a few reasons.  Mostly because they don't offer the flexibility that we need natively for any but the most basic tasks.  But we've already got a component in our project that we've used to display lists - Tabulator.   Let's use that to create a list of HexaGongs, and then add the columns we need to provide for selection as well as for dragging the list items to rearrange the sort order.  The definition for this Tabulator table, like the one used for the Audio Clips list, can be found in WebFormCreate.

    this.tabAudioSets =  new Tabulator("#divAudioSetsTable", {
      index: "ID",
      layout: "fitColumns",
      placeholder: "No HexaGongs available.",
      rowHeight: 30,
      selectable: 1,
      headerVisible: false,
      movableRows: true,
      columnDefaults:{
        resizable: false
      },
      initialSort: [
        {column:"Sort", dir:"asc"},
        {column:"Name", dir:"asc"}
      ],
      columns: [
        { title: "ID", field: "ID", visible: false },
        { title: "Selected", field: "Selected", width: 40, cssClass: "PlayClip",
          formatter: function(cell, formatterParams, onRendered) {
            if (cell.getValue() == false) {
              return '<div style="background: purple; width: 30px; height: 29px; margin: -3px 0px 0px 4px; padding: 5px 8px; "><i class="fa-solid fa-xmark fa-xl"></i></div>'
            }
            else {
              return '<div style="background: violet; width: 30px; height: 29px; margin: -3px 0px 0px 4px; padding: 5px 4px; "><i class="fa-solid fa-check fa-xl"></i></div>'
            }
          },
          cellClick: function(e, cell) {
            pas.Unit1.Form1.tabAudioClips.selectRow(cell.getRow());
            cell.setValue(!cell.getValue());
          },
        },
        { title: "Sort", field: "Sort", width: 30, minWidth:30, formatter: "handle" },
        { title: "Name", field: "Name" },
        { title: "Length", field: "Length", width: 60, hozAlign: "right", formatter:"html" }
      ]
    });

In this case, most of the attention is focused on the "Selected" column - where the inclusion of each HexaGong in the set can be set.  This is all handled within Tabulator itself, toggling the boolean value when the cell is clicked, and displaying either a Font Awesome "check" or "xmark" and adjusting the color at the same time. The list can also be sorted manually, by dragging the rows.  The "hamburger" icon is included as a column, though strictly not necessary as the columns can be dragged without this being visible. This extra sorting capability adds another layer of complexity, but is necessary in order to set the order of playback of the selected HexaGongs.  

This sets list is created dynamically each time the Options dialog is shown, so as to have the most current information about HexaGong play times, names, and so on.  The last settings for a particular HexaGong are then applied to this updated list.  A bit of work, but this is needed to keep things consistent as HexaGongs are changed.

  asm
    // Get list of known HexaGongs (not including the current HexaGong or deleted HexaGongs)
    this.OptionsAudioSetsData = [];
    for (var i = 0; i < this.GongData['HexaGongs'].length; i++) {
      if (parseInt(this.GongID) !== i) {
        if (this.GongData['HexaGongs'][i]['Deleted'] !== true) {
          this.OptionsAudioSetsData.push({
            "ID": i,
            "Selected": false,
            "Sort": -1,
            "Name": this.GongData['HexaGongs'][i]['Name'],
            "Length": '<div style="padding-right: 8px;">'+this.GongData['HexaGongs'][i]['Audio Time'].toFixed(1)+'s'+'</div>'
          });
        }
      }
    }

    // update the list to reflect the last sort order and selection for this HexaGong.
    for (var i = 0; i < this.GongData['HexaGongs'][this.GongID]['Audio Sets Data'].length; i++) {
      var id = this.GongData['HexaGongs'][this.GongID]['Audio Sets Data'][i].ID;
      for (var j = 0; j < this.OptionsAudioSetsData.length; j++) {
        if (this.OptionsAudioSetsData[j].ID == id) {
          this.OptionsAudioSetsData[j].Sort     = this.GongData['HexaGongs'][this.GongID]['Audio Sets Data'][i].Sort;
          this.OptionsAudioSetsData[j].Selected = this.GongData['HexaGongs'][this.GongID]['Audio Sets Data'][i].Selected;
        }
      }
    }

    this.tabAudioSets.setData(this.OptionsAudioSetsData);
    this.tabAudioSets.setSort([
      {column:"Name", dir:"asc"},
      {column:"Sort", dir:"asc"}
    ]);
  end;

Ultimately, we end up with a new "list box" where we can select any number of HexaGongs to include in the set, as well as change the sort order manually and display whatever other supporting information (like play length) that we might want to include.

TMS Software Delphi  Components
Audio Set Editing.

When saving this type of HexaGong, we can store the selected HexaGongs as an attribute alongside the other audio parameters.

  asm
   var SelectedSets = [];
    for (var i = 0; i < this.tabAudioSets.getDataCount(); i++) {
      this.tabAudioSets.getRowFromPosition(i+1).getCell('Sort').setValue(i);
      if (this.tabAudioSets.getRowFromPosition(i+1).getCell('Selected').getValue() == true) {
        SelectedSets.push('Gong-'+this.tabAudioSets.getRowFromPosition(i+1).getCell('ID').getValue());
      }
    }
    this.GongData['HexaGongs'][this.GongID]['Audio Sets Data'] = this.tabAudioSets.getData();
    this.GongData['HexaGongs'][this.GongID]['Audio Sets'] = JSON.stringify(SelectedSets);
    Sets = JSON.stringify(SelectedSets);
  end;

  if OptionsAudioStyle = 4
  then Gongs[GongID].ElementHandle.setAttribute('audiosets',Sets)
  else Gongs[GongID].ElementHandle.removeAttribute('audiosets');


To get the selected HexaGongs all playing in parallel, we can just click on all of the buttons that are included in the set.

            var sets = JSON.parse(event.target.getAttribute('audiosets'));
            if (sets !== null)  {
              sets.forEach(gong => {
                var el = document.getElementById(gong);
                el.click();
              });
              return;
            }

There's more to do here, such as setting them to play in series, potentially with an adjustable pause between them.  


Saving HexaGongs Projects.

Our last big-ticket items relate to saving and loading all the HexaGongs as a single project file.  For the most part, this involves saving the GongData JSON object, but we'll have to add some of our other arrays to it.  In particular, our GongAudio array, containing the decoded audio data, as well as the information about the positions of each of the HexaGongs. For now, let's deal with the JSON itself.  To trigger the browser's file saving mechanism, we can use another JavaScript library, FileSaver, which we previously covered in this post. As usual, we can add this via the Project.html file directly or by using the Delphi IDE's Manage JavaScript Libraries feature.

    <script src="https://cdn.jsdelivr.net/npm/file-saver@latest/dist/FileSaver.min.js"></script>  

The implementation then looks like the following. Note that we're creating files with the "hexagongs" extension, but they're just regular JSON files, for now at least.

procedure TForm1.btnDownloadClick(Sender: TObject);
var
  FileData: String;
  FileName: String;
begin
  FileData := '';
  FileName := '';
  asm
    FileData = JSON.stringify(this.GongData);
    FileName = this.GongData['HexaGongs Project Title']+'.hexagongs';
    var blob = new Blob([FileData], {type: "application/json;charset=utf-8"});
    saveAs(blob, FileName);
  end;
end;


Naturally, any and all JSON object contents can be saved in this way.  Note that this FileSaver mechanism doesn't prompt the user for a filename - it just downloads the file automatically, adding (1) or (2), etc. to the filename if it already exists.  The filename is taken from the first page of the Options dialog.  Ultimately, we'll want to create a new JSON object using GongData as the base, adding in whatever other bits we want, including things like the size and position of the Options dialog, zoom level, master volume level and so on.  So the contents of the JSON will change, but this saving mechanism will still work.

Loading HexaGongs Projects.

We've already had a couple of examples of loading files for this project.  This time out, we're doing the same thing and, perhaps amusingly, using a third variation of the TWebOpenDialog - the GetFileAsText option. For the "access" property, we can use our "hexagongs" extension that we used above.

procedure TForm1.btnUploadClick(Sender: TObject);
var
  i: Integer;
begin
  WebOpenDialog1.Accept := '.hexagongs';
  await(string, WebOpenDialog1.Perform);
  // If files were selected, iterate through them
  i := 0;
  while (i < WebOpenDialog1.Files.Count) do
  begin
    WebOpenDialog1.Files.Items[i].GetFileAsText;
    i := i + 1;
  end;
end;

Once we have the file, we can then replace our data structures by reversing the process we used to create the JSON in the saved file.  In our previous example, we just saved the GongData JSON objec.  Here we can just replace it.

procedure TForm1.WebOpenDialog1GetFileAsText(Sender: TObject; AFileIndex: Integer; AText: string);
begin
  asm
    pas.Unit1.Form1.GongData = JSON.parse(AText);
  end;
end;

Ultimately, we will want to add a little more error-checking to this, but ultimately this is all that we're doing.  Of course, we'll want to extract other data, such as the aforementioned Options dialog size and position, etc.  The GongData JSON object would likely end up being just one of many other elements in the JSON that is being saved and loaded.


Deployment - TMS XData Project

Deploying an XData project primarily involves running the generated executable file, found in the release folder, on a system that is at the very least accessible to the TMS WEB Core application that is accessing it.  Back in this post, a bit of a deployment checklist was developed.  Here's an updated version.

  1. Come up with a creative and catchy name for the project.
  2. Register the domain name.
  3. Configure domain registrar's nameserver to resolve domain to your server (VPS or whatever you're using)
  4. Use LetsEncrypt or equivalent to acquire an SSL certificate for this domain.
  5. Build "Release" version of XData project.
  6. Copy the Release folder contents of the client to your web server.
  7. Rename Project.html to index.html or adjust web server so that the Project.html file is loaded by default.
  8. Update or add JSON configuration file to specify the base URL and port number to use, file paths, etc.
  9. Run the XData service application, make accommodations for it to start after server reboots.
  10. Add the Port to the Windows firewall.
  11. Use the TMS HTTPSConfig tool to reserve the IP address and connect the SSL certificate.
  12. Test the client application. 
  13. Test the Swagger UI connection.
  14. Populate any supporting data, like icon-sets, audio-clips, etc.
  15. Test URLs, like www.example.com, http://example.com, other variations.

For that last step, most web servers have some kind of mechanism for rewriting the URL.  This might be to ensure that only the SSL version is used.  Or perhaps to enforce the "www" prefix, or alternatively to ensure that it is never used.  In Apache, for example, the www prefix and SSL can be enforced using something like this.  There are many examples (and many variations!) on how to do this kind of thing for whatever web server you happen to be using.

  RewriteEngine on

  RewriteRule ^ - [E=protossl]
  RewriteCond %{HTTPS} on
  RewriteRule ^ - [E=protossl:s]
  RewriteCond %{HTTPS} !=on
  RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

  RewriteCond %{HTTP_HOST} .
  RewriteCond %{HTTP_HOST} !^www\. [NC]
  RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Ultimately, you'll want to just check and make sure that the XData server is accessible only over SSL from wherever you are running the main TMS WEB Core app from.  Most of the time this might be public-facing websites, but all of this could be used on an internal network or over a VPN just as easily, so be sure to test it with that in mind.

Deployment - TMS WEB Core

Generally speaking, deploying a TMS WEB Core app to the web involves copying the contents of the "release" project folder over to a public-facing web server of some kind, such as Apache, IIS, or NGINX.  There are no special requirements to use a given web server - no doubt most will work just fine.  The main thing we're having to do that might be a little different is that we've configured our app to use a configuration JSON file so that we can tell it where to find the corresponding XData server.  This allows us to do our development work against one XData server, and then deploy the project to another server without having to change any of the source code as part of the deployment step.

The main challenges around deploying TMS WEB Core apps generally tend to revolve around testing different browsers and ensuring that the app works in all the environments that your visitors might be using.  It is a good idea to test different browsers periodically, as often there are bizarre issues that arise.  

In our project here, for example, the range sliders that are part of the Image Adjustments section are intended to show a nice linear gradient.  And they do, except for Firefox.  No issues on Chrome for Windows/Linux or iPadOS, but Firefox doesn't seem to want to do that - it just displays a solid color instead.  The range sliders that are part of the RGB color picker work fine, and the same linear-gradients are used elsewhere in the Options dialog without issue.  Another example is that recording audio on iOS seems to not work at the moment.

When sorting through issues, it might be that some items are just annoying (like the linear-gradients) or maybe deal-breakers (like iOS audio recording) that require further investigation.  Fortunately, if you run into problems, there is an excellent team behind the TMS Support Center that can likely answer your questions.  Don't be shy!

All Done - For Now.

That about covers the basic implementation for the HexaGongs project.  There are a few more items to sort through, but as far as something like an "MVP" (minimum viable product) I think we're well on our way here.  What do you think?  Is there a major feature that is missing that might make this the next killer app?  Is something horribly broken beyond repair?  Often when working on projects such as this, it is helpful to have another set of eyes to look things over, certainly, and provide a different perspective.  Our main objective has been achieved, however, with hexagons permeating every aspect of our UI, from the simplest TWebEdit field all the way to a more complex multi-select-list type of component using Tabulator.

HexaGongs website.
HexaGongs repository on GitHub.
HexaGongs XData repository on GitHub.




Viewing all articles
Browse latest Browse all 1006

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>