2.14.0 Change Notes

New Settings UI Features

The @bentley/ui-core package has added the SettingsManager class that allows any number of SettingsProvider classes to be registered. These providers provide SettingsTabEntry definitions used to populate the SettingsContainer UI component with setting pages used to manage application settings. These new classes are marked as beta in this release and are subject to minor modifications in future releases.

Add Settings Page to set Quantity Formatting Overrides

The QuantityFormatSettingsPanel component has been added to the @bentley/ui-framework package to provide the UI to set both the PresentationUnitSystem and formatting overrides in the QuantityFormatter. This panel can be used in the new SettingsContainer UI component. The function getQuantityFormatsSettingsManagerEntry will return a SettingsTabEntry for use by the SettingsManager. Below is an example of registering the QuantityFormatSettingsPanel with the SettingsManager.

// Sample settings provider that dynamically adds settings into the setting stage
export class AppSettingsProvider implements SettingsProvider {
  public readonly id = "AppSettingsProvider";

  public getSettingEntries(_stageId: string, _stageUsage: string): ReadonlyArray<SettingsTabEntry> | undefined {
    return [
      getQuantityFormatsSettingsManagerEntry(10, {availableUnitSystems:new Set(["metric","imperial","usSurvey"])}),
    ];
  }

  public static initializeAppSettingProvider() {
    UiFramework.settingsManager.addSettingsProvider(new AppSettingsProvider());
  }
}

The QuantityFormatSettingsPanel is marked as alpha in this release and is subject to minor modifications in future releases.

Breaking Api Changes

@bentley/ui-abstract package

Property onClick in LinkElementsInfo was changed to be mandatory. Also, the first PropertyRecord argument was removed from the method. Suggested ways to resolve:

  • If you have a function myFunction(record: PropertyRecord, text: string) and use the first argument, the issue can be resolved with a lambda:

    record.links = {
      onClick: (text) => myFunction(record, text),
    };
  • If you were omitting the onClick method to get the default behavior, it can still be achieved by not setting PropertyRecord.links at all. It's only valid to expect default click behavior when default matcher is used, but if a custom matcher is used, then the click handled can be as simple as this:

    record.links = {
      onClick: (text) => { window.open(text, "_blank"); },
    };

@bentley/imodeljs-frontend package

Initializing TileAdmin

The previously-alpha IModelAppOptions.tileAdmin property has been promoted to beta and its type has changed from TileAdmin to TileAdmin.Props. TileAdmin.create has become async. Replace code like the following:

  IModelApp.startup({ tileAdmin: TileAdmin.create(props) });

with:

  IModelApp.startup({ tileAdmin: props });

Tile request channels

Tiles are now required to report the TileRequestChannel via which requests for their content should be executed, by implementing the new abstract Tile.channel property. The channel needs to specify a name and a concurrency. The name must be unique among all registered channels, so choose something unlikely to conflict. The concurrency specifies the maximum number of requests that can be simultaneously active on the channel. For example, when using HTTP 1.1 modern browsers allow no more than 6 simultaneous connections to a given hostname, so 6 is a good concurrency for HTTP 1.1-based channels and the hostname is a decent choice for the channel's name.

Typically all tiles in the same TileTree use the same channel. Your implementation of Tile.channel will depend on the mechanism by which the content is obtained. If it uses HTTP, it's easy:

  public get channel() { return IModelApp.tileAdmin.getForHttp("my-unique-channel-name"); }

If your tile never requests content, you can implement like so:

  public get channel() { throw new Error("This tile never has content so this property should never be invoked"); }

If your tile uses the alpha TileAdmin.requestElementGraphics API, use the dedicated channel for such requests:

  public get channel() { return IModelApp.tileAdmin.channels.elementGraphicsRpc; }

Otherwise, you must register a channel ahead of time. Choose an appropriate concurrency:

  • If the tile requests content from some custom RpcInterface, use IModelApp.tileAdmin.channels.rpcConcurrency.
  • Otherwise, choose a reasonably small limit to prevent too much work from being done at one time. Remember that tile requests are frequently canceled shortly after they are enqueued as the user navigates the view. A concurrency somewhere around 6-10 is probably reasonable.

To register a channel at startup:

  await IModelApp.startup();
  const channel = new TileRequestChannel("my-unique-channel-name", IModelApp.tileAdmin.rpcConcurrency);
  IModelApp.tileAdmin.channels.add(channel);

If you store channel from the above snippet in a global variable, you can implement your channel property to return it directly; otherwise you must look it up:

  public get channel() {
    const channel = IModelApp.tileAdmin.channels.get("my-unique-channel-name");
    assert(undefined !== channel);
    return channel;
  }

Authentication changes for Electron and Mobile apps

For desktop and mobile applications, all authentication happens on the backend. The frontend process merely initiates the login process and waits for notification that it succeeds. Previously the steps required to set up the process were somewhat complicated.

Now, to configure your electron or mobile application for authorization, pass the authConfig option to ElectronApp.startup or IOSApp.startup to specify your authorization configuration.

Then, if you want a method that can be awaited for the user to sign in, use something like:

// returns `true` after successful login.
async function signIn(): Promise<boolean> {
  const auth = IModelApp.authorizationClient!;
  if (auth.isAuthorized)
    return true; // make sure not already signed in

  return new Promise<boolean>((resolve, reject) => {
    auth.onUserStateChanged.addOnce((token?: AccessToken) => resolve(token !== undefined)); // resolve Promise with `onUserStateChanged` event
    auth.signIn().catch((err) => reject(err)); // initiate the sign in process (forwarded to the backend)
  });
}

@bentley/imodeljs-quantity package

UnitProps property name change

The interface UnitProps property unitFamily has been renamed to phenomenon to be consistent with naming in ecschema-metadata package.

Enhancements to exportGraphics

IModelDb.exportGraphics and IModelDb.exportPartGraphics have new options that provide more control over the amount of detail in their output. See decimationTol and minLineStyleComponentSize in ExportGraphicsOptions for details.

Deprecation and removal of @bentley/forms-data-management-client

The current @bentley/forms-data-management-client package is being deprecated and immediately removed from the source. While this is technically a breaking change outside of the major release cycle, no one is able to use this client due to the required OIDC scopes not being published.

@bentley/imodeljs-quantity package

The alpha classes, interfaces, and definitions in the package @bentley/imodeljs-quantity have been updated to beta.

Last Updated: 02 April, 2021