Skip to content

Jl/refactor fetch hotspots #277

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/config-defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ export const coreUrl = 'https://squid-api.tjek.com';
export const coreUrlStaging = 'https://squid-api.tjek-staging.com';
export const eventsTrackUrl = 'https://wolf-api.tjek.com/sync';
export const eventsTrackUrlStaging = 'https://wolf-api.tjek-staging.com/sync';
export const viewerUrl = 'https://publication-viewer.tjek.com';
export const viewerUrlStaging = 'https://publication-viewer.tjek-staging.com';
8 changes: 7 additions & 1 deletion lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import MicroEvent from '../vendor/microevent';
import * as configDefaults from './config-defaults';

class Config extends MicroEvent<{change: [Record<string, any>]}> {
keys = ['apiKey', 'eventTracker', 'coreUrl', 'eventsTrackUrl'] as const;
keys = [
'apiKey',
'eventTracker',
'coreUrl',
'eventsTrackUrl',
'viewerUrl'
] as const;
_attrs = {...configDefaults};

set(config: Record<string, any>) {
Expand Down
7 changes: 5 additions & 2 deletions lib/kits/core-ui/list-publications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ const ListPublications = (
mainContainer = '',
apiKey,
coreUrl,
eventTracker
eventTracker,
viewerUrl
}: {
mainContainer: string;
apiKey: string;
coreUrl: string;
eventTracker: Tracker;
viewerUrl: string | undefined;
}
) => {
const scriptEls = {
Expand Down Expand Up @@ -112,7 +114,8 @@ const ListPublications = (
mainContainer: '#sgn-publication-viewer-container',
apiKey,
coreUrl,
eventTracker
eventTracker,
viewerUrl
});

pagedPublication.setOptions({id});
Expand Down
52 changes: 35 additions & 17 deletions lib/kits/core-ui/paged-publication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ const PagedPublication = (
mainContainer = '',
apiKey,
coreUrl,
eventTracker
eventTracker,
viewerUrl
}: {
mainContainer: string;
apiKey: string;
coreUrl: string;
eventTracker: Tracker;
viewerUrl: string | undefined;
}
) => {
let options;
Expand Down Expand Up @@ -161,6 +163,7 @@ const PagedPublication = (
el: document.querySelector<HTMLDivElement>('.sgn__pp'),
apiKey,
coreUrl,
viewerUrl,
eventTracker,
pageId:
opts?.pageId ||
Expand Down Expand Up @@ -211,9 +214,6 @@ const PagedPublication = (
?.classList.add('sgn-paged-publication--open');
}

const hotspots = await bootstrapper.fetchHotspots();
bootstrapper.applyHotspots(sgnViewer, hotspots, sgnPageDecorations);

displayUrlParams();
addFirstLastControlListener();
};
Expand Down Expand Up @@ -311,20 +311,38 @@ const PagedPublication = (
'.sgn-pp__progress-label'
);

progressLabel?.addEventListener('DOMSubtreeModified', (e) => {
const pageNum = e.target.innerHTML
?.split(' ')?.[0]
?.split('-')?.[0];

if (scriptEls.displayUrlParams?.toLowerCase() === 'query') {
pushQueryParam({
[scriptEls.publicationIdParam]: options.id,
[scriptEls.pageIdParam]: pageNum
if (progressLabel) {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (
mutation.type === 'childList' ||
mutation.type === 'characterData'
) {
const pageNum = mutation.target.textContent
?.split(' ')?.[0]
?.split('-')?.[0];

if (
scriptEls.displayUrlParams?.toLowerCase() ===
'query'
) {
pushQueryParam({
[scriptEls.publicationIdParam]: options.id,
[scriptEls.pageIdParam]: pageNum
});
} else {
location.hash = `${scriptEls.publicationHash}/${options.id}/${pageNum}`;
}
}
});
} else {
location.hash = `${scriptEls.publicationHash}/${options.id}/${pageNum}`;
}
});
});

observer.observe(progressLabel, {
childList: true,
characterData: true,
subtree: true
});
}
}
};

Expand Down
17 changes: 17 additions & 0 deletions lib/kits/paged-publication/bootstrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface BootstrapperInit {
eventTracker: Tracker;
apiKey: string;
coreUrl: string;
viewerUrl: string | undefined;
keyboard: 'disabled' | 'enabled' | 'global';
}
export default class Bootstrapper {
Expand All @@ -30,6 +31,8 @@ export default class Bootstrapper {
keyboard: this.options.keyboard ?? 'enabled',
pageId: this.options.pageId,
eventTracker: this.options.eventTracker,
fetchPageHotspotsAndDecorations:
this.fetchPageHotspotsAndDecorations,
pages: data.pages.map(({view, zoom}, i) => {
const pageNumber = i + 1;

Expand Down Expand Up @@ -170,4 +173,18 @@ export default class Bootstrapper {
applyPageDecorations(viewer: Viewer, pageDecorations: V2PageDecoration[]) {
viewer.applyPageDecorations(pageDecorations);
}

fetchPageHotspotsAndDecorations = (pageId: number) => {
if (this.options.viewerUrl) {
return request({
apiKey: this.options.apiKey,
coreUrl: this.options.viewerUrl,
url: `/api/paged-publications/${this.options.id}/${pageId}`
});
}

return false;

// this.applyHotspots(hotspots);
};
}
16 changes: 14 additions & 2 deletions lib/kits/paged-publication/hotspots.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import MicroEvent from '../../../vendor/microevent';
import PageSpread from '../../verso-browser/page_spread';
import * as translations from '../../translations';
import {V2Hotspot} from '../core';
import PagedPublicationPageSpread from './page-spread';
import {Page} from './page-spreads';
import {translate} from '../core-ui/components/helpers/component';

function getPosition(pages: Page[], ratio: number, hotspot: V2Hotspot) {
let minX: number | null = null;
Expand Down Expand Up @@ -98,10 +98,22 @@ function renderHotspot(hotspot, position, contentRect, boundingRect) {
el.style.height = `${height}px`;

if (hotspot.type === 'offer') {
const translations = {
for: translate('publication_viewer_offer_price_for'),
description: translate('publication_viewer_offer_description')
};
const pieceCountFor =
hotspot.offer.quantity.pieces.from > 1
? `${hotspot.offer.quantity.pieces.from} ${translations.for} `
: '';
const description = hotspot.offer.description
? `${translations.description}: ${hotspot.offer.description}`
: '';

el.setAttribute('tabindex', '-1');
el.setAttribute(
'aria-label',
`${hotspot.offer.heading}; ${hotspot.offer.pricing.price} ${hotspot.offer.pricing.currency};`
`${hotspot.offer.heading}; ${pieceCountFor}${hotspot.offer.pricing.price} ${hotspot.offer.pricing.currency}; ${description};`
);

const observer = new IntersectionObserver((entries) => {
Expand Down
131 changes: 126 additions & 5 deletions lib/kits/paged-publication/viewer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import MicroEvent from '../../../vendor/microevent';
import * as translations from '../../translations';
import Verso from '../../verso-browser/verso';
import {V2Hotspot, V2PageDecoration} from '../core';
import {V2Hotspot, V2PageDecoration, request} from '../core';
import PageDecorations from '../core-ui/page-decorations';
import singleChoicePopover from '../core-ui/single-choice-popover';
import {Tracker} from '../events';
Expand All @@ -25,7 +25,7 @@ function defaultPickHotspot(
x: e.verso.x,
y: e.verso.y,
items: hotspots
.filter((hotspot) => hotspot.type === 'offer')
.filter((hotspot) => hotspot?.type === 'offer')
.map((hotspot) => ({
id: hotspot.id,
title: hotspot.offer.heading,
Expand All @@ -42,6 +42,28 @@ function defaultPickHotspot(

return popover.destroy;
}

export interface V3Hotspot {
x1: number;
y1: number;
x2: number;
y2: number;
link: string | undefined;
offer:
| {
id: string;
name: string;
description: string | undefined;
}
| undefined;
rotate: number | undefined;
}

export interface PageHotspotsAndDecorations {
hotspots: V3Hotspot[];
pageDecorations: V2PageDecoration[];
}

export interface ViewerInit {
id: string;
ownedBy: unknown;
Expand All @@ -56,6 +78,7 @@ export interface ViewerInit {
keyboard: 'disabled' | 'enabled' | 'global';
hotspotRatio?: number;
pickHotspot?: typeof defaultPickHotspot;
fetchPageHotspotsAndDecorations: any;
}
class Viewer extends MicroEvent {
_hotspots = new Hotspots();
Expand Down Expand Up @@ -179,11 +202,14 @@ class Viewer extends MicroEvent {
this._eventTracking.trigger('zoomedOut', e);
this.trigger('zoomedOut', e);
});
this._core.bind('pageLoaded', (e) => {
this._core.bind('pageLoaded', async (e) => {
this._eventTracking.trigger('pageLoaded', e);
this.trigger('pageLoaded', e);
});
this._core.bind('pagesLoaded', (e) => {
this._core.bind('pagesLoaded', async (e) => {
for (const page of e.pages) {
await this.processPageHotspotsAndDecorations(page.pageNumber);
}
this._hotspots.trigger('pagesLoaded', e);
this.trigger('pagesLoaded', e);
});
Expand Down Expand Up @@ -348,7 +374,7 @@ class Viewer extends MicroEvent {
});
}

hotspotsRequested = (e) => {
hotspotsRequested = async (e) => {
this.hotspotQueue.push(e);
this.processHotspotQueue();
};
Expand Down Expand Up @@ -443,6 +469,101 @@ class Viewer extends MicroEvent {
this.trigger('hotspotPressed', hotspot);
});
};

transformPageHotspotsAndDecorations = (
pageHotspotsAndDecorations: PageHotspotsAndDecorations,
pageNumber: number
) => {
const {hotspots, pageDecorations} = pageHotspotsAndDecorations;

const transformedToV2Hotspots = hotspots.map((hotspot) => ({
heading: hotspot.offer?.name || '',
id: hotspot.offer?.id || '',
type: 'offer',
locations: {
[pageNumber]: [
[
hotspot.x1 / 100,
(hotspot.y1 * (this.options?.hotspotRatio || 1)) / 100
],
[
hotspot.x1 / 100,
(hotspot.y2 * (this.options?.hotspotRatio || 1)) / 100
],
[
hotspot.x2 / 100,
(hotspot.y2 * (this.options?.hotspotRatio || 1)) / 100
],
[
hotspot.x2 / 100,
(hotspot.y1 * (this.options?.hotspotRatio || 1)) / 100
]
]
},
link: hotspot.link || '',
embed_link: null,
rotate: hotspot.rotate || 0,
webshop: null,
offer: {
id: hotspot.offer?.id || '',
ern: '',
heading: hotspot.offer?.name || '',
description: hotspot.offer?.description || '',
pricing: {
currency: '',
price: 0
},
quantity: {
pieces: {
from: 1,
to: 1
}
},
run_from: '',
run_till: '',
publish: ''
}
}));

const transformedHotspots = transformedToV2Hotspots.reduce(
(obj, hotspot) => {
obj[hotspot.id] = hotspot;

return obj;
},
{}
) as typeof this.hotspots;

return {
hotspots: transformedHotspots,
pageDecorations
};
};

processPageHotspotsAndDecorations = async (pageNumber: number) => {
const hotspots = await this.options.fetchPageHotspotsAndDecorations(
pageNumber
);

if (!hotspots) {
return;
}

const transformedHotspots = this.transformPageHotspotsAndDecorations(
hotspots,
pageNumber
);

if (this.hotspots) {
for (const id in transformedHotspots.hotspots) {
if (!this.hotspots[id]) {
this.hotspots[id] = transformedHotspots.hotspots[id];
}
}
} else {
this.hotspots = transformedHotspots.hotspots;
}
};
}

export default Viewer;
Loading