JavaScript API functions

Switch to

JavaScript API version 12

Changelog

Added: - Retrieve a summary for one or more courses ShowpadLib.getCoursesByIds().

Usage

The methods listed below are all available on the window.ShowpadLib object after page load. Simply call the desired method with the correct parameters.

When window.ShowpadLib is created and ready to go, the method window.onShowpadLibLoaded() will be called (if it exists).

Have a look at the examples to get some more context.

Methods

All methods returning a boolean will return true when the call was executed successfully and return false when something went wrong. The method returns false when a required parameter was not set or when the method was called again before the previous method was completed. Some methods return full objects or single values like getUserInfo(), upload() or getVersion()

getVersion

Returns the version of the integration. If this function does not exist, we can assume that version 2 is available.


let version = null;
if (typeof window.ShowpadLib.getVersion === 'function') {
    version = window.ShowpadLib.getVersion();
}
else {
    version = 2;
}

getUserInfo

Returns the information of the active user.

const userInfo = window.ShowpadLib.getUserInfo();

// userInfo will look like this:
const userInfo = {
    email: 'user@company.com',
    id: '123',
    user_name: 'user@company.com',
    full_name: 'Firstname Lastname'
};

getDeviceInfo

Returns the information of the current device. The locale value is a 2-letter code following the ISO 639-1 standard.

The app is one of the following values: web, ios, android, windows. It represents the app where this HTML Content is currently being viewed.

The version is a version code for the app where this HTML content is being viewed.

const deviceInfo = window.ShowpadLib.getDeviceInfo();

// deviceInfo will look like this:
const deviceInfo = {
    locale: 'en', // language of the device, following ISO 639-1
    app: 'web', // the app,
    version: '2' // the app version
};

hasFeature

Use this method to know if a specific feature is supported on the app your content is running in.

This method will (synchronously) return:

The list of features can be found in the table below.

Key Description
shareEmail Share content by sending an email. Useful in combination with the share() method
shareLink Share content by generating a link. Useful in combination with the share() method
collections Add content to a Collection. Useful in combination with the addAssetsToCollection() method
collections Add content to multiple Collections. Useful in combination with the addAssetsToCollections() method
sharedSpaces Add content to a Shared Space. Useful in combination with the addAssetsToSharedSpace() method
sharedSpaces Add content to multiple Shared Space. Useful in combination with the addAssetsToSharedSpaces() method
if (window.ShowpadLib.hasFeature('shareLink')) {
    // The feature "shareLink" is supported on this platform.
}

trackEvent

Track an event in your Experience App. This is used to track how conversations are happening within the experience. The events currently aren’t visualized in the Online-Platform but can be exported through Showpad’s Export API.

The method accepts a single event or an array of events. The order of the array is the order they are tracked in. In principle there are no constraints on the event format, but we recommend to adhere to the formats as shown in the example below, for compatibility with potential future reporting:


// PAGEVIEW
const pageview = {
  "type": "pageview",
  "touchpoint": {
    "id": "eventID",
    "name": "Event name",
    "sourceId": "pageID",
    "sourceName": "Page name",
    "destinationId": "pageID",
    "destinationName": "Page name",
    "fields": {
      "pageviewDepth": "category A/item B/detail C", // Path representing the depth of the page (in this example, you could compare on category level, item level and detail level)
      "pageviewType": "tab" // How the page is displayed (page, tab, slide, ...)
    }
  }
};

// EVENT
const event = {
  "type": "event",
  "touchpoint": {
    "id": "eventID",
    "name": "Event name",
    "sourceId": "pageID",
    "sourceName": "Page name",
    "fields": {
      "eventCategory": "Video", // The object that was interacted with
      "eventAction": "play", // The type of interaction
      "eventLabel": "Fall Campaign", // Useful for categorizing events
      "eventValue": 42 // A numeric value associated with the event
    }
  }
};

ShowpadLib.trackEvent(event); // Track a single event
ShowpadLib.trackEvent([event, pageview]); // Track multiple events

share

Shows a modal view to share contents. The provided assets will be added so they can be shared.

The type of the modal is either email or link.

If some of the assets are not shareable (expired, deleted, unshareable, …), the modal will still be shown, but the callback function will be called with result partial. In this case, the Showpad client will show a warning message to the user that some content cannot be shared.

If none of the provided assets can be shared, a warning will be shown, but no sharing modal will be displayed and the result will be error

const assetSlugs = ['asset1-slug','asset2-slug'];
const type = 'email'; // Can be 'email' or 'link'.
if (window.ShowpadLib.share(type, assetSlugs, callbackFn)) {
    // Call went through, callbackFn will be called.
}
else {
    // Something went wrong or the given type is not supported on this app.
}

function callbackFn (result) {
    if (result == 'success') {
        // all assets were added to the share dialog and the dialog is shown
    }
    else if (result == 'partial') {
        // some assets were not added to the share dialog, because they could be expired, unshareable, deleted, ...
        // the share-dialog is shown
    }
    else if (result == 'error') {
        // no assets were added and the dialog is NOT shown
    }
    else {
      // null was returned, something went wrong
    }
}

getAssetPreviewUrl

Returns the url with which the asset preview image can be loaded. The assetId is always required. The assetSlug is also required on mobile.

const assetId = 'asset1-id';
const assetSlug = 'asset1-slug';
const size = 400;
const imageEl = document.getElementById('image');
imageEl.src = ShowpadLib.getAssetPreviewUrl(assetId, assetSlug, size);

// The asset preview image should now be loaded in the image element.

Sizes

From and including: iOS 6.5, Windows v1.6.12
For all Android versions and before iOS 6.5, Windows v1.6.12

getAssetsByTags

Invokes the callback function with a list of assets that have all the given tags. This function works offline.

const tags = ['Tag 1','screen-15','nl'];
if (ShowpadLib.getAssetsByTags(tags, callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (assets) {
    if (assets) {
        // Assets might look like this:
        const assets = [
                {
                    "id": "abc123", // Local id to the device
                    "slug": "bdfklje739v930g0d",
                    "name": "my_brochure.pdf",
                    "displayName": "My Brochure fo 2017.pdf",
                    "type": "document",
                    "description": "A description", // Can be null
                    "permissions": {
                        "share": true, // File can be shared
                        "annotate": true, // File can be annotated
                        "download": true // File can be downloaded
                    },
                    "tags": ["Tag 1","screen-15","nl"],
                    "previewUrl" : "https://endpoint/asset/abc123/preview", // Same as response from getAssetPreviewUrl
                },
                ...
            ];
    }
    else {
      // null was returned, something went wrong
    }
}

getAssetsInFolder

Invokes the callback function with a list of assets that can be found in the given folder. This function works offline.

Note: this will not work recursively and will only return actual assets. So it doesn’t return values from deeper levels nor will it return folders.

const folder = 'xyz2250'; // last part from showpad://folder/xyz2250, this can be the id or slug.
if (ShowpadLib.getAssetsInFolder(folder, callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (assets) {
    if (assets) {
        // Assets might look like this:
        const assets = [
                {
                    "id": "abc123", // Local id to the device
                    "slug": "bdfklje739v930g0d",
                    "name": "my_brochure.pdf",
                    "displayName": "My Brochure fo 2017.pdf",
                    "type": "document",
                    "description": "A description", // Can be null
                    "permissions": {
                        "share": true, // File can be shared
                        "annotate": true, // File can be annotated
                        "download": true // File can be downloaded
                    },
                    "tags": ["Tag 1","screen-13"],
                    "previewUrl" : "https://endpoint/asset/abc123/preview", // Same as response from getAssetPreviewUrl
                },
                ...
            ];
    }
    else {
      // null was returned, something went wrong
    }
}

getAssetsByQuery

Invokes the callback function with a list of assets that match with the given query. This function works offline and thus cannot search within documents.

const query = 'onepager';
if (ShowpadLib.getAssetsByQuery(query, callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (assets) {
    if (assets) {
        // Assets might look like this:
        const assets = [
                {
                    "id": "abc123", // Local id to the device
                    "slug": "bdfklje739v930g0d",
                    "name": "featured_product_onepager.pdf",
                    "displayName": "My Brochure fo 2017.pdf",
                    "type": "document",
                    "description": "A description", // Can be null
                    "permissions": {
                        "share": true, // File can be shared
                        "annotate": true, // File can be annotated
                        "download": true // File can be downloaded
                    },
                    "tags": ["Tag 1","screen-15","nl"],
                    "previewUrl" : "https://endpoint/asset/abc123/preview", // Same as response from getAssetPreviewUrl
                },
                ...
            ];
    }
    else {
      // null was returned, something went wrong
    }
}

getAssetFileUrl

Returns the url with which the asset file (the video, image, pdf,…) can be fetched. The assetId is always required. The assetSlug is also required on mobile.

const assetId = 'asset1-id';
const assetSlug = 'asset1-slug';
const videoEl = document.getElementById('video');
videoEl.src = ShowpadLib.getAssetFileUrl(assetId, assetSlug);

// The asset data should now be loaded in the video element.

getCollections

Invokes the callback function with a list of Collection data ([{ id, name }]) or null when something went wrong.

if (window.ShowpadLib.getCollections(callbackFn)) {
    // Call went through, callbackFn will be called.
}
else {
    // Something went wrong
}

function callbackFn (collections) {
    if (collections) {
        // Collections might look like this:
        const collections = [
                {
                    id: '1',
                    name: 'Collection Name 1',
                },
                {
                    id: '2',
                    name: 'Collection Name 2'
                }
            ];
    }
    else {
      // null was returned, something went wrong
    }
}

openCollection

Shows a modal view with the contents of the Collection. This view allows for the Collection to be shared

const collectionId = '...' ;
if (window.ShowpadLib.openCollection(collectionId)) {
    // Modal will be opened
}
else {
    // Something went wrong
}

createCollection

Creates a new Collection with the specified name, will invoke the callback function with the id of the new Collection or null when an error occured

const name = 'My Collection Name';
if (window.ShowpadLib.createCollection(name, callbackFn)) {
    // Call went through, callbackFn will be called.
}
else {
    // Something went wrong
}

function callbackFn (collectionId: string | null) {
    if (collectionId) {
        // Do something with collection id
    }
    else {
       // Something went wrong
    }
}

addAssetsToCollection

Shows the modal which prompts the user the select the Collection to add the assets to.
Will call the callback method with the id of the Collection the assets were added to or null if the flow was canceled or an error occured

const assetSlugs = [];
assetSlugs.push('asset1-slug');
assetSlugs.push('asset2-slug');
if (window.ShowpadLib.addAssetsToCollection(assetSlugs, callbackFn)) {
    // Call went through, modal will be shown.
    // callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (collectionId: string | null) {
    if (collectionId) {
        // Assets were added to the collection with provided collectionId
    }
    else {
       // Something went wrong or the user canceled the flow or
       // none of the given asset-slugs were found.
    }
}

addAssetsToCollections

Shows the modal which prompts the user the select Collections to add the assets to.
The provided callback method accepts 2 parameters. The first parameter is an error if something occured or null in case of success. The second paramter is a list of ids of the Collections that assets where added to.

const assetSlugs = [];
assetSlugs.push("asset1-slug");
assetSlugs.push("asset2-slug");
if (window.ShowpadLib.addAssetsToCollections(assetSlugs, callbackFn)) {
  // Call went through, modal will be shown.
  // callbackFn will be called
} else {
  // Something went wrong
}

function callbackFn(error: Error | null, collectionIds?: string[]) {
  if (error === null) {
    // Assets were added to the Collections with provided collectionIds
  } else {
    // Something went wrong or the user canceled the flow or none of the given asset-slugs were found.
    // error will contain an error with more information.
  }
}

addAssetsToCollectionWithId

Will add the given assets to the Collection with the given id. If the Collection does not exist, nothing will happen.

const collectionId = '...';
const assetSlugs = ['asset1-slug', 'asset2-slug'];
if (window.ShowpadLib.addAssetsToCollectionWithId(collectionId, assetSlugs, callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (collectionId: string | null) {
    if (collectionId) {
        // Assets were added to the collection with provided collectionId
    }
    else {
       // Something went wrong or the user canceled the flow
    }
}

clearCollection

Removes all the items from the given Collection

const collectionId = '...';
if (window.ShowpadLib.clearCollection(collectionId)) {
    // Call went through, items will be cleared
}
else {
    // Something went wrong
}

addAssetsToSharedSpace

Shows the modal which prompts the user to select the Shared Space to add the assets to.
Will call the callback method with the id of the Shared Space the assets were added to or null if the flow was canceled or an error occured

const assetSlugs = [];
assetSlugs.push('asset1-slug');
assetSlugs.push('asset2-slug');
if (window.ShowpadLib.addAssetsToSharedSpace(assetSlugs, callbackFn)) {
    // Call went through, modal will be shown.
    // callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (sharedSpaceId: string | null) {
    if (sharedSpaceId) {
        // Assets were added to the Shared Space with provided sharedSpaceId
    }
    else {
       // Something went wrong or the user canceled the flow or
       // none of the given asset-slugs were found.
    }
}

addAssetsToSharedSpaces

Shows the modal which prompts the user to select multiple Shared Space to add the assets to.
The provided callback method accepts 2 parameters. The first parameter is an error if something occured or null in case of success. The second paramter is a list of ids of the Shared Spaces that assets where added to.

const assetSlugs = [];
assetSlugs.push('asset1-slug');
assetSlugs.push('asset2-slug');
if (window.ShowpadLib.addAssetsToSharedSpaces(assetSlugs, callbackFn)) {
    // Call went through, modal will be shown.
    // callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (error: Error | null, sharedSpaceIds?: string[]) {
    if (error === null) {
        // Assets were added to the Shared Spaces with provided sharedSpaceIds
    }
    else {
        // Something went wrong or the user canceled the flow or none of the given asset-slugs were found.
        // error will contain an error with more information.
    }
}

getShowpadApi

Invokes the callback function with an object with the required information to do an HTTP request to the Showpad API.

If the Showpad API returns a 401, it means your access token has expired, and you’ll need to request a new one. Do this using the refreshShowpadApi() method.

if (window.ShowpadLib.getShowpadApi(callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (apiConfig) {
    if (apiConfig) {
        // apiConfig can look like this:
        const apiConfig = {
            accessToken: 'xyz',
            url: 'https://subdomain.showpad.biz', // no trailing slash
            error: null // or a string with the actual error
        };

        if (apiConfig.accessToken && apiConfig.url) {
            // Do an api-call
        }
        else if (apiConfig.error) {
            if (apiConfig.error === 'unavailable') {
                // no api key available
            }
            else {
                // something else went wrong
            }
        }
        else {
            // Something went wrong
        }
    }
    else {
       // Something went wrong or the user canceled the flow
    }
}

refreshShowpadApi

This method will refresh the Showpad access token and call the callback function with the same object as the getShowpadApi call

function doApiCall (url, config) {
    fetch(url, config)
            .then(response => {
                if (response.status < 200 || response.status >= 400) {
                    const error = new Error();
                    error.statusCode = response.status;
                    throw error;
                }
            })
            .catch(onApiCallFailed);
}
function onApiCallFailed (error) {
    if (error.statusCode === 401) {
        // Unauthorized, let's refresh the token
        refreshToken(url, config);
    }
}

function refreshToken (url, config) {
    if (window.ShowpadLib.refreshShowpadApi(apiConfig => {})) {
        // Call went through, callbackFn will be called
    }
    else {
        // Something went wrong
    }

    function callbackFn (apiConfig) {
        if (apiConfig) {
            // apiConfig can look like this:
            const apiConfig = {
                accessToken: 'xyz',
                url: 'https://subdomain.showpad.biz', // no trailing slash
                error: null // or a string with the actual error
            };

            if (apiConfig.accessToken && apiConfig.url) {
                // Refresh succeeded, retry the failed call.
                config.headers['Authorization'] = `Bearer ${apiConfig.accessToken}`;
                doApiCall(url, config);
            }
            else if (apiConfig.error) {
                if (apiConfig.error === 'unavailable') {
                    // no api key available
                }
                else if (apiConfig.error === 'expired') {
                    // Refresh Token has expired, no further calls can be made.
                }
                else {
                    // Something went wrong
                }
            }
            else {
                // Something went wrong
            }
        }
        else {
           // Something went wrong
        }
    }
}

getSalesforceApi

Invokes the callback function with an object with the required information to do an HTTP request to the Salesforce API.

If the Salesforce API returns a 401, it means your access token has expired, and you’ll need to request a new one. Do this using the refreshSalesforceApi() method.

if (window.ShowpadLib.getSalesforceApi(callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (apiConfig) {
    if (apiConfig) {
        // apiConfig can look like this:
        const apiConfig = {
            accessToken: 'xyz',
            url: 'https://login.salesforce.com', // no trailing slash
            error: null // or a string with the actual error
        };

        if (apiConfig.accessToken && apiConfig.url) {
            // Do an api-call to Salesforce
        }
        else if (apiConfig.error) {
            if (apiConfig.error === 'unavailable') {
                // no Salesforce access token available
                // Possibly, the user is not connected to Salesforce.
            }
            else {
                // something else went wrong
            }
        }
        else {
            // Something went wrong
        }
    }
    else {
       // Something went wrong or the user canceled the flow
    }
}

Particularities with the Salesforce API and HTML Content

To call an api on a different domain (eg. “https://login.salesforce.com”), that service needs to have CORS enabled. More info about CORS or (Cross-Origin Resource Sharing) can be found here .

Salesforce has a specific settings-page to enable specific domains for your Salesforce instance. Salesforce requires this to be an “https://”-url. The problem is that your HTML Content is also rendered in our mobile apps, and they don’t use an “https://”-scheme, but something specific for the platform (eg. “ms-appx-package://” on Windows).

So calling the Salesforce API directly from your HTML Content, will probably result in a CORS error by Salesforce.

To circumvent this, we suggest to proxy all api-calls to Salesforce through an iframe. This iframe would link to a webpage hosted on a CORS-enabled domain. The javascript in the HTML Content would just call some javascript in that iframe, which would send it through to Salesforce and send the response back. The communication between the HTML Content and the iframe needs to happen with “postMessage” https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage.

The diagram below might make things more clear.

refreshSalesforceApi

This method will refresh the Salesforce access token and call the callback function with the same object as the getSalesforceApi call.

function doSalesforceApiCall (url, config) {
    fetch(url, config)
            .then(response => {
                if (response.status >= 200 && response.status < 300) {
                    return response;
                }
                const error = new Error();
                error.statusCode = response.status;
                throw error;
            })
            .catch(onSalesforceApiCallFailed);
}
function onSalesforceApiCallFailed (error) {
    if (error.statusCode === 401) {
        // Unauthorized, let's refresh the token
        refreshSalesforceToken(url, config);
    }
}

function refreshSalesforceToken (url, config) {
    if (window.ShowpadLib.refreshSalesforceApi(apiConfig => {})) {
        // Call went through, callbackFn will be called
    }
    else {
        // Something went wrong
    }

    function callbackFn (apiConfig) {
        if (apiConfig) {
            // apiConfig can look like this:
            const apiConfig = {
                accessToken: 'xyz',
                url: 'https://login.salesforce.com', // no trailing slash
                error: null // or a string with the actual error
            };

            if (apiConfig.accessToken && apiConfig.url) {
                // Refresh succeeded, retry the failed call.
                config.headers['Authorization'] = `Bearer ${apiConfig.accessToken}`;
                doApiCall(url, config);
            }
            else if (apiConfig.error) {
                if (apiConfig.error === 'unavailable') {
                    // no Salesforce access token available
                }
                else if (apiConfig.error === 'expired') {
                    // Salesforce Refresh Token has expired, no further calls can be made.
                }
                else {
                    // Something went wrong
                }
            }
            else {
                // Something went wrong
            }
        }
        else {
           // Something went wrong
        }
    }
}

upload

Uploads a single file to “My files”. Returns an EventEmitter which emits the upload status.

A file can be a File object or an ArrayBuffer object.


const filename = 'filename.txt';
const file = new File(['file contents'], filename);

const upload = {
    file: file,
    filename: filename
};

const statusEmitter = window.ShowpadLib.upload(upload);

The EventEmitter will emit seperate events for each specific type of progress the upload goes through.

Events

queued

Emitted when the host received the upload but has not yet started the upload.

Fires once

statusEmitter.on('queued', () => {
    // Do something when the upload is queued
});
uploading

Emitted when the upload has progressed and reports on the amount of bytes sent.

Can fire multiple times

statusEmitter.on('uploading', (data) => {
    // Do something when uploading
});

with the data object containing

data = {
  bytesTotal: 1000, // The total number of bytes to upload
  bytesSent: 500 // The number of bytes uploaded
}
processing

The upload is succesfully transferred to the server and the file is now being processed.

Fires once

statusEmitter.on('processing', () => {
    // Do something when processing
});
success

The upload has been successfully uploaded and is processed.

Fires once

statusEmitter.on('success', (data) => {
    // Do something when the upload succeeded
});

With the object containing

data = { 
    asset: {
        "id": "abc123",
        "slug": "bdfklje739v930g0d",
        "name": "my_brochure.pdf",
        "displayName": "My Brochure fo 2017.pdf",
        "type": "document",
        "description": "A description",
        "permissions": {
            "share": true,
            "annotate": true,
            "download": true
        },
        "tags": ["Tag 1","screen-15","nl"],
        "previewUrl" : "https://endpoint/asset/abc123/preview"
    }
}
failed

At some point the upload failed.

Fires once

statusEmitter.on('failed', (error) => {
    // Do something when upload failed
});

with the error object containing

error = {
  reason: "A wrong filetype was submitted" 
}

displayModal

displays a native modal with a title, text and clickable buttons.

title to specify a title for the modal

text the text contents displayed in the modal

buttons an array of buttons to display

A button must have 2 fields named reason and text and possibly one optional field called tinted

reason is a text field that is passed back in the callback whenever a buttons is clicked.

text is the text to display on the button

tinted is an optional parameter. When set to true the button has a distinctive seperate color

window.ShowpadLib.displayModal({
    title: "The modal title",
    text: "The modal text",
    buttons: [
        {reason: 'cancel', text: 'Cancel'},
        {reason: 'ok', text: 'OK', tinted: true}
        ...
    ]
}, (reason) =>  {
    // Modal closed with reason
});

displayToast

Display a native toast

text is the text to be displayed in the toast

actionText the text to be used on the action button

type must be one of info|success|error.

Depending on the type the toast color will be blue|green|red.

window.ShowpadLib.displayToast({
    type: "info",
    text: "The text displayed in the toast",
    actionText: "click me",
}, (reason) => {
    // Toast has been closed with reason
});

reason can be timeout or clicked depending if the user clicked before a timeout occured.

getCoursesByIds

Invokes the callback function with a list of course summaries that match with the given courseIds.

const courseIds = [
    "A943324A8E9A405CA95C5DF55E4BD71A",
    "182D49E57B534C84B7431D55483400C4"
];
ShowpadLib.getCoursesByIds(courseIds, callbackFn);

function callbackFn (error, courseSummaries) {
    if (error) {
        // Error occured, handle it
    }
    else {
        // Response might look like this:
        const courses = [
            {
                "assignment": {
                    "assignedAt": "2020-01-01T01:01:01.001Z",
                    "dueAt": "2020-01-01T01:01:01.001Z",
                    "status": "REQUIRED"
                },
                "course": {
                    "description": "asfadsf",
                    "id": "0A5098512C5042CDB794BF1D1E6053AC",
                    "owner": {
                        "firstName": "Ruth",
                        "id": "15b582a012674557a1f69d455f69bb17",
                        "lastName": "Elliott"
                    },
                    "thumbnail": {
                        "assetId": "9069c2760c08efecef3eaf799a610cb5",
                        "checksum": "159657c3ecd1c01a8af41cda9b03384b",
                        "offsetX": 0.5,
                        "offsetY": 0.5,
                        "url": "https://example.showpad.biz/api/v3/assets/2d5d23fc8cef6da5ffea2613a8382c29/preview/v1"
                    },
                    "title": "Example Course"
                },
                "courseLinkUrl": "/webapp2/courses/0A5098512C5042CDB794BF1D1E6053AC",
                "isBookmarked": true,
                "prerequisite": {
                    "assignment": {
                        "assignedAt": "2022-10-11T20:42:32.000Z"
                    },
                    "course": {
                        "id": "F4B00C314A914267A04E648F8144F4BA",
                        "title": "An Example Pre"
                    },
                    "progress": {
                        "completedAt": "2022-10-12T10:35:28.000Z"
                    }
                },
                "progress": {
                    "completedAt": "2020-01-01T01:01:01.001Z",
                    "lastActivityAt": "2020-01-01T01:01:01.001Z",
                    "isOptedIn": true,
                    "remainingAttempts": 3,
                    "startedAt": "2020-01-01T01:01:01.001Z",
                    "timeRemainingInSeconds": 80
                }
            },
            ...
        ];
    }
}

AppsDB

Prerequisites

Create a store

Before being able to use an AppsDB store you first have to create it using our REST API. You can find the documentation on how to do that here.

Device Time

Syncing data to our mobile apps relies on a correct device time. If the device time does not match the actual time, data might be synced out of order or be blocked. When this occurs, the apps will try to sync again at a later point in time.

getStoreEntryValue

Retrieve a value from a store entry.


const store = 'my-store';
const entryId = 'my-key';

if (ShowpadLib.getStoreEntryValue(store, entryId, callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (value) {
    if (value) {
        console.log(value);
    }
    else {
      // null was returned, something went wrong
    }
}

getGlobalStoreEntryValue

Retrieve a value from a global store entry.


const store = 'my-store';
const entryId = 'my-key';

if (ShowpadLib.getGlobalStoreEntryValue(store, entryId, callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (value) {
    if (value) {
        console.log(value);
    }
    else {
      // null was returned, something went wrong
    }
}

setStoreEntryValue

Set a value on a store entry. The callback is optional.


const store = 'my-store';
const entryId = 'my-key';
const value = 'entry-value';

if (ShowpadLib.setStoreEntryValue(store, entryId, value, callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (value) {
    if (value) {
        console.log(value);
    }
    else {
      // null was returned, something went wrong. 
    }
}

deleteStoreEntry

Delete store entry. The callback is optional.


const store = 'my-store';
const entryId = 'my-key';

if (ShowpadLib.deleteStoreEntry(store, entryId, callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (success) {
    if (success) {
        console.log("Delete succeeded");
    }
    else {
      // null was returned, something went wrong. 
    }
}

getStoreEntries

Get all entries from a store. The results from this call are paginated.


const store = 'store-name';

if (ShowpadLib.getStoreEntries(store, callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (paginatedEntries) {
    if (paginatedEntries) {
        console.log(paginatedEntries);
    }
    else {
      // null was returned, something went wrong. 
    }
}

With an entry list returned of following structure

{
    cursor: "optional-cursor-string", // See the "getStoreEntries and getGlobalStoreEntries pagination" section for more information about this.
    entries: [ 
        {
            id: "entry-key-1",
            storeId: "my-store-id",
            value: "entry-value-1"
        },
        {
            id: "entry-key-2",
            storeId: "my-store-id",
            value: "entry-value-2"
        },
        ...
    ]
}

getGlobalStoreEntries

Get all global entries from a store. The results from this call are paginated.


const store = 'store-name';

if (ShowpadLib.getGlobalStoreEntries(store, callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (paginatedEntries) {
    if (paginatedEntries) {
        console.log(paginatedEntries);
    }
    else {
      // null was returned, something went wrong. 
    }
}

With an entry list returned of following structure

{
    cursor: "optional-cursor-string", // See the "getStoreEntries and getGlobalStoreEntries pagination" section for more information about this.
    entries: [ 
        {
            id: "entry-key-1",
            storeId: "my-store-id",
            value: "entry-value-1"
        },
        {
            id: "entry-key-2",
            storeId: "my-store-id",
            value: "entry-value-2"
        },
        ...
    ]
}

getStoreEntries and getGlobalStoreEntries pagination

Both getStoreEntries() and getGlobalStoreEntries() calls are paginated.

Meaning whenever the requested entry-list has too many entries(limit) or is too large(>1MB) to be returned in one go, the function will return a cursor-string that can be passed to a subsequent call to get the remainder of the entries.

Example: paginated getStoreEntries() call with cursor

// entryList returned in a previous getStoreEntries() request
entryList = {
    cursor: 'example-cursor-string',
    entries: [...]
};

// Pagination object 
const pagination = {
    cursor: entryList.cursor
};

ShowpadLib.getStoreEntries(store, pagination, callbackFn)

In the above example the entryList represents the entry-list returned in a previous call that had more entries than could be returned in one go. To get the next page of entries we do a second call passing in the cursor to indicate we want all entries after the already retrieved entry-list;

When dealing with very large entry-lists (multiple MB or thousands of entries) it is possible multiple calls are needed to get all the data in store.

Inversely when dealing with a large amount of small entries, you can set a limit on the maximum entries to be returned at once.


// Pagination object 
const pagination = {
    limit: 100
};

ShowpadLib.getStoreEntries(store, pagination, callbackFn)

In the above example we limit our output to a maximum of 100 entries.

When there are more than 100 entries to be returned or if the dataset is too large(>1MB), a cursor will be returned that can be used to fetch the next 100 entries.

Helper function to retrieve all entries from a store

When you simply want to fetch all entries from a store, you can make use of the following helper function.

    function getAllStoreEntries(storeId, callbackFn, cursor) {
        const pagination = { cursor: cursor };
        ShowpadLib.getStoreEntries(storeId, pagination,
            function getStoreEntriesCallback(entryList) {
                let allEntries = entryList.entries;
                if (entryList.cursor) {
                    getAllStoreEntries(storeId, function (entries) {
                        allEntries = allEntries.concat(entries);
                        callbackFn(allEntries);
                    }, entryList.cursor)
                } else {
                    callbackFn(allEntries);
                }
            }
        );
    }

    const store = 'store-name';

    getAllStoreEntries(store, function (entries) {
        console.log('all entries', entries);
    });