Tips and Tricks

Typescript support

How to get autocompletion for window.webxdc api in your IDE via Typescript.

Get the Typescript Definitions

Just copy webxdc.d.ts into your source dir:

type SendingStatusUpdate<T> = {
  /** the payload, deserialized json:
   * any javascript primitive, array or object. */
  payload: T;
  /** optional, short, informational message that will be added to the chat,
   * eg. "Alice voted" or "Bob scored 123 in MyGame";
   * usually only one line of text is shown,
   * use this option sparingly to not spam the chat. */
  info?: string;
  /** optional, if the Webxdc creates a document, you can set this to the name of the document;
   * do not set if the Webxdc does not create a document */
  document?: string;
  /** optional, short text, shown beside the icon;
   * it is recommended to use some aggregated value,
   * eg. "8 votes", "Highscore: 123" */
  summary?: string;
};

type ReceivedStatusUpdate<T> = {
  /** the payload, deserialized json */
  payload: T;
  /** the serial number of this update. Serials are larger than 0 and newer serials have higher numbers */
  serial: number;
  /** the maximum serial currently known */
  max_serial: number;
  /** optional, short, informational message. */
  info?: string;
  /** optional, if the Webxdc creates a document, this is the name of the document;
   * not set if the Webxdc does not create a document */
  document?: string;
  /** optional, short text, shown beside the webxdc's icon. */
  summary?: string;
};

interface Webxdc<T> {
  /** Returns the peer's own address.
   *  This is esp. useful if you want to differ between different peers - just send the address along with the payload,
   *  and, if needed, compare the payload addresses against selfAddr() later on. */
  selfAddr: string;
  /** Returns the peer's own name. This is name chosen by the user in their settings, if there is nothing set, that defaults to the peer's address. */
  selfName: string;
  /**
   * set a listener for new status updates.
   * The "serial" specifies the last serial that you know about (defaults to 0).
   * Note that own status updates, that you send with {@link sendUpdate}, also trigger this method
   * @returns promise that resolves when the listener has processed all the update messages known at the time when `setUpdateListener` was called.
   * */
  setUpdateListener(cb: (statusUpdate: ReceivedStatusUpdate<T>) => void, serial?: number): Promise<void>;
  /**
   * WARNING! This function is deprecated, see setUpdateListener().
   */
  getAllUpdates(): Promise<ReceivedStatusUpdate<T>[]>;
  /**
   * Webxdc are usually shared in a chat and run independently on each peer. To get a shared status, the peers use sendUpdate() to send updates to each other.
   * @param update status update to send
   * @param description short, human-readable description what this update is about. this is shown eg. as a fallback text in an email program.
   */
  sendUpdate(update: SendingStatusUpdate<T>, description: string): void;
}

////////// ANCHOR: global
declare global {
  interface Window {
    webxdc: Webxdc<any>;
  }
}
////////// ANCHOR_END: global

export { SendingStatusUpdate, ReceivedStatusUpdate, Webxdc };

you can find it also on https://github.com/webxdc/webxdc_docs/blob/master/webxdc.d.ts or just copy the code block above.

In the future this might become an @types npm module, but for now it is what it is: a simple file copy with no automatic updates.

Using types

Start by importing the file.

In Typescript:

import type { Webxdc } from './webxdc.d.ts'

In Javascript:

/**
 * @typedef {import('./webxdc').Webxdc} Webxdc
 */

This works in VS Code nicely together with the //@ts-check comment on top of your source file.

If you want you can also type your own functions using JSDoc comments.

If you don't use VS Code you can still make use of the type checking with the Typescript compiler:

npm i -g typescript # -g stands for global installation
tsc --noEmit --allowJs --lib es2015,dom *.js

Own Payload Type

If you have a type for your state update payloads, replace the any in Webxdc<any> with your own payload type:

declare global {
  interface Window {
    webxdc: Webxdc<any>;
  }
}

Transpile Newer Javascript With Babel.js

Older devices might not have the newest javascript features/syntax in their webview, you may want to transpile your code down to an older JavaScript version eg. with Babel.

Targets:

If you want to use a newer API make sure to check on https://caniuse.com. If you just want to use newer JavaScript syntax, babel.js is the right tool for you - it translates new JS into older JS, that can be interpreted.

Debugging With eruda.js

When you can not use debugging inside Deltachat, either because you have no computer to connect to or are on iOS, you can use eruda.js as an alternative to the native dev tools.

Installing eruda

Get eruda.js from https://github.com/liriliri/eruda, copy it next to your index.html and then add this snippet into the head section of your index.html, before all other scripts:

<script src="eruda.js"></script>
<script>
  eruda.init();
</script>

Using eruda

When you open the webxdc a floating button will appear in a corner, tap it to see the developer tools.

Debugging Inside Deltachat

Debug Your webxdc Content in Android via Chrome DevTools

  1. enable webView debugging in delta chat settings Settings > Advanced > Developer Mode: image of andvanced screen
  2. enable developer mode and ADB debugging on your device (go to system settings, device info, spam click on build number until there is a toast telling you that you are now a "Developer", then go into the developer menu that just appeared and enable "ADB debugging", see also android docs: Enable ADB debugging on your device).
  3. connect your device via USB to your computer
  4. open chromium (or google chrome) and go to chrome://inspect/#devices
  5. start your webxdc that you want to debug
  6. click on inspect:

screenshot of chrome dev tools device list

Inpect HTMLJavascript Console
dev tools inpectordev tools js console

Make sure to disable adb debugging again after you are done with debugging!

Debug Your webxdc Content in DeltaChat Desktop

Start the webxdc you want to debug and press F12 to open the developer tools:

screenshot of desktop webxdc window with devtool

A bit small isn't it? fix it either by resizing the window's width or undock the developer tools:

undock devtools

undock devtools

Optimizing Your App Icon

There are several things you can do to shrink down the size of your icon:

  • save without thumbnail image (in gimp it can be done in the export dialog)
  • shrink the image resolution (256px are enough, in some cases 128px or even lower like 64px can sufice)
  • change your PNG colors from RGB to Indexed (in gimp Image -> Mode -> Indexed, see https://docs.gimp.org/en/gimp-image-convert-indexed.html)

For png you can also use the oxipng tool (https://github.com/shssoichiro/oxipng), which automagically optimizes your icon's file size without quality loss:

oxipng icon.png -s -o max

If you have png files in your project, you should also do this them to safe even more bytes.

Noteworthy parameters:

  • --pretend only calculates gains
  • -Z even more compression, but takes longer
  • for more info see oxipng --help# Troubleshooting

I Cannot Share Variables on iOS Between Scripts!

Your code:

a.js

const CONFIG = { difficulty: "hard", hasCoins: true };

b.js

if (CONFIG.difficulty == "hard") {
  /** make the game harder **/
}

index.html

<html>
  <head>
    <!-- ... -->
  </head>
  <body>
    <!-- ... -->
    <script src="a.js"></script>
    <script src="b.js"></script>
  </body>
</html>

Basically you get many errors in your JS console like this:

Can't find variable: CONFIG

There are a few ways to solve this:

  • use a bundle to bundle all your JS to one file (some bundlers: parcel, webpack, esbuild)
  • use esm modules (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)
  • define your variables as inline script in your HTML file. (inline script means that the script is in the HTML file between the <script> tags: <script>my code</script>)
  • append your global variables to the window object: window.myVar = 1; and use them like console.log(window.myVar)