Skip to content
32 changes: 32 additions & 0 deletions dist/leanplum.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export default class Leanplum {
* inconsistent state or user experience.
*/
static clearUserContent(): void;
static defineAction(options: MessageTemplateOptions): void;
static applyQueue(queue: Array<{
name: string;
args: Array<any>;
Expand Down Expand Up @@ -156,4 +157,35 @@ export interface WebPushOptions {
serviceWorkerUrl?: string;
scope?: string;
}
export enum ActionParameterType {
Int = "int",
Integer = "integer",
Color = "color",
Float = "float",
Decimal = "decimal",
Number = "number",
Boolean = "bool",
String = "string",
Text = "text",
HTML = "html",
File = "file",
List = "list",
Group = "group",
Action = "action",
Unknown = ""
}
export enum MessageKind {
Action = 2,
Template = 3
}
export type ActionParameter = {
name: string;
type: ActionParameterType;
value: string | boolean | number | Array<ActionParameter> | Record<string, ActionParameter>;
};
export type MessageTemplateOptions = {
name: string;
kind?: MessageKind;
args: Array<ActionParameter>;
};

4 changes: 2 additions & 2 deletions dist/leanplum.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/leanplum.min.js

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions src/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export default {
HASH: 'hash',
EMAIL: 'email',
VARIABLES: 'vars',
ACTION_DEFINITIONS: 'actionDefinitions',
PARAMS: 'params',
INCLUDE_DEFAULTS: 'includeDefaults',
INCLUDE_VARIANT_DEBUG_INFO: 'includeVariantDebugInfo',
Expand All @@ -93,7 +94,7 @@ export default {
VARS: 'vars',
VARIANTS: 'variants',
VARIANT_DEBUG_INFO: 'variantDebugInfo',
ACTION_METADATA: 'actionMetadata',
ACTION_DEFINITIONS: 'actionDefinitions',
TOKEN: 'token',
},

Expand All @@ -103,7 +104,7 @@ export default {
VARIABLES: '__leanplum_variables',
VARIANTS: '__leanplum_variants',
VARIANT_DEBUG_INFO: '__leanplum_variant_debug_info',
ACTION_METADATA: '__leanplum_action_metadata',
ACTION_DEFINITIONS: '__leanplum_action_definitions',
INBOX_MESSAGES: '__leanplum_inbox_messages',
TOKEN: '__leanplum_token',
DEVICE_ID: '__leanplum_device_id',
Expand Down
5 changes: 5 additions & 0 deletions src/Leanplum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import LeanplumInternal from './LeanplumInternal'
import {
EventType,
Inbox,
MessageTemplateOptions,
SimpleHandler,
StatusHandler,
WebPushOptions,
Expand Down Expand Up @@ -266,6 +267,10 @@ export default class Leanplum {
Leanplum._lp.clearUserContent()
}

static defineAction(options: MessageTemplateOptions): void {
Leanplum._lp.defineAction(options)
}

static applyQueue(queue: Array<{ name: string; args: Array<any> }>): void {
Leanplum._lp.applyQueue(queue)
}
Expand Down
13 changes: 11 additions & 2 deletions src/LeanplumInternal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import {
Action,
EventType,
Inbox,
MessageKind,
MessageTemplateOptions,
SimpleHandler,
StatusHandler,
WebPushOptions,
Expand Down Expand Up @@ -205,6 +207,13 @@ export default class LeanplumInternal {
)
}

defineAction(options: MessageTemplateOptions): void {
this._varCache.registerActionDefinition({
kind: MessageKind.Template,
...options,
})
}

// TODO(breaking change): replace with events and remove stateful handlers
addStartResponseHandler(handler: StatusHandler): void {
this._internalState.addStartResponseHandler(handler)
Expand Down Expand Up @@ -241,7 +250,7 @@ export default class LeanplumInternal {
this._varCache.applyDiffs(
getVarsResponse[Constants.KEYS.VARS],
getVarsResponse[Constants.KEYS.VARIANTS],
getVarsResponse[Constants.KEYS.ACTION_METADATA])
getVarsResponse[Constants.KEYS.ACTION_DEFINITIONS])
this._varCache.setVariantDebugInfo(getVarsResponse[Constants.KEYS.VARIANT_DEBUG_INFO])

this._events.emit('messagesReceived', getVarsResponse[Constants.KEYS.MESSAGES])
Expand Down Expand Up @@ -342,7 +351,7 @@ Use "npm update leanplum-sdk" or go to https://docs.leanplum.com/reference#javas
this._varCache.applyDiffs(
startResponse[Constants.KEYS.VARS],
startResponse[Constants.KEYS.VARIANTS],
startResponse[Constants.KEYS.ACTION_METADATA])
startResponse[Constants.KEYS.ACTION_DEFINITIONS])
this._varCache.setVariantDebugInfo(startResponse[Constants.KEYS.VARIANT_DEBUG_INFO])
this._varCache.token = startResponse[Constants.KEYS.TOKEN]
} else {
Expand Down
4 changes: 4 additions & 0 deletions src/LeanplumRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ export default class LeanplumRequest {
return ''
}

if (/^https?:/.test(filename)) {
return filename
}

const args = new ArgsBuilder()
.attachApiKeys(this.appId, this.clientKey)
.add(Constants.PARAMS.SDK_VERSION, Constants.SDK_VERSION)
Expand Down
8 changes: 4 additions & 4 deletions src/LeanplumSocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ export default class LeanplumSocket {
const getVarsResponse = this.getLastResponse(response)
const values = getVarsResponse[Constants.KEYS.VARS]
const variants = getVarsResponse[Constants.KEYS.VARIANTS]
const actionMetadata = getVarsResponse[Constants.KEYS.ACTION_METADATA]
const actionDefinitions = getVarsResponse[Constants.KEYS.ACTION_DEFINITIONS]
if (!isEqual(values, this.cache.diffs)) {
this.cache.applyDiffs(values, variants, actionMetadata)
this.cache.applyDiffs(values, variants, actionDefinitions)
}
},
})
Expand All @@ -119,9 +119,9 @@ export default class LeanplumSocket {
'updated': true,
})
} else if (event === 'getActions') {
// Unsupported in JavaScript SDK.
const updated = this.cache.sendActions()
this.socketClient.send('getContentResponse', {
'updated': false,
updated,
})
} else if (event === 'registerDevice') {
const message = args[0] as RegisterMessage
Expand Down
52 changes: 21 additions & 31 deletions src/Messages.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
import { Action, UserAttributes } from './types/public'
import { Action, UserAttributes, ActionContext, RenderOptions } from './types/public'
import Constants from './Constants'
import ArgsBuilder from './ArgsBuilder'
import { CreateRequestFunction, Message, MessageVariables } from './types/internal'
import EventEmitter from './EventEmitter'
import Network from './Network'
import isEqual from 'lodash.isequal'
import LocalStorageManager from './LocalStorageManager'
import ValueTransforms from './ValueTransforms'

/* eslint-disable @typescript-eslint/ban-types */

type MessageId = string
type Timestamp = number
type MessageHash = { [key: string]: Message }
type ActionContext = {
// matches the ActionContext API in Android/iOS
// https://docs.leanplum.com/reference#section-android-custom-templates
track: (event?: string, value?: number, info?: string, params?: Object) => void;
runActionNamed: (actionName: string) => void;
runTrackedActionNamed: (actionName: string) => void;
}
type TriggerContext =
// TODO: 'install' trigger for first session
{ trigger: 'start' } |
{ trigger: 'resume' } |
{ trigger: 'userAttribute'; attributes: UserAttributes } |
Expand All @@ -34,11 +29,6 @@ type FilterConfig = {
objects?: Array<string | number>;
}>;
}
type RenderOptions = {
isPreview?: boolean;
context: ActionContext;
message: MessageVariables;
}
type TrackOptions = {
event?: string;
value?: number;
Expand Down Expand Up @@ -123,7 +113,6 @@ const verbToInterval = (verb: string): number => {
}

export default class Messages {
private _files: { [key: string]: string } = {}
private _messageCache: MessageHash = {}
private occurrenceTracker = new OccurrenceTracker()

Expand Down Expand Up @@ -200,10 +189,10 @@ export default class Messages {
this.handleMessage({
isPreview: true,

message: {
message: this.addDefaults({
messageId: message.messageId,
...vars,
},
}),

context,
})
Expand Down Expand Up @@ -452,35 +441,36 @@ export default class Messages {
return this._messageCache || {}
}

private colorToHex(color: number): string {
const b = color & 0xff; color >>= 8
const g = color & 0xff; color >>= 8
const r = color & 0xff; color >>= 8
const a = (color & 0xff) / 255
return `rgba(${r},${g},${b},${a})`
}

private addDefaults(vars: MessageVariables): MessageVariables {
const kinds = this.getMessages().actionDefinitions || {}
const defaults = kinds[vars.__name__]
const definitions = this.getMessages().actionDefinitions || {}
const definition = definitions[vars.__name__]
const kinds = definition?.kinds

if (!defaults) {
if (!definition) {
return vars
}

function useDefaults(obj: MessageVariables, defaultValues: MessageVariables): MessageVariables {
const useDefaults = (
obj: MessageVariables,
defaultValues: MessageVariables,
path = ''
): MessageVariables => {
for (const key of Object.keys(defaultValues)) {
const value = defaultValues[key]
if (typeof value === 'object') {
obj[key] = useDefaults(obj[key] || {}, value)
obj[key] = useDefaults(obj[key] || {}, value, `${path}${key}.`)
} else if (typeof obj[key] === 'undefined') {
obj[key] = value
}

if (kinds[`${path}${key}`] === 'FILE') {
obj[key] = this.getFileUrl(obj[key])
}
}
return obj
}

return useDefaults({ ...vars }, defaults.values)
return useDefaults({ ...vars }, definition.values)
}

private resolveFiles(vars: MessageVariables): MessageVariables {
Expand All @@ -505,7 +495,7 @@ export default class Messages {
const name = key.replace(filePrefix, '')
vars[name + ' URL'] = this.getFileUrl(vars[key])
} else if (colorSuffix.test(key)) {
vars[key] = this.colorToHex(vars[key])
vars[key] = ValueTransforms.decodeColor(vars[key])
} else if (typeof vars[key] === 'object') {
vars[key] = this.resolveFields(vars[key])
}
Expand Down
40 changes: 40 additions & 0 deletions src/ValueTransforms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
export default class ValueTransforms {
public static decodeColor(color: number): string {
const b = color & 0xff; color >>= 8
const g = color & 0xff; color >>= 8
const r = color & 0xff; color >>= 8
const a = (color & 0xff) / 255
return `rgba(${r},${g},${b},${a})`
}

public static encodeColor(color: string | number): number {
if (typeof color === 'number') {
return color
}

// rgba -> number
const rgbaRe = /^rgba\((\d+),(\d+),(\d+),(\d+(\.\d+)?)\)$/
const rgba = rgbaRe.exec(color)
if (rgba) {
const a = parseInt(rgba[4], 10) * 255
const r = parseInt(rgba[1], 10) & 0xff
const g = parseInt(rgba[2], 10) & 0xff
const b = parseInt(rgba[3], 10) & 0xff

return (a << 24) + (r << 16) + (g << 8) + b
}

// hex -> number
const hexRe = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i
const hex = hexRe.exec(color)
if (hex) {
const r = parseInt(hex[1], 16) & 0xff
const g = parseInt(hex[2], 16) & 0xff
const b = parseInt(hex[3], 16) & 0xff

return (255 << 24) + (r << 16) + (g << 8) + b
}

throw new Error(`Could not parse color "${color}"`)
}
}
Loading