Embedded app

Prerequisites

  1. You know how to create an app. If not, please visit https://shopline-developers.readme.io/docs/create-a-draft-app
  2. You know how to configure the app settings. If not, please visit https://shopline-developers.readme.io/docs/configure-apps-settings
  3. You know how to submit the app for approval. If not, please visit https://shopline-developers.readme.io/docs/subscribe-to-webhook

Intro

Admin app extension allows app page path configuration. It loads your configured web application through iframe when accessing designated SHOPLINE admin page.

Your web application can then trigger admin actions through admin app extension.

Integration

We provide a standardized tool app bridge for your frontend web application to communicate with admin.

It provides several abilities, here list out some

  • Get admin current user language (getLanguage()) & listen to its change (onLanguageChanged())
  • Trigger admin intercom (intercom())
  • Redirect user to arbitrary url (redirect(<url>))
  • Redirect to designated admin page (redirectAdminPage(<page>)), page includes AppDetail
  • Get merchant authority (getMerchantAuthority())
  • Trigger oauth flow (oauth())

Full list can be found in here

Install

npm

npm i https://github.com/shoplineapp/app-bridge-js.git

yarn

yarn add https://github.com/shoplineapp/app-bridge-js.git

Usage

First initialize app bridge

import { AppBridge } from "@shopline/app-bridge-js";

const client = await AppBridge.init({
  clientId: '{{client id}}',
  authUrl: '{{auth url}}',
});

Then you may use the client provided functions

client.redirect('/products');

Example

A demonstration on react to verify merchant has installed provided embedded app or not, if no, redirect to designated page

This example requires app bridge version >= 1.4.0

import { AppBridge, AppBridgeClient } from "@shopline/app-bridge-js";

interface InitConfig {
  clientId: string;
  authUrl: string;
}

class AppBridgeInstance {
  private static instance: AppBridgeInstance | null = null;

  client!: AppBridgeClient;

  constructor() {
    if (AppBridgeInstance.instance) {
      return AppBridgeInstance.instance;
    }
    AppBridgeInstance.instance = this;
  }

  async init(initConfig: InitConfig): Promise<string> {
    // set timeout on init
    const timeoutPromise = new Promise<never>((_, reject) => {
      setTimeout(() => {
        reject(new Error("Promise timed out"));
      }, 5000);
    });

    return Promise.race([AppBridge.init(initConfig), timeoutPromise])
      .then((client) => {
        this.client = client;
        return "success";
      })
      .catch((err) => {
        console.log("appBridge error", err);
        return "failed";
      });
  }
}

const appBridge = new AppBridgeInstance();

export default appBridge;
import { Suspense, use } from "react";
import appBridge from "./AppBridgeSingleton";

async function initAppBridge() {
  return appBridge.init({
    clientId: "",
    authUrl: "",
  });
}

async function isMerchantInstalledEmbeddedApp() {
  const authority = await appBridge?.client?.getMerchantAuthorities();
  return authority.authorized;
}

const Fallback = () => <p>Loading...</p>;

const AppContent = ({
  authorityPromise,
}: {
  authorityPromise: Promise<boolean>;
}) => {
  const isInstalled = use<boolean>(authorityPromise);

  if (isInstalled) {
    return <p>You have installed this app.</p>;
  }

  setTimeout(() => {
    appBridge.client.redirectAdminPage("AppDetail");
  }, 3000);

  return <p>You have not installed this app, redirect soon...</p>;
};

const LandingComponent = ({ promise }: { promise: Promise<string> }) => {
  const initResult = use<string>(promise);

  if (initResult === "success") {
    const authorityPromise = isMerchantInstalledEmbeddedApp();
    return (
      <Suspense fallback={<Fallback />}>
        <AppContent authorityPromise={authorityPromise} />
      </Suspense>
    );
  }

  return <p>App bridge init error</p>;
};

export default function App() {
  const initAppBridgePromise = initAppBridge();

  return (
    <Suspense fallback={<Fallback />}>
      <LandingComponent promise={initAppBridgePromise} />
    </Suspense>
  );
}