Skip to content

Documents that describe Quicket vector seatmap integration

Notifications You must be signed in to change notification settings

Kwiket/jets-vector-setmap-integration

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 

Repository files navigation

Seatmap integration and communication

This document describes how to integrate seatmap HTML page (further "seatmap") into any website, Android or iOS mobile app. Plus, communication between the seatmap and a parent layer that embeds seatmap (further just "parent layer").

They say seats with letter F are CSS bug. I say it's a smoking area 😜

 

Table of contents

 

Integration

This section explains how to integrate seatmap into a website, Android and iOS mobile apps.

 

Website integration

Embed seatmap into HTML page via <iframe>:

const iframe = document.createElement('iframe');

iframe.addAttribute('frameborder', '0');
iframe.addAttribute('marginheight', '0');
iframe.addAttribute('marginwidth', '0');
iframe.addAttribute('scrolling', 'yes');
iframe.id = 'seatmap';
iframe.addEventListener('load', handleSeatmapLoad, false);
iframe.src = 'http://demo-seatmap.quicket.me/htmls/dev/htmls/1764.html';
document.body.appendChild(iframe);

⚠️  It's important to listen to load event, ensuring iframe contents is loaded before publishing messages to it.

  When iframe contents is loaded, publish message down to the embed seatmap via postMessage() function:

function handleSeatmapLoad(e: BrowserEvent) {
  const targetIframe = e.currentTarget as HTMLIframeElement;
  const targetIframeWindow = targetIframe.contentWindow;
  const message = { // Shall match `IMessage` standard interface.
    type: 'SYNC_PASSENGERS',
    data: {
      flightGuid: '1ABC',
      passengers: [
        {
          id: '1',
          seatPreference: null,
          seat: null
        }
      ]
    }
  };

  targetIframeWindow.postMessage(JSON.stringify(message), '*');
}

  Subscribe to message emitted by the seatmap:

function handleMessage(e: MessageEvent): void {
  const message = JSON.parse(e.data);

  // `message`, matches `IMessage` standard interface with data received from the seatmap.
}

window.addEventListener('message', handleMessage, false);

 

Android integration

Seatmap shall be rendered using WebView.

WebView webview = new WebView(this);

setContentView(webview);
webview.loadUrl("1764.html?<b>platform=android</b>");

⚠️  It's important and required to include ?platform=android GET parameter into loadable url.

  When WebView contents is loaded, it's possible to publish messages to the seatmap and receive messages back from it.

  Publish message into the seatmap by executing public JavaScript function within WebViewweb.sendMessage(message: string). Seatmap has this function available for you.

webView.evaluateJavascript("web.sendMessage(\'{\"type\":\"SYNC_PASSENGERS\",\"data\":{\"flightGuid\":\"1ABC\",\"passengers\":[{\"id\":\"1\",\"seatPreference\":null,\"seat\":null}]}}\');

  To receive message from the seatmap add JavaScript interface into the seatmap. Normally, webView.addJavascriptInterface() native method can be used to inject and map Java interface inside JavaScript source code in a WebView. Interface shall contain handleSeatmapEvent() public method. Make sure the name used to expose the object in JavaScript is android.

Here's approximately how to add JavaScript interface into WebView:

import org.json.JSONObject;
…

class JsObject {
  @JavascriptInterface
  public void handleSeatmapEvent(String: serializedMessage) {
    JSONObject message = new JSONObject(serializedMessage);

    // `message`, matches `IMessage` standard interface with data received from the seatmap.
  }
}

webView.addJavascriptInterface(new JsObject(), "android");

Now when interface is available within JavaScript code inside WebView, seatmap is going to execute android.handleSeatmapEvent(serializedMessage) method to send messages up to Android. You shall be able to receive every incoming message and build necessary business logic to handle it.

 

  iOS integration

Seatmap shall be rendered within WKWebView.

import WebKit


var webView = WKWebView()
var url = NSURL(string:"1764.html?<b>platform=ios</b>")
var req = NSURLRequest(URL:url)

webView!.loadRequest(req)

⚠️  It's important and required to include ?platform=ios GET parameter into loadable url.

  Publish message into the seatmap by executing public JavaScript functionweb.sendMessage(message: string). Seatmap has this function available for you.

webView.evaluateJavaScript("web.sendMessage(\'{\"type\":\"SYNC_PASSENGERS\",\"data\":{\"flightGuid\":\"1ABC\",\"passengers\":[{\"id\":\"1\",\"seatPreference\":null,\"seat\":null}]}}\')

  To receive message from the seatmap, follow this article. In general, WKWebView automatically injects webkit.messageHandlers.callbackHandler.postMessage() method into the seatmap. This method connects WKWebView contents and Swift native source code. Seatmap is going to execute this method to send messages up to iOS. You shall be able to build necessary business logic to handle incoming messages.

postMessage(serializedMessage) is going to receive the only parameter, that is serialised JSON object matching IMessage standard interface. Learn to handle incoming messages and implement necessary business logic.

 

i18n internationalization   🇱🇻   🇺🇸   🇩🇪   🇺🇦

Seatmap is able to display labels (especially seat descriptions) in multiple languages:

  • English en, en-US or en_US
  • German de, de-DE or de_DE
  • Russian ru, ru-RU or ru_RU
  • Chinese Simplified cn, cn-CN or cn_CN
  • French fr, fr-FR or fr_FR
  • Italian it, it-IT or it_IT
  • Swedish sv, sv-SV or sv_SV
  • Danish da, da-DA or da_DA
  • Norwegian no, no-NO or no_NO
  • Spanish es, es-ES or es_ES

Default language is English.

To enforce seatmap labels display in a specific language, include ?language= GET parameter into seatmap url/path rendered inside iframe, WebView or WKWebView.

When seatmap is integrated into a website:

iframe.src = 'http://demo-seatmap.quicket.me/htmls/dev/htmls/1764.html?language=ru_RU';

When integrated into Android mobile app:

webview.loadUrl("1764.html?platform=android&<b>language=ru_RU</b>");

Or within iOS app:

var url = NSURL(string:"1764.html?platform=ios&<b>language=ru_RU</b>")
var req = NSURLRequest(URL:url)

webView!.loadRequest(req)

 

Color themes

Seatmap styling could be changed using color themes. At the moment, we have 4 different color themes:

You can try to create own color theme here and export a JSON file - we can built-in it in next release.

To enforce seatmap use one of these color themes, include ?colorTheme= GET parameter into seatmap url/path rendered inside iframe, WebView or WKWebView.

When seatmap is integrated into a website:

iframe.src = 'http://demo-seatmap.quicket.me/htmls/dev/htmls/1764.html?<b>colorTheme=skyscanner</b>';

When integrated into Android mobile app:

webview.loadUrl("1764.html?platform=android&<b>colorTheme=kayak</b>&language=cn_CN");

Or within iOS app:

var url = NSURL(string:"1764.html?platform=ios&<b>colorTheme=momondo</b>&language=ru_RU")
var req = NSURLRequest(URL:url)

webView!.loadRequest(req)

 

📢  Available communication message types

All messages follow standard IMessage interface that is used for bidirectional communication:

interface IMessage {
  readonly type: string;
  readonly data: {};
}

type represents one of available message types (see below) and data is custom to every message.

Seatmap is able to receive and emit the following messages:


 

SEAT_MAP__LOADED

This message fires up when content (DOM tree, images etc.) is loaded. It also provides height and width of document view.

Interface, describing data types:

interface IWebViewData {
  deviceDPI: number;
  devicePixelRatio: number;
  heightInPx: number;
  planeId: number;
  platform: string; // "web" / "android" / "ios"
  scrollLeftInPx: number;
  scrollTopInPx: number;
  widthInPx: number;
}

Example of data parent layer receives:

{
  "data": {
    "deviceDPI": 288,
    "devicePixelRatio": 3,
    "heightInPx": 736,
    "planeId": 1719,
    "platform": "ios",
    "scrollLeftInPx": 0,
    "scrollTopInPx": 1456,
    "widthInPx": 414
  },
  "type": "SEAT_MAP__LOADED"
}

 

SEAT_MAP__DATA

This message works in both directions (request–response). It is similar to SEAT_MAP__LOADED except you can call it anytime.

Interface, describing data types:

interface IWebViewData {
  deviceDPI: number;
  devicePixelRatio: number;
  heightInPx: number;
  planeId: number;
  platform: string;
  scrollLeftInPx: number;
  scrollTopInPx: number;
  widthInPx: number;
}

Example how to call it:

{
  "data": {},
  "type": "SEAT_MAP__DATA"
}

Example of data parent layer receives:

{
  "data": {
    "deviceDPI": 288,
    "devicePixelRatio": 3,
    "heightInPx": 736,
    "planeId": 1719,
    "platform": "ios",
    "scrollLeftInPx": 0,
    "scrollTopInPx": 1456,
    "widthInPx": 414
  },
  "type": "SEAT_MAP__DATA"
}

 

WANNA_SEE_VR

This message gets bubbled up to the parent layer when 360° icon is clicked inside the seatmap. Parent layer shall implement logic to display 360° panorama.

360° icon within seatmap

Interface describing data received by the parent layer:

interface IData {
  fragment: string; // Filename of the aerial panorama photo
}

Example of data parent layer receives via WANNA_SEE_VR message bubbled up from the embed seatmap:

{
  "data": {
    "fragment": "6N"
  },
  "type": "WANNA_SEE_VR"
}

 

WANNA_CLOSE_SEATMAP

Bubbles up to the parent layer when user wishes to close the seatmap and get back to the previous screen. Parent layer shall implement logic to close seatmap, returning user back to the previous state.

Optionally, there are some controls within the seatmap, that lets user emit message with this message.

Example of data parent layer receives:

{
  "data": {},
  "type": "WANNA_CLOSE_SEATMAP"
}

 

SYNC_PASSENGERS

This message works both directions (bidirectional):

  • When posted from parent layer, it enables passengers allocation within seatmap.
  • Seatmap emits this message, when particular passenger picks the seat. Actually, every time when passenger picks its seat or preferences, seatmap emits this message with updated list of passengers.

Interfaces, describing data types:

interface IAllocatablePassengers {
  readonly flightGuid: string;
  readonly passengers: IPassenger[];
}

interface IPassenger {
  readonly id: string;
  seatPreference: ISeatPreference;
  seat: ISeat;
}

interface ISeatPreference {
  cabinSection: TCabinSection;
  seatPosition: TSeatPosition;
  seatLabel: string;
}

interface ISeat {
  price: number;
  seatLabel: string;
}

type TCabinSection = 'NOSE' | 'MIDDLE' | 'TAIL';
type TSeatPosition = 'WINDOW' | 'MIDDLE' | 'AISLE';

Example of data when SYNC_PASSENGERS message is fired:

{
  "data": {
    "flightGuid": "1ABC",
    "passengers": [
      {
        "id": "1",
        "seatPreference": null,
        "seat": null
      },
      {
        "id": "2",
        "seatPreference": null,
        "seat": {
          "price": 0,
          "seatLabel": "12F"
        }
      },
      {
        "id": "3",
        "seatPreference": {
          "cabinSection": "TAIL",
          "seatPosition": "WINDOW",
          "seatLabel": "2A"
        },
        "seat": null
      }
    ]
  },
  "type": "SYNC_PASSENGERS"
}

Or even like this:

{
  "data": {
    "flightGuid": "1ABC",
    "passengers": [
      { "id": "1" },
      { "id": "2" },
      { "id": "3" }
    ]
  },
  "type": "SYNC_PASSENGERS"
}

Please notice, that seatmap identifies how many passengers to allocate by a length of passengers array. Therefore, for example, to allocate 2 passengers without any predefined seat or its preferences, passengers array shall contain 2 items.


 

SEAT_AVAILABILITY_AVAILABLE

Parent layer is able to post this message to the embed seatmap, guiding seatmap to enable some seats for reservation, while disable others.

Interfaces, describing data types:

interface IIncomingAvailibility {
  errors: string;
  results: IIncomingResult[];
}

interface IIncomingResult {
  availableClasses: IIncomingClass[];
  notAvailableClasses: IIncomingClass[];
}

interface IIncomingClass {
  classCode: string;
  seats: IIncomingSeat[];
}

interface IIncomingSeat {
  currency: string;
  label: string;
  price: number;
}

Example of data posted from parent layer to the seatmap via SEAT_AVAILABILITY_AVAILABLE message:

{
  "data": {
    "errors": "",
    "results": [
      {
        "availableClasses": [
          {
            "classCode": "E",
            "seats": [
              {
                "currency": "USD",
                "label": "12E",
                "price": 33
              },
              {
                "currency": "USD",
                "label": "12F",
                "price": 13
              }
            ]
          }
        ]
      }
    ]
  },
  "type": "SEAT_AVAILABILITY_AVAILABLE"
}

You can pass seat with label "*". This label work like all selector for seats. In example below all seats with classCode equals "E" will be enabled with price of 50. However next seat configuration "12F" will override price and set it to 75.

{
  "data": {
    "errors": "",
    "results": [
      {
        "availableClasses": [
          {
            "classCode": "E",
            "seats": [
              {
                "currency": "USD",
                "label": "*",
                "price": 50
              },
              {
                "currency": "USD",
                "label": "12F",
                "price": 75
              }
            ]
          }
        ]
      }
    ]
  },
  "type": "SEAT_AVAILABILITY_AVAILABLE"
}

 

DECK__SWITCH

This event can be sent to embed seat map to switch plane deck. When event is sent field deckId is in priority. It should be numeric field equals 1 or 2. Currently there is only double-deck aircrafts exists. If deckId is not defined or not valid application attempts to select deck by seatLabel.

Interfaces:

interface IEventSwitchDeck {
  readonly data: {
    readonly deckId?: number; // 1 or 2 and only if plane has second deck
    readonly seatLabel?: string;
  };
  readonly type: string;
}

Example of data posted from parent layer to the seat map via DECK__SWITCH message:

{
  "data": {
    "deckId": 2, // this field is priority
    "seatLabel": "14D"
  },
  "type": "SWITCH_DECK"
}

 

SEAT__JUMP_TO

The event is called from the outside from parent layer. This event switch view to plane deck on which seat is located. Then try to scroll (jump) possibly close to the seat trying to fit into center of view. The seat may not be scrolled completely to the center depending on the layout or scroll position. If tooltip option is passed reservation window will be shown and view will be scrolled to fit it in. effect responsible for animation during jump. If ``

Data:

Name Type Required Default Possible values
label string ✔️
tooltip boolean ✖️ false false, true
effect string ✖️ "none" "none" "linear" "swing"
duration integer ✖️ 400

Interfaces:

interface IEventSeatJumpTo {
  readonly data: {
    readonly label: string;
    readonly tooltip?: boolean;
    readonly effect?: string;
    readonly duration?: number;
  };
  readonly type: string;
}

Example of data posted from parent layer to the seat map via SEAT__JUMP_TO message:

Instant jump to seat

{
  "data": {
    "label": "18E",
    "tooltip": true,
  },
  "type": "SEAT__JUMP_TO"
}

Motion scrolling to seat in half a second

{
  "data": {
    "label": "18E",
    "tooltip": true,
    "effect": "swing",
  },
  "type": "SEAT__JUMP_TO"
}

Linear scroll speed to seed in 600 ms.

{
  "data": {
    "label": "18E",
    "tooltip": true,
    "effect": "linear",
    "duration": 600
  },
  "type": "SEAT__JUMP_TO"
}

 

SEAT_MAP__SCROLL_TO

Since version 0.4.0.

This event post to parent layer information about scroll position changes.

Message has 250ms delay (debounce) after event happened.

If it's Airbus A380 build or color theme of URL parameter is set to &iflya380=true - no scroll changes will be made and only message will posted to parent layer.

Interfaces:

export interface IEventSeatMapScrollToData {
  /**
   * @description Element height to fit into view. If set and above zero, height might be important.
   * @default 0
   * @readonly
   * @type number
   */
  readonly height?: number;
  /**
   * @description Element width to fit into view. If set and above zero, width might be important.
   * @default 0
   * @readonly
   * @type number
   */
  readonly width?: number;
  /**
   * @description Vertical position of the scroll bar that should be set
   * @readonly
   * @type number
   */
  readonly x: number;
  /**
   * @description Horizontal position of the scroll bar that should be set
   * @readonly
   * @type number
   */
  readonly y: number;
}

export interface IEventSeatMapScrollTo extends IEvent {
  readonly data: IEventSeatMapScrollToData;
  readonly type: MessageType;
}

Example of data posted to parent layer to the seat map via SEAT_MAP__SCROLL_TO message:

{
  "data": {
    "x": 138.15,
    "y": 4074
  },
  "type": "SEAT_MAP__SCROLL_TO"
}
{
  "data": {
    "height": 292,
    "width": 940.890625,
    "x": 0,
    "y": 4074
  },
  "type": "SEAT_MAP__SCROLL_TO"
}

 

SEAT__PRESSED

Interfaces:

export interface IEventSeatPressed {
  readonly classLetter: string;
  readonly deckLevel: number;
  readonly fragment: string;
  readonly label:string;
  readonly type: string;
  readonly x: number;
  readonly y: number;
}

Example of data posted to parent layer to the seat map via SEAT__PRESSED message:

{
  "data": {
    "classLetter": "E",
    "deckLevel": 1,
    "fragment": "234876",
    "label": "60B",
    "type": "Economy",
    "x": 55.8,
    "y": 1108.88
  },
  "type": "SEAT__PRESSED"
}

   

Initialisation and workflow

  • When seatmap is rendered and communication is set up, seatmap will be optionally waiting for SEAT_AVAILABILITY_AVAILABLE and SYNC_PASSENGERS messages. After receiving first message, seatmap is going to disable some seats, keeping others enabled. When second message arrives, seatmap is going unlock user with an opportunity to pick seat for reservation for every given passenger.

  • Every time user picks particular seat for reservation, seatmap emits SYNC_PASSENGERS message back to the parent layer. Message contains information with picked seat for every initialised passenger.

  • If SYNC_PASSENGERS message arrived, seatmap displays horizontal bar at the top. It contains ← icon (back button). When clicked, seatmap emits WANNA_CLOSE_SEATMAP message up to the parent layer, signalling user wishes to close the seatmap and return back to the previous view.

  • Some seatmaps are going to contain 360° dots and VR buttons within seat tooltip. When user clicks these, seatmap emits WANNA_SEE_VR message.

About

Documents that describe Quicket vector seatmap integration

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published