# LEAP Host SDK

LEAP Host SDK is a TypeScript library that helps your LEAP apps communicate easily with LEAP client applications.

## How to install

This library should be installed as an npm package. We use semantic versioning so the best way to stay up-to-date without backward compatibility concern is to accept minor releases:

```
npm i @leapdev/leap-host
```

## Breaking changes in 4.0.0

Version 4.0.0 is a major release. Review the items below before upgrading from 3.x.

### `document.selectPrecedent()` response is now camelCase on LEAP Desktop

On LEAP Desktop, `document.selectPrecedent()` previously returned the raw host response with PascalCase property names (for example `RecordId`, `ParentId`, `DocumentType`). The SDK now normalises the response to camelCase (for example `recordId`, `parentId`, `documentType`) to match the `SelectPrecedentResponse` TypeScript interface and LEAP Web behaviour.

If your app reads properties directly from the `selectPrecedent` response, update property access to camelCase:

```typescript
// Before (3.x on LEAP Desktop)
const response = await sdk.document.selectPrecedent(request);
const id = response.RecordId;

// After (4.0)
const response = await sdk.document.selectPrecedent(request);
const id = response.recordId;
```

## New in 4.0.0

- **`sdk.leapContext.hostInfo.accessibility`** — accessibility settings from the host (font scale, theme, high contrast, reduced motion, and more), available after `init()` and on context updates.
- **`document.createDocumentBasedOnURL()`** — create a document in LEAP from a remote URL.
- **`subscribeContextUpdate()` / `unsubscribeContextUpdate()`** — listen for host context changes with automatic `sdk.leapContext` updates.

## How to use

**In order to have all the features supported, please develop with the latest LEAP desktop 2.3 or above**

The first thing you have to do is to initialise the SDK

```typescript
import { LeapHostSdkFactory } from '@leapdev/leap-host';

const sdk = LeapHostSdkFactory.getInstance();
if (!!sdk) {
  await sdk.init('LEAP auth clientId');
  // IMPORTANT: please make sure you are using the correct clientId we provide for your app
}
```

This `init` function returns a `Promise`. We recommend that you do this initialisation once and cache the promise result in memory.

By awaiting the promise, you then have access to methods that perform actions in the LEAP client applications. Please refer to the [API reference](#api-reference) for all supported methods.

### Subscribing to context updates

The SDK keeps `sdk.leapContext` up to date automatically. Register a callback if your app needs to react when the host context changes:

```typescript
sdk.subscribeContextUpdate(
  (leapContext) => console.log(leapContext),
  (error) => console.error(error)
);
```

## TL;DR

### Abstracting LEAP context retrieval

A LEAP app needs to know the context of where its being loaded, e.g. the matterId where it is loaded, which record is being selected (if the current page in LEAP is a list), etc. The LEAP app is expected to retrieve the context object from the hosting LEAP client application. A connection between the LEAP client application and LEAP app is assigned a unique guid. The LEAP app can access this value via "appSessionId" property received.

The LEAP Host SDK abstracts the context retrieval process by presenting a unified API to the LEAP app. By calling the init function, the LEAP app will have access to the context object as follow.

```typescript
import { LeapHostSdkFactory } from '@leapdev/leap-host';

const sdk = LeapHostSdkFactory.getInstance();
if (!!sdk) {
  await sdk.init('LEAP auth clientId');
  console.log(sdk.leapContext);
}
```

### Obtaining LEAP Auth token

A LEAP app often needs to call the LEAP API Gateway services. To do so, you will need to init the sdk first then call the method below

```typescript
// for best practise, just call this method whenever you need to use the access token, caching it is not recommeded
const accessToken = await sdk.getRefreshedAccessToken();

// or if you want to get the claims in the token you can go
const decodedAccessToken = await sdk.getDecodedRefreshedAccessToken();
```

### Sending a command to the LEAP client application

A LEAP client applications (LEAP Desktop / Web / Mobile) accepts certain messages from LEAP apps and act upon them. For example, they can open a document or show a message box.

The LEAP Host SDK facilitates this process by exposing a set of semantic functions through which the LEAP app can invoke.

```typescript
await sdk.init();

// open a message box in LEAP
sdk.system.alert({ message: 'Hello, world' });

// open a matter
sdk.matter.openMatter(openMatterRequest);

// close the app
sdk.system.close();
```

## API reference

### Accounting

#### Create a time entry

```typescript
createTimeEntryV2(request: CreateTimeEntryRequest): Promise<CreateTimeEntryResponse>;
```

#### Create a fee entry

```typescript
createFeeEntryV2(request: CreateFeeEntryRequest): Promise<CreateFeeEntryResponse>;
```

#### Create a cost recovery

```typescript
createCostRecoveryV2(request: CreateCostRecoveryEntryRequest): Promise<CreateCostRecoveryResponse>;

//Request example:
const request = {
  "matterId":"c2b1cbde-77ec-2642-91d9-09191e5258c6",
  "appSessionId":"1fa55cbb-7b02-a546-94a2-8986be7fce5b"
};

const response = await sdk.accounting.createCostRecoveryV2(request);
```

#### Create an invoice

```typescript
createInvoiceV2(request: CreateInvoiceRequest): Promise<CreateInvoiceResponse>;
```

#### Create an office receipt

```typescript
createOfficeReceiptV2(request: CreateOfficeReceiptRequest): Promise<CreateOfficeReceiptResponse>;
```

#### Create an office payment

```typescript
createOfficePaymentV2(request: CreateOfficePaymentRequest): Promise<CreateOfficePaymentResponse>;

//Request example:
const request = {
  "matterId":"c2b1cbde-77ec-2642-91d9-09191e5258c6",
  "appSessionId":"1fa55cbb-7b02-a546-94a2-8986be7fce5b"
};

const response = await sdk.accounting.createOfficePaymentV2(request);
```

#### Create an office payment request

```typescript
createOfficePaymentRequestV2(request: CreateNewOfficePaymentRequestRequest): Promise<CreateNewOfficePaymentRequestResponse>;
```

#### Create an trust payment request

```typescript
createTrustPaymentRequestV2(request: CreateNewTrustPaymentRequestRequest): Promise<CreateNewTrustPaymentRequestResponse>;
```

#### Create an office journal

```typescript
createOfficeJournalV2(request: CreateOfficeJournalRequest): Promise<CreateOfficeJournalResponse>;
```

#### Create a trust receipt

```typescript
createTrustReceiptV2(request: CreateTrustReceiptRequest): Promise<CreateTrustReceiptResponse>;
```

#### Create a trust payment

```typescript
createTrustPaymentV2(request: CreateTrustPaymentRequest): Promise<CreateTrustPaymentResponse>;
```

#### Create a trust journal

```typescript
createOfficeJournalV2(request: CreateOfficeJournalRequest): Promise<CreateOfficeJournalResponse>;
```

#### Create trust to office

```typescript
createTrustToOfficeV2(request: CreateTrustToOfficeRequest): Promise<CreateTrustToOfficeResponse>;
```

#### Reload the financial summary

```typescript
reloadFinancialSummary(request: ReloadFinancialSummaryRequest): void;
```

#### Reload the time fee list

```typescript
reloadTimeFeeList(request: ReloadTimeFeeListRequest): void;
```

#### Reload the office ledger

```typescript
reloadOfficeLedger(request: ReloadOfficeLedgerRequest): void;
```

#### Reload the anticipated payment list

```typescript
reloadAnticipatedPaymentList(request: ReloadAnticipatedPaymentListRequest): void;
```

#### Reload the cost recovery list

```typescript
reloadCostRecoveryList(request: ReloadCostRecoveryListRequest): void;

//Request example:
const request = {
  "matterId":"c2b1cbde-77ec-2642-91d9-09191e5258c6",
  "appSessionId":"1fa55cbb-7b02-a546-94a2-8986be7fce5b"
};

sdk.accounting.reloadCostRecoveryList(request);
```

#### Reload the trust ledger

```typescript
reloadTrustLedger(request: ReloadTrustLedgerRequest): void;
```

#### Reload the controlled money list

```typescript
reloadControlledMoneyList(request: ReloadControlledMoneyListRequest): void;
```

#### Reload the power money list

```typescript
reloadPowerMoneyList(request: ReloadPowerMoneyListRequest): void;
```

#### Reload the transit money list

```typescript
reloadTransitMoneyList(request: ReloadTransitMoneyListRequest): void;
```

#### Open a fee

```typescript
openFee(request: OpenFeeRequest): void;
```

### App

#### Open the legal guide

Opens the ByLawyers "Legal Guides" tab and routes to the guide for the given matter type and state.

```typescript
openLegalGuide(request: OpenLegalGuideRequest): void;

//Request example:
const request = {
  "matterTypeId":"c2b1cbde-77ec-2642-91d9-09191e5258c6",
  "state":"NSW"
};

sdk.app.openLegalGuide(request);
```

### Calc

#### Evaluate paths

```typescript
evaluatePaths(request: EvaluatePathsRequest): Promise<PathResult[]>;

//Request example:
const request = {
  "executionContext": {
    "matterGUID": "a4a9a42d-2dd8-48c4-bf06-b213369a5c84"
  },
  "paths": ["matter.clientList.{__id,__className,__fileOrder,__description,personList}", "matter.fileNumber"]
};

sdk.calc.evaluatePaths(request)
.then(response =>{
  //handle the response here
});
```

### Card

#### Select card(s) from the list

```typescript
selectCard(request?: SelectCardRequest): Promise<Card[]>;
```

#### Create a card

```typescript
createCard(): Promise<CreatedCard[]>;
```

#### Open a card

```typescript
openCard(request: OpenCardRequest): void;
```

### Communication

#### Compose an email

```typescript
composeEmail(request: CreateEmailRequest): void;
```

#### Create an appointment

```typescript
createAppointment(request: CreateAppointmentRequest): void;

//Request example:
const request = {
  "matterId":"c2b1cbde-77ec-2642-91d9-09191e5258c6",
  "startDate": "2020-10-21T05:12:46.472Z",
  "endDate": "2020-10-22T05:13:46.472Z",
  "attachments":[],
  "requiredAttendees":[],
  "optionalAttendees":[]
};

sdk.communication.createAppointment(request);
```

#### Create a task

```typescript
createTask(request: CreateTaskRequest): void;

//Request example:
const request = {
  "matterId":"c2b1cbde-77ec-2642-91d9-09191e5258c6",
  "startDate":"2020-10-21T00:00:00.000Z",
  "dueDate":"2020-10-22T00:00:00.000Z"
};

sdk.communication.createTask(request);
```

### Document

#### Preview a document

```typescript
previewDocument(request: PreviewDocumentRequest): void;
```

#### Preview a Precedent

```typescript
previewPrecedent(request: PreviewPrecedentRequest): void;
```

#### Edit a Precedent

```typescript
editPrecedent(request: EditPrecedentRequest): void;
```

#### Get the details of a selected precedent

```typescript
selectPrecedent(request: SelectPrecedentRequest): Promise<SelectPrecedentResponse>;
```

#### Create a document from a URL

```typescript
createDocumentBasedOnURL(request: CreateDocumentBasedOnURLRequest): void;
```

#### Create a document from a container

```typescript
createDocumentFromContainer(request: CreateDocumentFromContainerRequest): void;
```

#### Create a document from a precedent

```typescript
createDocumentFromPrecedent(request: CreateDocumentFromPrecedentRequest): void;
```

#### Open a document

```typescript
openDocument(request: OpenDocumentRequest): void;

const request = {
  "matterId":"c2b1cbde-77ec-2642-91d9-09191e5258c6",
  "documentId":"ba5e2173-e6bc-47b4-9280-787f78454e2b",
  "appSessionId":"b90402b8-c7cb-435b-b1fc-8bbcb1d49934"
};

sdk.document.openDocument(request);
```

#### Reload the document list

```typescript
reloadDocumentList(request: ReloadDocumentListRequest): void;
```

### LawConnect

#### Share documents

```typescript
shareDocuments(request: ShareDocumentsRequest): void;
```

#### Share a folder

```typescript
shareFolder(request: ShareFolderRequest): void;
```

#### Share an office statement

```typescript
shareOfficeStatement(request: ShareOfficeStatementRequest): void;
```

#### Share a trust statement

```typescript
shareTrustStatement(request: ShareTrustStatementRequest): void;
```

### Matter

#### Open a table

```typescript
openTable(request: OpenTableRequest): void;
```

#### Open a matter

```typescript
openMatter(request: OpenMatterRequest): void;

//Request example:
const request = {
  "matterId":"c2b1cbde-77ec-2642-91d9-09191e5258c6",
  "appSessionId":"1fa55cbb-7b02-a546-94a2-8986be7fce5b"
};

sdk.matter.openMatter(request);
```

#### Select matter(s)

```typescript
selectMatter(request?: SelectMatterRequest): Promise<Matter[]>;
```

### Person

#### Select staff

```typescript
selectStaff(request?: SelectStaffRequest): Promise<Staff[]>;
```

### MS Teams

#### Share a document

```typescript
shareDocument(request: ShareDocumentRequest): void;
```

#### Share a matter

```typescript
shareMatter(request: ShareMatterRequest): void;
```

#### Request a callback

```typescript
shareMatter(request: RequestCallbackRequest): void;
```

### Person

#### Select person(s) from the list

```typescript
selectPerson(request: SelectPersonRequest): Promise<Person[]>;
```

#### Open a person

```typescript
openPerson(request: OpenPerson): void;
```

### Register

#### Reload document register list

```typescript
reloadDocumentRegisterList(request: ReloadDocumentRegisterListRequest): void;
```

#### Reload power estate list

```typescript
reloadPowerEstateList(request: ReloadPowerEstateListRequest): void;
```

### Schema

#### Get schema list

```typescript
getLists(request: GetListsRequest): Promise<SchemaResponse<SchemaList[]>>;
```

#### Customise list

```typescript
customiseList(request: CustomiseListRequest): Promise<CustomiseListResponse>;
```

### System

#### Get a refreshed access token

```typescript
getRefreshedAccessToken(): Promise<string>;
```

#### Get a decoded access token

```typescript
getDecodedRefreshedAccessToken(): Promise<any>;
```

#### Subscribe to context updates

```typescript
subscribeContextUpdate(onSuccess?: (leapContext: LeapContext) => void, onError?: (error: any) => void): void;
```

#### Unsubscribe from context updates

```typescript
unsubscribeContextUpdate(): void;
```

#### Open Twitter window

```typescript
openTwitter(request: BaseRequest): void;
```

#### Close the current LEAP app

```typescript
close(): void;
```

#### Hide the current LEAP app

```typescript
hide(): void;
```

#### Show loading icon in LEAP

```typescript
wait(request?: MessageRequest): void;
```

#### Hide loading icon in LEAP

```typescript
resume(): void;
```

#### Show a message box in LEAP (Deprecated, replaced with openDialog)

```typescript
alert(): void;
```

#### Show an error message in LEAP (Deprecated, replaced with openDialog)

```typescript
error(): void;
```

#### Display a message on the dialog box in LEAP

```typescript
openDialogV2(request: DialogRequest): Promise<system.DialogButtonType>;
```

#### Open an URL in browser (Deprecated, replaced with openUrlV2)

```typescript
openUrl(config: BrowserConfig): void;

//Request example:
const config = {
  "url": "https://app.test.bylawyers.com.au/",
  "openExternal": false,
  "useNativeLoading": false,
  "width": 900,
  "height": 500,
  "minWidth": 800,
  "minHeight": 400,
  "maxWidth": 1000,
  "maxHeight": 600,
  "closeHandler": "jsFunction(parameter)",
  "fixedWindowSize": false,
  "independentWindow": false,
  "windowTitle": "Good day"
};

sdk.system.openUrl(config);
```

#### Open an URL in browser

```typescript
openUrlV2(config: BrowserConfigV2): void;

//Request example:
const config = {
  "url": "https://app.test.bylawyers.com.au/",
  "openExternal": false,
  "useNativeLoading": false,
  "showControls": true,
  "width": 900,
  "height": 500,
  "minWidth": 800,
  "minHeight": 400,
  "maxWidth": 1000,
  "maxHeight": 600,
  "closeHandler": "jsFunction(parameter)",
  "fixedWindowSize": false,
  "independentWindow": false,
  "windowTitle": "Good day"
};

sdk.system.openUrlV2(config);
```

#### Get host system info

```typescript
getSystemInfo(): void;
```

#### Set window title

```typescript
setWindowTitle(request: SetWindowTitleRequest): void;
```

#### Register a handler to trigger before closing a window

```typescript
registerHostMessageHandler('onBeforeClose', () => {
  //sdk.system.cancelClose();
  //your logic goes here
  //sdk.system.close();
});
```

#### Send a custom request object to the host

```typescript
send(request: object, hasResponse?: boolean): Promise<any>;
```

#### Notify the host system when your app has finished loading (to avoid having different loaders from LEAP and your app)

```typescript
finishLoading(): void;
```
