Skip to content

Commit

Permalink
Web-to-App Banners 1.8.1 Release (#39)
Browse files Browse the repository at this point in the history
* Web-to-App banners: Cache config (#38)

* Cache DDL config to mimimise api requests

* Bump version

* Update bundles

* Make feature dependency checks stronger typed

* Update bundles

Co-authored-by: Rob Dick <r.dick@kumulos.com>

* Web-to-App banners: support device safe areas (#37)

* Add css support for notches

* Apply notch support css based on prompt pos

* Renaming to maintain kumulos scoping and class semantic

* Remove redundant classes

* Renaming

* Create and apply sass variable for banner padding

* Update dist bundle

* Apply left/right inset padding

* Bump version

* Update bundles

* Change access modifiers

Co-authored-by: Rob Dick <r.dick@kumulos.com>
Co-authored-by: cgwyllie <c.wyllie@kumulos.com>

* Update bundles

Co-authored-by: robdick <robdick101@gmail.com>
Co-authored-by: Rob Dick <r.dick@kumulos.com>
  • Loading branch information
3 people authored Jul 21, 2021
1 parent abd64f7 commit 5ea6ef3
Show file tree
Hide file tree
Showing 16 changed files with 116 additions and 52 deletions.
2 changes: 1 addition & 1 deletion dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/worker.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@kumulos/web",
"version": "1.8.0",
"version": "1.8.1",
"description": "Official SDK for integrating Kumulos services with your web projects",
"main": "dist/index.js",
"types": "types/src/index.d.ts",
Expand Down
50 changes: 37 additions & 13 deletions src/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,30 @@ import { get, set } from './storage';

import { authedFetchJson } from './utils';

const getCacheKeys = (key: string) => ({
CONFIG_CACHE_KEY: `${key}Config`,
CONFIG_CACHE_KEY_UPDATED: `${key}ConfigUpdated`
});

const MAX_CACHE_AGE_MS = 1 * 60 * 60 * 1000;

enum ConfigCacheType {
PLATFORM = 'platform',
DDL = 'ddl'
}

async function loadConfig<TConfigType>(
url: string,
cacheKey: string,
ctx: Context
): Promise<TConfigType> {
const CONFIG_CACHE_KEY = `${cacheKey}Config`;
const CONFIG_CACHE_KEY_UPDATED = `${cacheKey}ConfigUpdated`;
const MAX_AGE_MS = 1 * 60 * 60 * 1000;
const cacheKeys = getCacheKeys(cacheKey);
let config = await get<TConfigType>(cacheKeys.CONFIG_CACHE_KEY);

let config = await get<TConfigType>(CONFIG_CACHE_KEY);

const lastLoadTime = (await get<number>(CONFIG_CACHE_KEY_UPDATED)) ?? 0;
const lastLoadTime = (await get<number>(cacheKeys.CONFIG_CACHE_KEY_UPDATED)) ?? 0;
let updatedRemoteConfig = false;

if (Date.now() - lastLoadTime > MAX_AGE_MS) {
if (Date.now() - lastLoadTime > MAX_CACHE_AGE_MS) {
console.info('Config never synced/stale, syncing now...');

try {
Expand All @@ -37,8 +46,8 @@ async function loadConfig<TConfigType>(
}

if (updatedRemoteConfig) {
await set(CONFIG_CACHE_KEY, config);
await set(CONFIG_CACHE_KEY_UPDATED, Date.now());
await set(cacheKeys.CONFIG_CACHE_KEY, config);
await set(cacheKeys.CONFIG_CACHE_KEY_UPDATED, Date.now());
}

return config;
Expand All @@ -50,7 +59,7 @@ export async function loadPlatformConfig(
return (
(await loadConfig<PlatformConfig>(
`${PUSH_BASE_URL}/v1/web/config`,
'platform',
ConfigCacheType.PLATFORM,
ctx
)) ?? {}
);
Expand All @@ -62,9 +71,10 @@ export async function loadDdlConfig(
const webInstallId = await getInstallId();

try {
return await authedFetchJson<DdlPromptConfig[]>(
ctx,
`${DDL_BASE_URL}/v1/banners?webInstallId=${webInstallId}`
return await loadConfig<DdlPromptConfig[]>(
`${DDL_BASE_URL}/v1/banners?webInstallId=${webInstallId}`,
ConfigCacheType.DDL,
ctx
);
} catch (err) {
console.warn(
Expand All @@ -74,3 +84,17 @@ export async function loadDdlConfig(
// undefined return / no config
}
}

export async function deleteDdlBannerConfigFromCache(bannerUuid: string): Promise<void> {
const cacheKeys = getCacheKeys(ConfigCacheType.DDL);

let configs = await get<DdlPromptConfig[]>(cacheKeys.CONFIG_CACHE_KEY);

if (!configs) {
return;
}

configs = configs.filter(c => c.uuid !== bannerUuid);

await set(cacheKeys.CONFIG_CACHE_KEY, configs);
}
2 changes: 1 addition & 1 deletion src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { authedFetch, cyrb53, uuidv4 } from './utils';
import { del, get, set } from './storage';
import { Channel } from './channels';

const SDK_VERSION = '1.8.0';
const SDK_VERSION = '1.8.1';
const SDK_TYPE = 10;
const EVENTS_BASE_URL = 'https://events.kumulos.com';
export const PUSH_BASE_URL = 'https://push.kumulos.com';
Expand Down
49 changes: 25 additions & 24 deletions src/core/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { Context, SDKFeature, PromptConfig } from '.';
import { Context, SDKFeature } from './index';

type FeatureDependency = string | object | IDBFactory | PromiseConstructor | Notification | PushManager | ServiceWorkerContainer | SafariRemoteNotification;

const CORE_FEATURE_DEPENDENCIES : FeatureDependency[] = [typeof Promise, typeof fetch, typeof indexedDB];

const FEATURE_DEPENDENCY_CHECK : {[key in SDKFeature]: () => boolean} = {
'push': isBrowserSupportedForPush,
'ddl': isBrowserSupportedForDdl
};

// See: https://stackoverflow.com/a/2117523
export function uuidv4() {
Expand Down Expand Up @@ -36,31 +45,23 @@ export function getBrowserName(): string {
return '';
}

export function isBrowserSupported(sdkFeatures?: SDKFeature[]): boolean {
const checkPushSupported = undefined === sdkFeatures || sdkFeatures.includes(SDKFeature.PUSH);
const checkDdlSupported = sdkFeatures?.includes(SDKFeature.DDL);

const requiredThings = [typeof Promise, typeof fetch, typeof indexedDB];

if (checkPushSupported && !isBrowserSupportedForPush(requiredThings)) {
return false;
}

if (checkDdlSupported && !isBrowserSupportedForDdl(requiredThings)) {
return false;
export function isBrowserSupported(sdkFeatures?: SDKFeature[]) {
sdkFeatures = sdkFeatures ?? [];
if (!sdkFeatures.length) {
sdkFeatures.push(SDKFeature.PUSH);
}

return true;
return sdkFeatures.filter(f => FEATURE_DEPENDENCY_CHECK[f]()).length > 0;
}


function isBrowserSupportedForPush(requiredThings: any) {
function isBrowserSupportedForPush() {
const coreDependencies = [...CORE_FEATURE_DEPENDENCIES];
const browser = getBrowserName();

if ('safari' === browser) {
requiredThings.push(typeof window.safari?.pushNotification);
coreDependencies.push(typeof window.safari?.pushNotification);
} else {
requiredThings.push(
coreDependencies.push(
...[
typeof Notification,
typeof navigator.serviceWorker,
Expand All @@ -69,16 +70,16 @@ function isBrowserSupportedForPush(requiredThings: any) {
);
}

return checkRequired(requiredThings);
return checkRequired(coreDependencies);
}

function isBrowserSupportedForDdl(requiredThings: any) {
return checkRequired(requiredThings);
function isBrowserSupportedForDdl() {
return checkRequired(CORE_FEATURE_DEPENDENCIES);
}

function checkRequired(requiredThings: any) {
return requiredThings.reduce(
(supported: boolean, thing: any) => supported && thing !== 'undefined',
function checkRequired(coreDependencies: FeatureDependency[]) {
return coreDependencies.reduce(
(supported: boolean, dependency: FeatureDependency) => supported && dependency !== 'undefined',
true
);
}
Expand Down
26 changes: 22 additions & 4 deletions src/prompts/ddl/ddl-banner.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component, h, createRef, RefObject } from 'preact';
import { DdlBannerPromptConfig } from '../../core';
import { DdlBannerPromptConfig, PromptPosition } from '../../core';
import DeeplinkButton from './deeplink-button';
import { AppRating } from '../../components/app-rating';

Expand All @@ -15,6 +15,14 @@ const styles = {
}
};

const CLASSES = [
'kumulos-prompt',
'kumulos-banner-container',
'kumulos-banner-compact',
'kumulos-safe-area-inset-pad-left',
'kumulos-safe-area-inset-pad-right'
];

export interface DdlBannerProps {
config: DdlBannerPromptConfig;
onConfirm: (config: DdlBannerPromptConfig) => void;
Expand All @@ -41,14 +49,24 @@ export class DdlBanner extends Component<DdlBannerProps, never> {
this.props.dimensions(clientWidth, clientHeight);
}

onConfirm = () => {
private onConfirm = () => {
this.props.onConfirm(this.props.config);
};

onCancel = () => {
private onCancel = () => {
this.props.onCancel(this.props.config);
};

private getCssClasses(promptPosition: PromptPosition) {
const classes = [...CLASSES, `kumulos-prompt-position-${promptPosition}`];

if ([PromptPosition.TOP, PromptPosition.BOTTOM].includes(promptPosition)) {
classes.push(`kumulos-safe-area-inset-pad-${promptPosition}`);
}

return classes.join(' ');
}

render() {
const { config } = this.props;

Expand All @@ -70,7 +88,7 @@ export class DdlBanner extends Component<DdlBannerProps, never> {
ratingFg
} = colors;

const classes = `kumulos-prompt kumulos-banner-container kumulos-banner-compact kumulos-prompt-position-${position}`;
const classes = this.getCssClasses(position);

const containerStyle = {
backgroundColor: bg,
Expand Down
6 changes: 4 additions & 2 deletions src/prompts/ddl/manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { h, render } from 'preact';
import { Context, DdlPromptConfig, PromptConfig, UiActionType } from '../../core/index';
import RootFrame, { RootFrameContainer } from '../../core/root-frame';
import Ui from './ui';
import { loadDdlConfig } from '../../core/config';
import { loadDdlConfig, deleteDdlBannerConfigFromCache } from '../../core/config';
import { maybePersistReminder, isPromptSuppressed } from '../prompt-reminder';
import { deferPromptActivation } from '../utils';

Expand All @@ -25,8 +25,10 @@ export default class DdlManager {
this.setState(DdlManagerState.LOADING);
}

private onBannerConfirm = (prompt: DdlPromptConfig) => {
private onBannerConfirm = async (prompt: DdlPromptConfig) => {
this.hidePrompt(prompt);

await deleteDdlBannerConfigFromCache(prompt.uuid);
};

private onBannerCancelled = (prompt: DdlPromptConfig) => {
Expand Down
2 changes: 1 addition & 1 deletion src/prompts/styles/_banner.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
width: 100%;
min-height: 80px;

padding: 10px;
padding: $defaultBannerPadding;

font-family: $defaultFont;

Expand Down
14 changes: 14 additions & 0 deletions src/prompts/styles/_safe-area-inset-pad.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@supports(padding: unquote('max(0px)')) {
.kumulos-safe-area-inset-pad-left {
padding-left: unquote('max(' + $defaultBannerPadding + ', env(safe-area-inset-left))');
}
.kumulos-safe-area-inset-pad-right {
padding-right: unquote('max(' + $defaultBannerPadding + ', env(safe-area-inset-right))');
}
.kumulos-safe-area-inset-pad-top {
padding-top: unquote('max(' + $defaultBannerPadding + ', env(safe-area-inset-top))');
}
.kumulos-safe-area-inset-pad-bottom {
padding-bottom: unquote('max(' + $defaultBannerPadding + ', env(safe-area-inset-bottom))');
}
}
2 changes: 2 additions & 0 deletions src/prompts/styles/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ $defaultBannerHeaderLineHeightMobile: 14px;

$defaultBannerBodyFontSizeMobile: 12px;
$defaultBannerBodyLineHeightMobile: 14px;

$defaultBannerPadding: 10px;
1 change: 1 addition & 0 deletions src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
@import './prompts/styles/checkbox';
@import './prompts/styles/banner';
@import './prompts/styles/banner.compact';
@import './prompts/styles/safe-area-inset-pad';

@import './prompts/bell/bell.scss';
@import './prompts/dialog/dialog.scss';
Expand Down
1 change: 1 addition & 0 deletions types/src/core/config.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Context, PlatformConfig, DdlPromptConfig } from '.';
export declare function loadPlatformConfig(ctx: Context): Promise<PlatformConfig>;
export declare function loadDdlConfig(ctx: Context): Promise<DdlPromptConfig[] | undefined>;
export declare function deleteDdlBannerConfigFromCache(bannerUuid: string): Promise<void>;
2 changes: 1 addition & 1 deletion types/src/core/utils.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Context, SDKFeature } from '.';
import { Context, SDKFeature } from './index';
export declare function uuidv4(): string;
export declare function getBrowserName(): string;
export declare function isBrowserSupported(sdkFeatures?: SDKFeature[]): boolean;
Expand Down
5 changes: 3 additions & 2 deletions types/src/prompts/ddl/ddl-banner.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export declare class DdlBanner extends Component<DdlBannerProps, never> {
private containerRef;
constructor(props: DdlBannerProps);
componentDidMount(): void;
onConfirm: () => void;
onCancel: () => void;
private onConfirm;
private onCancel;
private getCssClasses;
render(): h.JSX.Element;
}

0 comments on commit 5ea6ef3

Please sign in to comment.