Jak BBC Interactive Content działa w AMP, aplikacjach i internecie

Opublikowany: 2022-03-10
Szybkie podsumowanie ↬ Publikowanie treści na tak wielu nośnikach bez dodatkowych kosztów związanych z programowaniem może być trudne. Chris Ashton wyjaśnia, jak podeszli do problemu w dziale dziennikarstwa wizualnego BBC.

W zespole Visual Journalism w BBC tworzymy ekscytujące wizualne, angażujące i interaktywne treści, od kalkulatorów po wizualizacje, nowe formaty opowiadania historii.

Każda aplikacja jest unikalnym wyzwaniem do samodzielnego tworzenia, ale tym bardziej, jeśli weźmiesz pod uwagę, że większość projektów musimy wdrażać w wielu różnych językach. Nasze treści muszą działać nie tylko w witrynach BBC News i Sports, ale także w odpowiadających im aplikacjach na iOS i Androida, a także w witrynach stron trzecich, które wykorzystują treści BBC.

Teraz weź pod uwagę, że istnieje coraz więcej nowych platform , takich jak AMP, artykuły na Facebooku i Apple News. Każda platforma ma swoje ograniczenia i autorski mechanizm publikowania. Tworzenie interaktywnych treści, które działają we wszystkich tych środowiskach, to prawdziwe wyzwanie. Opiszę, jak podeszliśmy do problemu w BBC.

Przykład: kanoniczne a AMP

To wszystko jest trochę teoretyczne, dopóki nie zobaczysz tego w akcji, więc zagłębimy się w przykład.

Oto artykuł BBC zawierający treści dotyczące dziennikarstwa wizualnego:

Zrzut ekranu strony BBC News zawierającej treści Visual Journalism
Nasze treści dotyczące dziennikarstwa wizualnego zaczynają się od ilustracji Donalda Trumpa i znajdują się w ramce iframe

Jest to kanoniczna wersja artykułu, tj. wersja domyślna, którą otrzymasz, jeśli przejdziesz do artykułu ze strony głównej.

Więcej po skoku! Kontynuuj czytanie poniżej ↓

Spójrzmy teraz na wersję AMP artykułu:

Zrzut ekranu strony BBC News AMP zawierającej tę samą treść co poprzednio, ale treść jest przycięta i ma przycisk Pokaż więcej
Wygląda na tę samą treść co normalny artykuł, ale pobiera inny element iframe zaprojektowany specjalnie dla AMP

Chociaż wersje kanoniczna i AMP wyglądają tak samo, w rzeczywistości są to dwa różne punkty końcowe o różnym zachowaniu:

  • Wersja kanoniczna przewinie Cię do wybranego kraju po przesłaniu formularza.
  • Wersja AMP Cię nie przewija, ponieważ nie możesz przewijać strony nadrzędnej z elementu iframe AMP.
  • Wersja AMP wyświetla przycięty element iframe z przyciskiem „Pokaż więcej”, w zależności od rozmiaru widocznego obszaru i pozycji przewijania. To jest cecha AMP.

Oprócz wersji kanonicznej i AMP tego artykułu, projekt ten został również przesłany do aplikacji News, która jest kolejną platformą z własnymi zawiłościami i ograniczeniami. Jak więc obsługujemy wszystkie te platformy?

Narzędzia są kluczowe

Nie tworzymy naszych treści od podstaw. Mamy rusztowanie oparte na Yeoman, które wykorzystuje Node do generowania projektu wzorcowego za pomocą jednego polecenia.

Nowe projekty są dostarczane z pakietem Webpack, SASS, wdrażaniem i strukturą komponentyzacji po wyjęciu z pudełka. Internacjonalizacja jest również wpajana w nasze projekty za pomocą systemu szablonów Handlebars. Tom Maslen pisze o tym szczegółowo w swoim poście, 13 porad dotyczących tworzenia wielojęzycznego projektowania responsywnego.

Po wyjęciu z pudełka działa to całkiem dobrze w przypadku kompilacji na jednej platformie, ale musimy obsługiwać wiele platform . Zagłębmy się w jakiś kod.

Osadź a samodzielny

W dziennikarstwie wizualnym czasami wyświetlamy nasze treści w ramce iframe, aby mogły one stanowić samodzielne „osadzenie” w artykule, na które nie mają wpływu globalne skrypty i style. Przykładem tego jest interaktywność Donalda Trumpa osadzona w kanonicznym przykładzie wcześniej w tym artykule.

Z drugiej strony czasami wysyłamy naszą zawartość jako surowy kod HTML. Robimy to tylko wtedy, gdy mamy kontrolę nad całą stroną lub jeśli potrzebujemy naprawdę responsywnej interakcji przewijania. Nazwijmy je odpowiednio naszymi wyjściami „embed” i „standalone”.

Wyobraźmy sobie, jak możemy zbudować „Czy robot przyjmie twoją pracę?” interaktywne zarówno w formacie „osadzonym”, jak i „samodzielnym”.

Dwa zrzuty ekranu obok siebie. Jeden pokazuje zawartość osadzoną na stronie; druga pokazuje tę samą treść, co strona sama w sobie.
Wymyślny przykład przedstawiający „umieszczanie” po lewej stronie w porównaniu z treścią jako „samodzielną” stroną po prawej stronie

Obie wersje treści współdzielą zdecydowaną większość swojego kodu, ale istniałyby pewne istotne różnice w implementacji JavaScript między tymi dwiema wersjami.

Na przykład spójrz na przycisk „Sprawdź moje ryzyko automatyzacji”. Gdy użytkownik kliknie przycisk przesyłania, powinien zostać automatycznie przewinięty do jego wyników.

„Samodzielna” wersja kodu może wyglądać tak:

 button.on('click', (e) => { window.scrollTo(0, resultsContainer.offsetTop); });

Ale jeśli kompilowałeś to jako wyjście „osadzone”, wiesz, że twoja treść znajduje się w iframe, więc musiałbyś ją zakodować w inny sposób:

 // inside the iframe button.on('click', () => { window.parent.postMessage({ name: 'scroll', offset: resultsContainer.offsetTop }, '*'); }); // inside the host page window.addEventListener('message', (event) => { if (event.data.name === 'scroll') { window.scrollTo(0, iframe.offsetTop + event.data.offset); } });

A co, jeśli nasza aplikacja musi działać na pełnym ekranie? Jest to dość łatwe, jeśli jesteś na „samodzielnej” stronie:

 document.body.className += ' fullscreen';
 .fullscreen { position: fixed; top: 0; left: 0; right: 0; bottom: 0; } 
Zrzut ekranu z osadzoną mapą z nakładką „Dotknij, aby wejść w interakcję”, a następnie zrzut ekranu mapy w trybie pełnoekranowym po jej dotknięciu.
Z powodzeniem wykorzystujemy pełnoekranową funkcjonalność, aby w pełni wykorzystać nasz moduł map na urządzeniach mobilnych

Gdybyśmy spróbowali zrobić to z wnętrza „embed”, ten sam kod miałby skalowanie treści do szerokości i wysokości elementu iframe , a nie widocznego obszaru:

Zrzut ekranu przykładu mapy jak poprzednio, ale tryb pełnoekranowy jest wadliwy. Tekst z otaczającego artykułu jest widoczny tam, gdzie nie powinien.
Przejście na pełny ekran z elementu iframe może być trudne

…więc oprócz zastosowania stylizacji pełnoekranowej wewnątrz elementu iframe, musimy wysłać wiadomość do strony hosta, aby zastosować stylizację do samego elementu iframe:

 // iframe window.parent.postMessage({ name: 'window:toggleFullScreen' }, '*'); // host page window.addEventListener('message', function () { if (event.data.name === 'window:toggleFullScreen') { document.getElementById(iframeUid).className += ' fullscreen'; } });

Może to przełożyć się na dużo kodu spaghetti, gdy zaczniesz obsługiwać wiele platform:

 button.on('click', (e) => { if (inStandalonePage()) { window.scrollTo(0, resultsContainer.offsetTop); } else { window.parent.postMessage({ name: 'scroll', offset: resultsContainer.offsetTop }, '*'); } });

Wyobraź sobie, że robisz ekwiwalent tego dla każdej znaczącej interakcji DOM w swoim projekcie. Kiedy skończysz drżeć, zrób sobie relaksującą filiżankę herbaty i czytaj dalej.

Abstrakcja jest kluczem

Zamiast zmuszać naszych programistów do obsługi tych warunków w ich kodzie, zbudowaliśmy warstwę abstrakcji między ich treścią a środowiskiem. Nazywamy tę warstwę „opakowaniem”.

Zamiast bezpośrednio odpytywać zdarzenia DOM lub natywnej przeglądarki, możemy teraz przekazywać nasze żądanie za pośrednictwem modułu wrapper .

 import wrapper from 'wrapper'; button.on('click', () => { wrapper.scrollTo(resultsContainer.offsetTop); });

Każda platforma ma własną implementację opakowującą zgodną ze wspólnym interfejsem metod opakowujących. Opakowanie owija się wokół naszej zawartości i obsługuje za nas złożoność.

Diagram UML pokazujący, że kiedy nasza aplikacja wywołuje samodzielną metodę wrapper scroll, wrapper wywołuje natywną metodę scroll na stronie hosta.
Prosta implementacja 'scrollTo' przez samodzielny wrapper

Implementacja funkcji scrollTo w samodzielnym opakowaniu jest bardzo prosta, przekazując nasz argument bezpośrednio do window.scrollTo pod maską.

Przyjrzyjmy się teraz osobnemu wrapperowi implementującemu tę samą funkcjonalność dla elementu iframe:

Diagram UML pokazujący, że gdy nasza aplikacja wywołuje metodę przewijania embed wrapper, embed wrapper łączy żądaną pozycję przewijania z przesunięciem iframe przed wyzwoleniem natywnej metody przewijania na stronie hosta.
Zaawansowana implementacja 'scrollTo' przez embed wrapper

Opakowanie „embed” przyjmuje ten sam argument, co w przykładzie „standalone”, ale manipuluje wartością tak, aby uwzględnić przesunięcie iframe. Bez tego dodatku przesunęlibyśmy naszego użytkownika w zupełnie niezamierzone miejsce.

Wzór opakowania

Używanie wrapperów powoduje, że kod jest czystszy, bardziej czytelny i spójny między projektami. Pozwala również na mikrooptymalizacje w miarę upływu czasu, ponieważ wprowadzamy stopniowe ulepszenia opakowań, aby ich metody były bardziej wydajne i dostępne. Twój projekt może zatem korzystać z doświadczenia wielu programistów.

Jak więc wygląda opakowanie?

Struktura opakowania

Każde opakowanie zasadniczo składa się z trzech rzeczy: szablonu Handlebars, pliku JS opakowania i pliku SASS oznaczającego stylizację specyficzną dla opakowania. Dodatkowo istnieją zadania kompilacji, które łączą się ze zdarzeniami ujawnionymi przez podstawowe rusztowanie, dzięki czemu każde opakowanie jest odpowiedzialne za własną wstępną kompilację i czyszczenie.

To jest uproszczony widok otoki osadzania:

 embed-wrapper/ templates/ wrapper.hbs js/ wrapper.js scss/ wrapper.scss

Nasze podstawowe rusztowanie udostępnia główny szablon projektu jako podzespół Handlebars, który jest używany przez opakowanie. Na przykład templates/wrapper.hbs może zawierać:

 <div class="bbc-news-vj-wrapper--embed"> {{>your-application}} </div>

scss/wrapper.scss zawiera stylizację specyficzną dla otoki, której kod aplikacji nie powinien sam definiować. Na przykład embed wrapper powiela wiele stylów BBC News wewnątrz elementu iframe.

Wreszcie, js/wrapper.js zawiera implementację iframed wrapper API, opisaną poniżej. Jest on dostarczany osobno do projektu, a nie kompilowany z kodem aplikacji — w naszym procesie budowania pakietu Webpack oznaczamy wrapper jako globalny. Oznacza to, że chociaż dostarczamy naszą aplikację na wiele platform, kod kompilujemy tylko raz.

Opakowania API

Opakowania API abstrahuje szereg kluczowych interakcji przeglądarki. Oto najważniejsze:

scrollTo(int)

Przewija do podanej pozycji w aktywnym oknie. Opakowanie znormalizuje podaną liczbę całkowitą przed wyzwoleniem przewijania, aby strona hosta została przewinięta do właściwej pozycji.

getScrollPosition: int

Zwraca bieżącą (znormalizowaną) pozycję przewijania użytkownika. W przypadku elementu iframe oznacza to, że pozycja przewijania przekazana do aplikacji jest w rzeczywistości ujemna , dopóki element iframe nie znajdzie się u góry widocznego obszaru. Jest to bardzo przydatne i pozwala nam robić takie rzeczy, jak animowanie komponentu tylko wtedy, gdy jest on widoczny.

onScroll(callback)

Zapewnia zaczep do zdarzenia przewijania. W samodzielnym opakowaniu jest to zasadniczo podpięcie do zdarzenia przewijania natywnego. W opakowaniu osadzania wystąpi niewielkie opóźnienie w odebraniu zdarzenia przewijania, ponieważ jest ono przekazywane przez postMessage.

viewport: {height: int, width: int}

Metoda pobierania wysokości i szerokości widocznego obszaru (ponieważ jest to implementowane zupełnie inaczej w przypadku zapytania z elementu iframe).

toggleFullScreen

W trybie samodzielnym ukrywamy menu BBC i stopkę z widoku i ustawiamy position: fixed na naszej treści. W aplikacji News nie robimy nic — treść jest już na pełnym ekranie. Skomplikowany jest element iframe, który polega na stosowaniu stylów zarówno wewnątrz, jak i na zewnątrz iframe, koordynowanych przez postMessage.

markPageAsLoaded

Poinformuj opakowanie, że Twoja treść została załadowana. Ma to kluczowe znaczenie, aby nasze treści działały w aplikacji Wiadomości, która nie będzie próbowała wyświetlać naszych treści użytkownikowi, dopóki wyraźnie nie poinformujemy aplikacji, że nasze treści są gotowe. Usuwa również pokrętło ładowania w internetowych wersjach naszych treści.

Lista opakowań

W przyszłości przewidujemy stworzenie dodatkowych wrapperów dla dużych platform, takich jak Facebook Instant Article i Apple News. Do tej pory stworzyliśmy sześć owijarek:

Samodzielna owijarka

Wersja naszych treści, która powinna znaleźć się na samodzielnych stronach. W zestawie z brandingiem BBC.

Osadź opakowanie

Wersja naszych treści w ramce iframe, którą można bezpiecznie umieszczać w artykułach lub dystrybuować do witryn innych niż BBC, ponieważ zachowujemy kontrolę nad treścią.

Opakowanie AMP

To jest punkt końcowy, który jest wciągany jako element amp-iframe na strony AMP.

Opakowanie aplikacji wiadomości

Nasze treści muszą nawiązywać połączenia z zastrzeżonym protokołem bbcvisualjournalism:// .

Owijarka rdzenia

Zawiera tylko kod HTML — nie zawiera CSS ani JavaScript naszego projektu.

Opakowanie JSON

Reprezentacja JSON naszych treści do udostępniania w produktach BBC.

Owijarki przewodów do platform

Aby nasze treści pojawiały się na stronie BBC, zapewniamy dziennikarzom ścieżkę z przestrzenią nazw:

 /include/[department]/[unique ID], eg /include/visual-journalism/123-quiz

Dziennikarz umieszcza tę „ścieżkę włączenia” w CMS, który zapisuje strukturę artykułu w bazie danych. Wszystkie produkty i usługi znajdują się za tym mechanizmem publikowania. Każda platforma jest odpowiedzialna za wybór żądanej treści i żądanie tej treści z serwera proxy.

Weźmy ten interaktywny Donald Trump z wcześniej. Tutaj ścieżka dołączania w CMS to:

 /include/newsspec/15996-trump-tracker/english/index

Kanoniczna strona artykułu wie, że chce „embed” wersji treści, więc dołącza /embed do ścieżki include:

 /include/newsspec/15996-trump-tracker/english/index /embed

…zanim zażądasz tego z serwera proxy:

 https://news.files.bbci.co.uk/include/newsspec/15996-trump-tracker/english/index/embed

Z drugiej strony strona AMP widzi ścieżkę include i dołącza /amp :

 /include/newsspec/15996-trump-tracker/english/index /amp

Renderer AMP robi trochę magii, aby renderować niektóre AMP HTML, które odwołują się do naszej treści, pobierając wersję /amp jako element iframe:

 <amp-iframe src="https://news.files.bbci.co.uk/include/newsspec/15996-trump-tracker/english/index/amp" width="640" height="360"> <!-- some other AMP elements here --> </amp-iframe>

Każda obsługiwana platforma ma swoją własną wersję zawartości:

 /include/newsspec/15996-trump-tracker/english/index /amp

/include/newsspec/15996-trump-tracker/english/index /core

/include/newsspec/15996-trump-tracker/english/index /envelope

...i tak dalej

To rozwiązanie można skalować w celu uwzględnienia większej liczby typów platform w miarę ich pojawiania się.

Abstrakcja jest trudna

Budowanie architektury „napisz raz, wdrażaj gdziekolwiek” brzmi dość idealistycznie i tak jest. Aby architektura opakowująca działała, musimy być bardzo rygorystyczni w pracy z abstrakcją. Oznacza to, że musimy walczyć z pokusą „zrobienia tej dziwacznej rzeczy, aby zadziałało w [tu wstaw nazwę platformy]”. Chcemy, aby nasze treści były całkowicie nieświadome środowiska, w którym są dostarczane — ale łatwiej to powiedzieć niż zrobić.

Cechy platformy są trudne do abstrakcyjnego skonfigurowania

Przed podejściem do abstrakcji mieliśmy pełną kontrolę nad każdym aspektem naszego wyjścia, w tym na przykład znacznikiem naszego iframe. Jeśli musielibyśmy coś poprawić dla każdego projektu, na przykład dodać atrybut title do elementu iframe ze względu na ułatwienia dostępu, moglibyśmy po prostu edytować znacznik.

Teraz, gdy znacznik opakowujący istnieje w izolacji od projektu, jedynym sposobem na jego skonfigurowanie byłoby ujawnienie haka w samym rusztowaniu. Możemy to zrobić stosunkowo łatwo w przypadku funkcji międzyplatformowych, ale ujawnienie haków dla określonych platform przełamuje abstrakcję. Tak naprawdę nie chcemy ujawniać opcji konfiguracyjnej „iframe title”, która jest używana tylko przez jedno opakowanie.

Moglibyśmy nazwać właściwość bardziej ogólnie, np. title , a następnie użyć tej wartości jako atrybutu title iframe. Jednak śledzenie tego, co i gdzie jest używane, staje się trudne i ryzykujemy abstrakcję naszej konfiguracji do tego stopnia, że ​​przestaniemy ją rozumieć. Ogólnie rzecz biorąc, staramy się, aby nasza konfiguracja była jak najszczuplejsza, ustawiając tylko te właściwości, które mają zastosowanie globalne.

Zachowanie komponentów może być złożone

W sieci nasz moduł narzędzi do udostępniania udostępnia przyciski udostępniania w sieciach społecznościowych, które można indywidualnie kliknąć i otwierają wstępnie wypełnioną wiadomość udostępniania w nowym oknie.

Zrzut ekranu sekcji BBC sharetools zawierającej ikony mediów społecznościowych Twittera i Facebooka.
Sharetools BBC Visual Journalism przedstawia listę opcji na akcje społecznościowe

W aplikacji News nie chcemy udostępniać treści przez internet mobilny. Jeśli użytkownik ma zainstalowaną odpowiednią aplikację (np. Twitter), chcemy udostępnić samą aplikację. Idealnie, chcemy zaprezentować użytkownikowi natywne menu udostępniania iOS/Android, a następnie pozwolić mu wybrać opcję udostępniania, zanim otworzymy dla niego aplikację z wstępnie wypełnioną wiadomością o udostępnieniu. Możemy uruchomić natywne menu udostępniania z aplikacji, wywołując zastrzeżony protokół bbcvisualjournalism:// .

Zrzut ekranu menu udostępniania na Androidzie z opcjami udostępniania przez Wiadomości, Bluetooth, Kopiuj do schowka i tak dalej.
Natywne menu udostępniania na Androidzie

Jednak ten ekran zostanie wywołany bez względu na to, czy klikniesz „Twitter” lub „Facebook” w sekcji „Udostępnij swoje wyniki”, więc użytkownik będzie musiał dwukrotnie dokonać wyboru; pierwszy raz w naszej treści, a drugi raz w natywnym wyskakującym okienku.

To dziwna podróż użytkownika, dlatego chcemy usunąć poszczególne ikony udostępniania z aplikacji Wiadomości i zamiast tego wyświetlić ogólny przycisk udostępniania. Możemy to zrobić, jawnie sprawdzając, który wrapper jest używany, zanim wyrenderujemy komponent.

Zrzut ekranu przycisku udostępniania wiadomości w aplikacji. Jest to pojedynczy przycisk z następującym tekstem: „Podziel się, jak to zrobiłeś”.
Ogólny przycisk udostępniania używany w aplikacji News

Budowanie warstwy abstrakcji otoki działa dobrze w przypadku projektów jako całości, ale gdy wybór otoki wpływa na zmiany na poziomie komponentów , bardzo trudno jest zachować czystą abstrakcję. W tym przypadku straciliśmy trochę abstrakcji i mamy trochę niechlujnej logiki rozwidlania w naszym kodzie. Na szczęście te przypadki są bardzo nieliczne.

Jak radzimy sobie z brakującymi funkcjami?

Utrzymywanie abstrakcji jest dobre i dobre. Nasz kod mówi wrapperowi, co ma zrobić platforma, np. „przejść na pełny ekran”. Ale co, jeśli platforma, na którą wysyłamy, nie może działać na pełnym ekranie?

Opakowanie postara się nie zepsuć całkowicie, ale ostatecznie potrzebujesz projektu, który z wdziękiem powraca do działającego rozwiązania, niezależnie od tego, czy metoda się powiedzie, czy nie. Musimy projektować defensywnie.

Załóżmy, że mamy sekcję wyników zawierającą kilka wykresów słupkowych. Często lubimy utrzymywać wartości na wykresie słupkowym na poziomie zerowym, dopóki wykresy nie zostaną przewinięte do widoku, w którym to momencie uruchamiamy animacje słupków do ich prawidłowej szerokości.

Zrzut ekranu przedstawiający zbiór wykresów słupkowych porównujących obszar użytkownika ze średnimi krajowymi. Każdy słupek ma swoją wartość wyświetlaną jako tekst po prawej stronie słupka.
Wykres słupkowy przedstawiający wartości istotne dla mojego obszaru

Ale jeśli nie mamy mechanizmu umożliwiającego zaczepienie się w pozycji przewijania — jak to ma miejsce w naszym opakowaniu AMP — wtedy paski na zawsze pozostaną na poziomie zera, co jest całkowicie mylącym doświadczeniem.

Ten sam zrzut ekranu wykresów słupkowych co poprzednio, ale słupki mają 0&#37; szerokość i wartości każdego słupka są ustalone na 0&#37;. To jest niepoprawne.
Jak mógłby wyglądać wykres słupkowy, gdyby zdarzenia przewijania nie były przekazywane dalej

Coraz częściej staramy się stosować w naszych projektach bardziej progresywne podejście do ulepszania. Na przykład moglibyśmy zapewnić przycisk, który będzie domyślnie widoczny dla wszystkich platform, ale który zostanie ukryty, jeśli opakowanie obsługuje przewijanie. W ten sposób, jeśli przewijanie nie uruchomi animacji, użytkownik nadal może ręcznie uruchomić animację.

Ten sam zrzut ekranu wykresów słupkowych, co niepoprawne 0&#37; wykresy słupkowe, ale tym razem z subtelną szarą nakładką i wyśrodkowanym przyciskiem zapraszającym do „Wyświetl wyniki”.
Zamiast tego moglibyśmy wyświetlić przycisk awaryjny, który uruchamia animację po kliknięciu.

Plany na przyszłość

Mamy nadzieję opracować nowe opakowania dla platform takich jak Apple News i Facebook Instant Artykuły, a także zaoferować wszystkim nowym platformom „podstawową” wersję naszych treści po wyjęciu z pudełka.

Mamy również nadzieję, że poprawimy się w progresywnym ulepszaniu; sukces na tym polu oznacza rozwój defensywny. Nigdy nie można zakładać, że wszystkie platformy teraz i w przyszłości będą obsługiwać daną interakcję, ale dobrze zaprojektowany projekt powinien być w stanie przekazać główne przesłanie bez pokonywania pierwszej przeszkody technicznej.

Praca w ramach opakowania to trochę zmiana paradygmatu i wydaje się być trochę w połowie drogi, jeśli chodzi o długoterminowe rozwiązanie. Ale dopóki branża nie dojrzeje do standardu wieloplatformowego, wydawcy będą zmuszeni do wdrażania własnych rozwiązań lub używania narzędzi takich jak Distro do konwersji z platformy na platformę, lub też całkowicie ignorować całe sekcje swoich odbiorców.

To dla nas dopiero początek, ale jak dotąd odnieśliśmy wielki sukces w wykorzystaniu wzorca wrappera do tworzenia naszych treści i dostarczania ich na niezliczone platformy, z których korzystają obecnie nasi widzowie.