Toasts and Dialogs

Report a result or ask before acting.

A toast reports something without stopping the user, such as "Copied". A dialog asks before you go on, such as "Reset all settings?".

Quick Start

Each common case is one line. Text is a message key. A key with placeholders takes its values next, as with format.

import { dialogs, toasts } from '@unbound-app/api';

declare const name: string;
declare const error: unknown;

toasts.success('STARRED', { name });
toasts.info('SYNCING');
toasts.warning('OFFLINE', { duration: 0 });
toasts.error(error);
toasts.error('FAILED_TO_SYNC', error);

const confirmed = await dialogs.confirm('DELETE_TITLE', {
	titleVars: { name },
	body: 'DELETE_BODY',
	confirm: 'DELETE',
	destructive: true,
});

await dialogs.alert('SYNC_DONE', { body: 'SYNC_DONE_BODY' });

Reference

FunctionDoes
toasts.success, info, warning(key, vars?, options?)A toast with a check, info or warning icon
toasts.error(error)Logs the error, then shows "Something went wrong" with its message
toasts.error(key, vars?, error, options?)The same, titled by your key
toasts.show(options)A toast from text. Returns a handle
dialogs.confirm(key, options)Discord's alert with confirm and Cancel. Resolves true or false
dialogs.alert(key, options?)Discord's alert with one OK button. Resolves when closed
dialogs.showDialog(options)A dialog from text with your own buttons. Returns a handle with dismiss
dialogs.dismissAll()Closes every dialog your plugin opened

A shorthand's options sets icon (replacing the variant's), duration and id, as in show. Full signatures: toasts and dialogs in the SDK reference.

Typed Keys

Your editor knows each key and its placeholders from en-US.json:

import { toasts } from '@unbound-app/api';

toasts.success('SAVED');
toasts.success('SAVED_N', { count: 3 });

// @ts-expect-error Not one of your keys.
toasts.success('Saved!');
// @ts-expect-error SAVED_N needs { count }.
toasts.success('SAVED_N');
// @ts-expect-error SAVED has no placeholders.
toasts.success('SAVED', { count: 3 });

ubd build also reports a literal key that en-US.json lacks:

src/settings.tsx:12:17  'SAVD' is not a key in i18n/en-US.json.  Fix: add "SAVD" to i18n/en-US.json, or pass a key it has.

Error Toasts

toasts.error logs with your plugin's logger, then shows the error's message as the text. Pass whatever was thrown: an Error, a string or anything else.

import { toasts } from '@unbound-app/api';

declare function sync(): Promise<void>;

try {
	await sync();
} catch (error) {
	toasts.error('FAILED_TO_SYNC', error);
}

With one argument, it is always the error. With a key, the key's vars come next, then the error:

import { toasts } from '@unbound-app/api';

declare const file: { name: string };
declare const error: unknown;

toasts.error('FAILED_TO_SAVE', { name: file.name }, error);

Confirm and Alert

Both take the title key first. confirm resolves false on Cancel, a tap outside, or a swipe away. alert resolves however it closes.

OptionWhat it does
titleVarsTitle placeholder values. Required when it has placeholders
bodyKey of the text under the title
bodyVarsBody placeholder values. Required when it has placeholders
confirmConfirm button key. For alert, optional, default OK
cancelconfirm only: Cancel button key, default Cancel
destructiveconfirm only: red confirm button. Use it whenever confirming loses data

Custom Toasts

toasts.show takes text, for a body, buttons or your own icon.

import { toasts } from '@unbound-app/api';
import { messages } from '@unbound-app/i18n';

toasts.show({ title: messages.COPIED, content: messages.COPIED_LINK, icon: 'LinkIcon' });
OptionWhat it does
title, contentThe bold first line, and the text under it
iconAn asset name or id
durationMilliseconds before closing. 0 keeps it open. Default: the user's setting
idA later toast with the same id replaces this one
buttonsEach with content and onPress

show and every shorthand return a handle with update and close, for a toast still open:

import { toasts } from '@unbound-app/api';

import { messages } from '@unbound-app/i18n';

const toast = toasts.show({ title: messages.CHECKING, duration: 0 });
const response = await fetch('https://example.com/latest.json');

toast.update({ title: response.ok ? messages.UP_TO_DATE : messages.CHECK_FAILED });
setTimeout(() => toast.close(), 3000);

Custom Dialogs

dialogs.showDialog takes text and any number of buttons. A button closes the dialog, then runs its onPress.

import { dialogs, storage } from '@unbound-app/api';

import { messages } from '@unbound-app/i18n';

const settings = storage.getBucket();

dialogs.showDialog({
	title: messages.RESET_TITLE,
	body: messages.RESET_BODY,
	buttons: [
		{ label: messages.RESET_COLORS, onPress: () => settings.remove('colors') },
		{ label: messages.RESET_ALL, variant: 'destructive', onPress: () => settings.clear() },
	],
	cancel: true,
	onDismiss: () => logger.info('Closed without choosing'),
});
OptionWhat it does
canceltrue adds a last Cancel button. A string sets its label
onDismissRuns when it closes without a button
contentA component for more than text. Gets a dismiss function