Skip to content

Commit

Permalink
📐 Implement resize and related methods
Browse files Browse the repository at this point in the history
  • Loading branch information
viktor-rasevych-criteo committed Sep 21, 2023
1 parent 25af7e8 commit 820fdd5
Show file tree
Hide file tree
Showing 18 changed files with 1,478 additions and 27 deletions.
2 changes: 1 addition & 1 deletion build/criteo-mraid.js

Large diffs are not rendered by default.

12 changes: 10 additions & 2 deletions src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,25 @@ export enum MraidEvent {
Error = "error",
StateChange = "stateChange",
ViewableChange = "viewableChange",
SizeChange = "sizeChange",
}

export type EventListener<T1, T2> = (t1: T1, t2: T2) => void;
export type ErrorEventListener = EventListener<string, SafeString>;
export type StateChangeEventListener = EventListener<MraidState, void>;
export type ViewableChangeEventListener = EventListener<boolean, void>;
export type ReadyEventListener = EventListener<void, void>;
export type SizeChangeEventListener = EventListener<number, number>;

// Describes functions with possible parameters defined by MRAID spec
export type MraidEventListener =
| ErrorEventListener
| StateChangeEventListener
| ViewableChangeEventListener
| ReadyEventListener;
| ReadyEventListener
| SizeChangeEventListener;

export class EventsCoordinator {
// TODO: Object.values is ES2017. Check if it will work for Android and iOS
private eventListeners: Map<string, Set<MraidEventListener>> = new Map(
Object.values(MraidEvent).map((e) => [e, new Set()])
);
Expand Down Expand Up @@ -120,6 +122,12 @@ export class EventsCoordinator {
});
}

fireSizeChangeEvent(width: number, height: number) {
this.eventListeners.get(MraidEvent.SizeChange)?.forEach((value) => {
(value as SizeChangeEventListener)?.(width, height);
});
}

private isCorrectEvent(event: MraidEvent): boolean {
return event && this.eventListeners.has(event);
}
Expand Down
9 changes: 8 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { AndroidMraidBridge } from "./mraidbridge/androidmraidbridge";
import { SdkInteractor } from "./mraidbridge/sdkinteractor";
import { Logger } from "./log/logger";
import {} from "./mraidwindow";
import { ResizePropertiesValidator } from "./resize";

export {};

Expand All @@ -14,7 +15,13 @@ const sdkInteractor = new SdkInteractor([
]);
const eventsCoordinator = new EventsCoordinator();
const logger = new Logger(eventsCoordinator, sdkInteractor);
const resizePropertiesValidator = new ResizePropertiesValidator();

window.mraid =
window.mraid ??
new MRAIDImplementation(eventsCoordinator, sdkInteractor, logger);
new MRAIDImplementation(
eventsCoordinator,
sdkInteractor,
logger,
resizePropertiesValidator
);
114 changes: 113 additions & 1 deletion src/mraid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
SupportedSdkFeatures,
} from "./sdkfeature";
import { initialPosition, Position } from "./position";
import { ResizeProperties, ResizePropertiesValidator } from "./resize";

export class MRAIDImplementation implements MRAIDApi, SDKApi {
private eventsCoordinator: EventsCoordinator;
Expand All @@ -29,6 +30,8 @@ export class MRAIDImplementation implements MRAIDApi, SDKApi {

private logger: Logger;

private resizePropertiesValidator: ResizePropertiesValidator;

private currentState = MraidState.Loading;

private placementType = MraidPlacementType.Unknown;
Expand All @@ -52,14 +55,18 @@ export class MRAIDImplementation implements MRAIDApi, SDKApi {

private currentPosition = initialPosition.clone();

private currentResizeProperties?: ResizeProperties = undefined;

constructor(
eventsCoordinator: EventsCoordinator,
sdkInteractor: SdkInteractor,
logger: Logger
logger: Logger,
resizePropertiesValidator: ResizePropertiesValidator
) {
this.eventsCoordinator = eventsCoordinator;
this.sdkInteractor = sdkInteractor;
this.logger = logger;
this.resizePropertiesValidator = resizePropertiesValidator;

this.spreadMraidInstance();
}
Expand Down Expand Up @@ -271,6 +278,90 @@ export class MRAIDImplementation implements MRAIDApi, SDKApi {
}
}

getResizeProperties(): ResizeProperties | undefined {
return this.currentResizeProperties?.copy();
}

resize(): void {
if (
this.currentState !== MraidState.Resized &&
this.currentState !== MraidState.Default
) {
this.logger.log(
LogLevel.Error,
"resize",
`Can't resize in ${this.currentState} state`
);
return;
}

if (this.placementType !== MraidPlacementType.Inline) {
this.logger.log(
LogLevel.Error,
"resize",
"Resize is only available for inline placement"
);
return;
}

if (!this.currentResizeProperties) {
this.logger.log(
LogLevel.Error,
"resize",
"You must set resize properties before calling resize"
);
} else {
// validate resize properties one more time because position and max size
// might have changed by this time
const errorMessage = this.resizePropertiesValidator.validate(
this.currentResizeProperties,
this.currentMaxSize,
this.currentPosition
);
if (errorMessage) {
this.logger.log(LogLevel.Error, "resize", errorMessage);
return;
}

const {
offsetY,
height,
offsetX,
width,
customClosePosition,
allowOffscreen,
} = this.currentResizeProperties;
this.sdkInteractor.resize(
width,
height,
offsetX,
offsetY,
customClosePosition,
allowOffscreen
);
}
}

setResizeProperties(resizeProperties: ResizeProperties | Anything): void {
const errorMessage = this.resizePropertiesValidator.validate(
resizeProperties,
this.currentMaxSize,
this.currentPosition
);
if (errorMessage) {
this.logger.log(LogLevel.Error, "setResizeProperties", errorMessage);
} else {
this.currentResizeProperties = new ResizeProperties(
resizeProperties.width,
resizeProperties.height,
resizeProperties.offsetX,
resizeProperties.offsetY,
resizeProperties.customClosePosition,
resizeProperties.allowOffscreen
);
}
}

// #endregion

// #region SDKApi
Expand Down Expand Up @@ -340,6 +431,7 @@ export class MRAIDImplementation implements MRAIDApi, SDKApi {
"ad is already expanded"
);
break;
case MraidState.Resized:
case MraidState.Loading:
case MraidState.Hidden:
this.logger.log(
Expand Down Expand Up @@ -372,10 +464,30 @@ export class MRAIDImplementation implements MRAIDApi, SDKApi {
JSON.stringify(this.defaultPosition) === JSON.stringify(initialPosition)
) {
this.defaultPosition = newPosition;
} else {
// do we need size change for initial size?
this.eventsCoordinator.fireSizeChangeEvent(width, height);
}
this.currentPosition = newPosition;
}

notifyResized(): void {
switch (this.currentState) {
case MraidState.Default:
case MraidState.Resized: {
this.updateState(MraidState.Resized);
break;
}
default:
this.logger.log(
LogLevel.Warning,
"notifyResized",
`Can't resize from ${this.currentState} state`
);
break;
}
}

// #endregion

private updateState(newState: MraidState) {
Expand Down
18 changes: 18 additions & 0 deletions src/mraidapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ExpandProperties } from "./expand";
import { Size } from "./size";
import { SdkFeature } from "./sdkfeature";
import { Position } from "./position";
import { ResizeProperties } from "./resize";

export interface MRAIDApi {
/**
Expand Down Expand Up @@ -183,4 +184,21 @@ export interface MRAIDApi {
* @param url - the URI of the video or video stream
*/
playVideo(url: Url | Anything): void;

/**
* The resize method will cause the existing web view to change size using the existing HTML
* document
*/
resize(): void;

/**
* @returns current ResizeProperties object
*/
getResizeProperties(): ResizeProperties | undefined;

/**
* Sets resize properties object
* @param resizeProperties
*/
setResizeProperties(resizeProperties: ResizeProperties | Anything): void;
}
27 changes: 27 additions & 0 deletions src/mraidbridge/androidmraidbridge.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { MraidBridge } from "./mraidbridge";
import { LogLevel } from "../log/loglevel";
import { ClosePosition } from "../resize";

/**
* Interaction object is defined in global scope (window) so we are using
Expand Down Expand Up @@ -28,6 +29,14 @@ export declare interface CriteoInterface {
expand(width: number, height: number): void;
close(): void;
playVideo(url: string): void;
resize(
width: number,
height: number,
offsetX: number,
offsetY: number,
customClosePosition: string,
allowOffscreen: boolean
): void;
}

export class AndroidMraidBridge implements MraidBridge {
Expand All @@ -51,6 +60,24 @@ export class AndroidMraidBridge implements MraidBridge {
this.getMraidBridge()?.playVideo(url);
}

resize(
width: number,
height: number,
offsetX: number,
offsetY: number,
customClosePosition: ClosePosition,
allowOffscreen: boolean
): void {
this.getMraidBridge()?.resize(
width,
height,
offsetX,
offsetY,
customClosePosition,
allowOffscreen
);
}

private getMraidBridge(): CriteoInterface | undefined | null {
// criteoMraidBridge object is not injected into iframe on Android
// but doc says it should be. It is always available on topmost window
Expand Down
30 changes: 30 additions & 0 deletions src/mraidbridge/iosmraidbridge.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { MraidBridge } from "./mraidbridge";
import { LogLevel } from "../log/loglevel";
import { ClosePosition } from "../resize";

// #region MessageHandler

Expand Down Expand Up @@ -50,6 +51,15 @@ export declare interface PlayVideoIosMessage extends IosMessage {
url: string;
}

export declare interface ResizeIosMessage extends IosMessage {
width: number;
height: number;
offsetX: number;
offsetY: number;
customClosePosition: string;
allowOffscreen: boolean;
}

// #endregion

export class IosMraidBridge implements MraidBridge {
Expand Down Expand Up @@ -95,6 +105,26 @@ export class IosMraidBridge implements MraidBridge {
this.postMessage(playVideoMessage);
}

resize(
width: number,
height: number,
offsetX: number,
offsetY: number,
customClosePosition: ClosePosition,
allowOffscreen: boolean
): void {
const resizeMessage: ResizeIosMessage = {
action: "resize",
width,
height,
offsetX,
offsetY,
customClosePosition,
allowOffscreen,
};
this.postMessage(resizeMessage);
}

private postMessage(message: IosMessage) {
window?.webkit?.messageHandlers?.criteoMraidBridge?.postMessage(message);
}
Expand Down
9 changes: 9 additions & 0 deletions src/mraidbridge/mraidbridge.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { LogLevel } from "../log/loglevel";
import { ClosePosition } from "../resize";

/**
* Defines API for interaction with native platforms (iOS and Android)
Expand All @@ -10,4 +11,12 @@ export interface MraidBridge {
expand(width: number, height: number): void;
close(): void;
playVideo(url: string): void;
resize(
width: number,
height: number,
offsetX: number,
offsetY: number,
customClosePosition: ClosePosition,
allowOffscreen: boolean
): void;
}
Loading

0 comments on commit 820fdd5

Please sign in to comment.