Skip to main content

Services

@smartcompanion/services assembles the domain services of @smartcompanion/data into one ServiceFacade, and adds what an app needs around them: audio playback, the menu, and routing. An app wires up this one object instead of a dozen.

npm install @smartcompanion/services

Peer dependencies: @smartcompanion/data 2, @smartcompanion/native-audio-player 1.0.2 or newer, @capacitor/core 8, @ionic/core 9.

The facade​

import { ServiceFacade } from '@smartcompanion/services';

const facade = new ServiceFacade();
facade.registerCollectibleAudioPlayerService('My Audioguide');
facade.registerOnlineLoadService(() => fetch(DATA_URL, { cache: 'no-store' }).then(response => response.json()));

The constructor takes an optional Storage (default BrowserStorage) and an optional resolveUrl for asset URLs — see Data.

Two things must be registered; everything else is created on first use:

RegisterChoices
An audio playerregisterCollectibleAudioPlayerService(subtitle) or registerDefaultAudioPlayerService(subtitle)
A load serviceregisterOnlineLoadService(downloadData) or registerOfflineLoadService(downloadData, downloadFile, remove, save, list)

Using the audio player or the load service before registering one throws an error that says which call is missing.

What it exposes​

GroupMethods
Domain servicesgetStationService(), getTourService(), getAssetService(), getTextService(), getLanguageService(), getPinService(), getServerService(), getShareService(), getStorage()
App servicesgetAudioPlayerService(), getMenuService(), getRoutingService(), getLoadService()
LanguagechangeLanguage(code), getLanguages()
Texts__(key)
RoutingcanLoadRoute(), getPendingRoute()

Texts​

__(key) returns the text for key in the current language, trying the key with - and _ swapped if it is not there as written. Before a language is chosen it returns the key unchanged; for a key missing from the content, the key lower-cased with its first hyphen and its first underscore each turned into a space.

The route guard​

canLoadRoute() is meant for beforeEnter on routes that need content. When the content is loaded it returns true. Otherwise it stores the requested route and returns { redirect: '/' }; the loading page reads the stored route with getPendingRoute() and continues there once loading is done. That is what makes a deep link work on a fresh device.

The audio player​

AudioPlayerService plays a list of stations through @smartcompanion/native-audio-player, which uses the platform's audio session in a native app and an HTML audio element in a browser.

  • start(stations) loads the stations as the playlist — one item per audio asset, titled with the asset's or the station's title, with the station's first image as artwork and the registered subtitle underneath.
  • select(index), play(), pause(), seek(position), next(), prev(), stop() control it.
  • registerUpdateListener(callback) reports playing, paused, skip and completed.
  • setEarpiece() and setSpeaker() switch the output in a native app.

The collectible player, CollectibleAudioPlayerService, additionally tracks how much of each station has been listened to. It reports a collected update with the percentage, which the pages store through StationService and show as the progress ring.

The pages drive the player themselves; an app only registers it.

The menu​

MenuService opens, closes, enables and disables the app's ion-menu: open(), close(), enable(), disable(). The loading, language, PIN and error pages disable the menu; the content pages enable it again.

It talks to the ion-menu element itself, never to Ionic's menuController. A Stencil app compiles Ionic's components from source, while import { menuController } from '@ionic/core' resolves to Ionic's standalone build — a second controller no menu registers with. For the same reason services imports Ionic for its types only.

Routing​

RoutingService navigates between pages: push(), replace(), back(), canGoBack() and addRouteChangeListener(). It has a page of its own: Routing.