Skip to main content
Skip table of contents

Example Implementation

This section gives a full example of a minimal host adator which builts on the PICTOFiT Platform. The platform provides a public endpoint which returns stock assets (default avatars, garments and scenes). Keep in mind that this is an example. For your actual implementation, you will have to either host the data yourself or use our PICTOFiT Platform.

You can find the full source in our sample repository.

Fetching the Data

The PICTOFiT Platform provides a a public GraphQL API that lets you fetch the stock assets via the getStockResources query. A simple query for stock garments could look like this:

GRAPHQL
query{
  getStockResources(forFormat:WEB, forKind:GARMENT_2D) {
    id,
    name,
    thumbnail,
    assets {
      files {
        fileName,
        getLocation
      }
    }
  }
}

This will return a list of stock garments where each entry looks like this:

JSON
{
"id": "168db2f6-8a7d-4b4e-ae5e-883480396c88",
"name": "Snow Peak 3 Layer Rain Jacket",
"thumbnail": "https://pictofit-platform.f21e22a4fbbf2fdc7fbae60413530984.r2.cloudflarestorage.com/168db2f6-8a7d-4b4e-ae5e-883480396c88/c2d420ce-88ee-4a60-b1ba-9cb86f05a90e?x-id=GetObject&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=fa329d5876ec80cb82dbf8e4b59fa5ff%2F20231016%2Fauto%2Fs3%2Faws4_request&X-Amz-Date=20231016T090508Z&X-Amz-Expires=172800&X-Amz-SignedHeaders=host&X-Amz-Signature=d20453135003831f29c1f825bbfd1144ccb60986a2303a342e9887c4f4ee3ac5",
"assets": [
  {
    "files": [
      {
        "fileName": "diffuse.jpg",
        "getLocation": "https://pictofit-platform.f21e22a4fbbf2fdc7fbae60413530984.r2.cloudflarestorage.com/168db2f6-8a7d-4b4e-ae5e-883480396c88/3e2fcd55-a80b-4e42-bda4-27854ca02c13?x-id=GetObject&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=fa329d5876ec80cb82dbf8e4b59fa5ff%2F20231016%2Fauto%2Fs3%2Faws4_request&X-Amz-Date=20231016T090508Z&X-Amz-Expires=172800&X-Amz-SignedHeaders=host&X-Amz-Signature=da0fee3d0a0e6d4f3e10f6f5e57aaf21b21daa89f6b122dda7e29dc6291e4c75"
      },
      ...
      ]
  }]
}

This result already has all the information we need. It provides an ID, a display name, a thumbnail and the assets for a garment. So let’s define a simple function to fetch this data from the endpoint:

JS
/**
 * Helper function to fetch public resources from the PICTOFiT platform API
 */
function fetchPublicResources(kind : 'GARMENT_2D' | 'SCENE' | 'AVATAR_2D') : Promise<Resource[]> {

  const graphQLQuery = `
  query {
    getStockResources(forFormat:WEB, forKind:${kind}) {
      id,
      name,
      thumbnail,
      assets {
        files {
          fileName,
          getLocation
        }
      }
    }
  }`;

  return fetch("https://api.platform.pictofit.com/graphql/public/v1", {
    method : 'POST',
    headers: {
      accept: "application/json, multipart/mixed",
    },
    body: JSON.stringify( { query : graphQLQuery })
  })
  .then((res) => res.json()) // conver the result to json
  .then((json) => {
    return json.data.getStockResources; // return only the actual relevant data part of the response
  });
}

Line 6-19 define the GraphQL query which returns the data in the format from above. We have to provide the kind of resource we want to fetch to the query since we have to write different providers for garments, avatars and scenes. We then call fetch and provide the query as JSON in the body of the request. The resulting data is transformed into a JSON object. Finally, we extract the getStockResource field as the GraphQL wraps the result into an object containing metadata for the request.

We have defined the Resource interface to get code completion when accessing the result of the GraphQL query.

TYPESCRIPT
interface Resource {
  id : string,
  name : string,
  thumbnail : string,
  assets : {
    files : {
      fileName : string,
      getLocation : string
    }[]
  }[]
}

Implementing a Garment Provider

Now let’s define a Providers.GarmentProvider to fill our dressing room with first content. The interface look like this:

TYPESCRIPT
export interface GarmentProvider extends AssetProvider {
  getIds(): Awaitable<Array<string>>;
  getById(id: string): Awaitable<Core.AssetInfo | undefined>;
  getName(id: string): Awaitable<string | undefined>;
  getThumbnail(id: string): Awaitable<string | undefined>;
  removeById(id: string): Awaitable<void>;
  /** Callback to add a garments from a shared look into the current dressing room / garment provider. */
  addById?: (id: string) => void;
}

Please be aware that getIds() and getById() are actually defined by AssetProvider which is the underlying interface for all resources which need to provide assets that can be consumed by the Web SDK (garment, avatars, scenes). First, let’s fetch all garments and provide their IDs.

TYPESCRIPT
class SimpleGarmentProvider implements Providers.GarmentProvider  {

  private _items : Promise<Resource[]>;
  constructor() {
    this._items = fetchPublicResources("GARMENT_2D");
  }

  public getIds(): Awaitable<string[]> {
    return this._items.then((assets) => {
     return assets.map((asset) => asset.id)
    });
  }
  
  // ...
}

We first fetch the resources of kind GARMENT_2D from the PICTOFiT Platform and then simply extract their IDs using Javascript’s Array.map(...) and return them. Now let’s continue with the name and the thumbnail which are again straightforward.

TYPESCRIPT
public getName(id: string): Awaitable<string | undefined> {
  return this._items.then((assets) => {
    const asset = assets.find((asset) => asset.id === id);
    if(asset === undefined) {
      return undefined;
    }
    return asset.name;
  })
}

public getThumbnail(id: string): Awaitable<string | undefined> {
  return this._items.then((assets) => {
    const asset = assets.find((asset) => asset.id === id);
    if(asset === undefined) {
      return undefined;
    }
    return asset.thumbnail;
  })
}

We first check if a resource with the given ID exists. If that’s the case we return the name/thumbnail. Now the remaining part is the getById() method which returns the assets for a resource.

TYPESCRIPT
public getById(id: string): Awaitable<Core.AssetInfo | undefined> {
  return this._items.then((assets) => {
    const asset = assets.find((asset) => asset.id === id);
    if(asset === undefined) {
      return undefined;
    }
    return asset.assets[0].files.reduce((init, file) => ({
      ...init,
      [file.fileName] : file.getLocation
    }), {} );
  })
}

Again, we check if a resource with the given ID exist. If that’s the case, we use the Array.reduce(…) method to transform the files array which contains objects for each file with name and location as separate properties into the form filename : location. Please see the Introduction section for more details on the format of the Core.AssetInfo object.

We have left out removeById() and addById() for the sake of simplicity in this example. Apart from that, this is all you need to implement to populate the dressing room with garments. Repeat this for the avatars and scenes and you are good to go. You can check out our sample repository which has a full, working implementation. Note that we extract the common operations into a generic provider called GenericPublicAssetProvider to avoid code duplication. Now let’s assemble our actual host provider:

TYPESCRIPT
export const simpleHostAdapter : HostAdaptor = {
  garments : {
    "2D_Parallax" : new SimpleGarmentProvider()
  },
  avatars : {
    "2D_Parallax" : new SimpleAvatarProvider()
  },
  scenes : {
    "2D_Parallax" : new SimpleSceneProvider()
  },
  dressingRoom : {
    mode : "2D_Parallax"
  }
}

The MDR supports different modes so we have to define for which mode the respective resource provider is designed. Finally, we set the mode of the dressing room to 2D_Parallax. What now remains is to create an instance of the dressing room and to pass the host provider to it.

Integrating the MDR

Finally, we need to create the pictofit-modal-dressing-room element, add it to the DOM and assign our HostAdapter.

TYPESCRIPT
import { simpleHostAdapter } from './host-adapter';
import { HostAdaptor } from '@reactivereality/pictofit-dressing-room';
import "@reactivereality/pictofit-dressing-room/bundle"

window.getHostAdapter = function() {
  return simpleHostAdapter;
}

const url = "https://spf-app.pictofit.dev";
const dressingRoom = document.createElement("pictofit-modal-dressing-room")
dressingRoom.setAttribute('host-adaptor', 'getHostAdapter')
dressingRoom.setAttribute('compute-server', url)
document.body.appendChild(dressingRoom);

We define a factory function getHostAdapter which returns our simpleHostAdapter and pass it to the MDR web component. The MDR is expecting this method to be part of the window object. Now to actually show the dressing room, we add e.g. a link to the html and register a click event handler that opens the MDR:

TYPESCRIPT
document.querySelector<HTMLDivElement>('#open-dressing-room')!.addEventListener('click', () => {
  const dressingRoom = document.querySelector("pictofit-modal-dressing-room");
  dressingRoom!.open();
})

That’s it, you have successfully created a host adapter to populate the MDR with content and related functionality. Please remember that the source of the data can be anything. Ideally this data comes from your e-commerce solution and your CDN. Alternatively, we can host your assets for you on our PICTOFiT Platform. Read more about this in the next section. Be aware that this is sample code and not production ready.

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.