@saasquatch/component-environment 1.0.0-3 → 1.0.0-4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -1
- package/CHANGELOG.md +0 -13
- package/src/contexts/LocaleContext.ts +0 -50
- package/src/contexts/ProgramContext.ts +0 -50
- package/src/contexts/UserIdentityContext.ts +0 -164
- package/src/debug.ts +0 -7
- package/src/environment.ts +0 -114
- package/src/fetchLocale.ts +0 -58
- package/src/index.ts +0 -5
- package/src/listeners.ts +0 -52
- package/src/types.ts +0 -173
- package/tsconfig.json +0 -15
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saasquatch/component-environment",
|
|
3
|
-
"version": "1.0.0-
|
|
3
|
+
"version": "1.0.0-4",
|
|
4
4
|
"description": "Environment and contexts for SaaSquatch components",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/"
|
|
9
|
+
],
|
|
7
10
|
"scripts": {
|
|
8
11
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|
|
9
12
|
"test": "echo \"Error: no test specified\" && exit 1"
|
package/CHANGELOG.md
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
-
|
|
8
|
-
## [1.0.0] - 2022-06-16
|
|
9
|
-
|
|
10
|
-
Initial version.
|
|
11
|
-
|
|
12
|
-
[unreleased]: https://github.com/saasquatch/program-tools/compare/component-environment@1.0.0...HEAD
|
|
13
|
-
[1.0.0]: https://github.com/saasquatch/program-tools/releases/tag/%40saasquatch%2Fcomponent-environment%401.0.0
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { ContextProvider } from "dom-context";
|
|
2
|
-
import { LOCALE_CONTEXT_NAME } from "../types";
|
|
3
|
-
import { debug as _debug } from "../debug";
|
|
4
|
-
|
|
5
|
-
const debug = (...args: any[]) => _debug(LOCALE_CONTEXT_NAME, ...args);
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Lazily start the locale context provider. If it already exists, the existing provider is
|
|
9
|
-
* returned. This function is safe to call multiple times.
|
|
10
|
-
*
|
|
11
|
-
* @returns The global locale context provider
|
|
12
|
-
*/
|
|
13
|
-
export function lazilyStartLocaleContext() {
|
|
14
|
-
let globalProvider = window.squatchLocale;
|
|
15
|
-
|
|
16
|
-
if (!globalProvider) {
|
|
17
|
-
debug("Creating locale context provider");
|
|
18
|
-
|
|
19
|
-
globalProvider = new ContextProvider<string | undefined>({
|
|
20
|
-
element: document.documentElement,
|
|
21
|
-
initialState:
|
|
22
|
-
window.widgetIdent?.locale || navigator.language.replace("-", "_"),
|
|
23
|
-
contextName: LOCALE_CONTEXT_NAME,
|
|
24
|
-
}).start();
|
|
25
|
-
|
|
26
|
-
window.squatchLocale = globalProvider;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return globalProvider;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Overide the globally defined Locale context
|
|
34
|
-
*
|
|
35
|
-
* @param locale the new locale used by the user
|
|
36
|
-
*/
|
|
37
|
-
export function setLocale(locale?: string) {
|
|
38
|
-
const globalProvider = lazilyStartLocaleContext();
|
|
39
|
-
if (globalProvider.context !== locale) {
|
|
40
|
-
debug(`Setting locale context value [${locale}]`);
|
|
41
|
-
globalProvider.context = locale;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Get the current value of the locale context
|
|
47
|
-
*/
|
|
48
|
-
export function getLocale() {
|
|
49
|
-
return window.squatchLocale?.context;
|
|
50
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { ContextProvider } from "dom-context";
|
|
2
|
-
import { PROGRAM_CONTEXT_NAME } from "../types";
|
|
3
|
-
import { debug as _debug } from "../debug";
|
|
4
|
-
|
|
5
|
-
const debug = (...args: any[]) => _debug(PROGRAM_CONTEXT_NAME, ...args);
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Lazily start the program context provider. If it already exists, the existing provider is
|
|
9
|
-
* returned. This function is safe to call multiple times.
|
|
10
|
-
*
|
|
11
|
-
* @returns The global program context provider
|
|
12
|
-
*/
|
|
13
|
-
export function lazilyStartProgramContext() {
|
|
14
|
-
let globalProvider = window.squatchProgramId;
|
|
15
|
-
|
|
16
|
-
if (!globalProvider) {
|
|
17
|
-
debug("Creating program context provider");
|
|
18
|
-
|
|
19
|
-
// Lazily creates a global provider
|
|
20
|
-
globalProvider = new ContextProvider<string | undefined>({
|
|
21
|
-
element: document.documentElement,
|
|
22
|
-
initialState: window.widgetIdent?.programId || undefined,
|
|
23
|
-
contextName: PROGRAM_CONTEXT_NAME,
|
|
24
|
-
}).start();
|
|
25
|
-
|
|
26
|
-
window.squatchProgramId = globalProvider;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return globalProvider;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Overide the globally defined Program ID context
|
|
34
|
-
*
|
|
35
|
-
* @param programId the new programID used by the user, or undefined if logged out
|
|
36
|
-
*/
|
|
37
|
-
export function setProgramId(programId: string | undefined) {
|
|
38
|
-
const globalProvider = lazilyStartProgramContext();
|
|
39
|
-
if (globalProvider.context !== programId) {
|
|
40
|
-
debug(`Setting program context value [${programId}]`);
|
|
41
|
-
globalProvider.context = programId;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Get the current value of the program context
|
|
47
|
-
*/
|
|
48
|
-
export function getProgramId() {
|
|
49
|
-
return window.squatchLocale?.context;
|
|
50
|
-
}
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import decode from "jwt-decode";
|
|
2
|
-
import { ContextProvider } from "dom-context";
|
|
3
|
-
import { equal } from "@wry/equality";
|
|
4
|
-
import { getEnvironmentSDK } from "../environment";
|
|
5
|
-
import {
|
|
6
|
-
USER_CONTEXT_NAME,
|
|
7
|
-
UserIdentity,
|
|
8
|
-
DecodedSquatchJWT,
|
|
9
|
-
DecodedWidgetAPIJWT,
|
|
10
|
-
} from "../types";
|
|
11
|
-
import { startUserContextListenerForLocale } from "../listeners";
|
|
12
|
-
import { debug as _debug } from "../debug";
|
|
13
|
-
|
|
14
|
-
const debug = (...args: any[]) => _debug(USER_CONTEXT_NAME, ...args);
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Lazily start the user context provider. If it already exists, the existing provider is
|
|
18
|
-
* returned. This function is safe to call multiple times.
|
|
19
|
-
*
|
|
20
|
-
* @returns The global user context provider
|
|
21
|
-
*/
|
|
22
|
-
export function lazilyStartUserContext() {
|
|
23
|
-
let globalProvider = window.squatchUserIdentity;
|
|
24
|
-
|
|
25
|
-
if (!globalProvider) {
|
|
26
|
-
debug("Creating user context provider");
|
|
27
|
-
|
|
28
|
-
// Lazily creates a global provider
|
|
29
|
-
globalProvider = new ContextProvider<UserIdentity | undefined>({
|
|
30
|
-
element: document.documentElement,
|
|
31
|
-
initialState: _getInitialValue(),
|
|
32
|
-
contextName: USER_CONTEXT_NAME,
|
|
33
|
-
}).start();
|
|
34
|
-
|
|
35
|
-
window.squatchUserIdentity = globalProvider;
|
|
36
|
-
startUserContextListenerForLocale();
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return globalProvider;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function isDecodedSquatchJWT(decoded: any): decoded is DecodedSquatchJWT {
|
|
43
|
-
return decoded.user && decoded.user.id && decoded.user.accountId;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function isDecodedWidgetAPIJWT(decoded: any): decoded is DecodedWidgetAPIJWT {
|
|
47
|
-
return decoded.sub && /.*:.*@.*:users/.test(decoded.sub);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Extract a user identity from a JWT
|
|
52
|
-
*
|
|
53
|
-
* @param jwt The JWT to extract a user identity from
|
|
54
|
-
* @returns The user identity or undefined if the JWT is not valid
|
|
55
|
-
*/
|
|
56
|
-
export function userIdentityFromJwt(jwt?: string): UserIdentity | undefined {
|
|
57
|
-
if (!jwt) return undefined;
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
const decoded = decode<DecodedSquatchJWT | DecodedWidgetAPIJWT>(jwt);
|
|
61
|
-
const exp = decoded.exp;
|
|
62
|
-
|
|
63
|
-
let userId: string | undefined = undefined;
|
|
64
|
-
let accountId: string | undefined = undefined;
|
|
65
|
-
|
|
66
|
-
if (isDecodedWidgetAPIJWT(decoded)) {
|
|
67
|
-
// Pull the accountId and userId from the subject and Base64-decode them
|
|
68
|
-
// NOTE: This is to support classic theme engine widget token generation
|
|
69
|
-
const matches = decoded.sub.match(/(.*):(.*)@(.*):users/);
|
|
70
|
-
if (matches?.[1]) accountId = atob(matches[1]);
|
|
71
|
-
if (matches?.[2]) userId = atob(matches[2]);
|
|
72
|
-
} else if (isDecodedSquatchJWT(decoded)) {
|
|
73
|
-
accountId = decoded.user.accountId;
|
|
74
|
-
userId = decoded.user.id;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (!userId || !accountId) {
|
|
78
|
-
return undefined;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Check if the JWT has expired
|
|
82
|
-
if (exp && Date.now() >= exp * 1000) {
|
|
83
|
-
return undefined;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return {
|
|
87
|
-
id: userId,
|
|
88
|
-
accountId: accountId,
|
|
89
|
-
jwt,
|
|
90
|
-
};
|
|
91
|
-
} catch (e) {
|
|
92
|
-
// Invalid JWT
|
|
93
|
-
return undefined;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function _getInitialValue(): UserIdentity | undefined {
|
|
98
|
-
const sdk = getEnvironmentSDK();
|
|
99
|
-
switch (sdk.type) {
|
|
100
|
-
case "SquatchAndroid":
|
|
101
|
-
case "SquatchJS2":
|
|
102
|
-
return {
|
|
103
|
-
id: sdk.widgetIdent.userId,
|
|
104
|
-
accountId: sdk.widgetIdent.accountId,
|
|
105
|
-
jwt: sdk.widgetIdent.token,
|
|
106
|
-
};
|
|
107
|
-
case "SquatchPortal":
|
|
108
|
-
// Portals can have the jwt provided as a URL parameter, so look for that first
|
|
109
|
-
const searchParams = new URLSearchParams(document.location.search);
|
|
110
|
-
if (searchParams.has("jwt")) {
|
|
111
|
-
return userIdentityFromJwt(searchParams.get("jwt")!);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Look for user identity in local storage
|
|
115
|
-
const stored = localStorage.getItem(USER_CONTEXT_NAME);
|
|
116
|
-
if (!stored) return undefined;
|
|
117
|
-
try {
|
|
118
|
-
const potentialUserIdent = JSON.parse(stored) as UserIdentity;
|
|
119
|
-
const identity = userIdentityFromJwt(potentialUserIdent.jwt);
|
|
120
|
-
if (identity) {
|
|
121
|
-
return {
|
|
122
|
-
...potentialUserIdent, // for any stored managedIdentity fields
|
|
123
|
-
...identity,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
return undefined;
|
|
127
|
-
} catch (e) {
|
|
128
|
-
// Not valid JSON
|
|
129
|
-
return undefined;
|
|
130
|
-
}
|
|
131
|
-
case "SquatchAdmin":
|
|
132
|
-
case "None":
|
|
133
|
-
// Not logged in for admin portal / none default case
|
|
134
|
-
return undefined;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Overide the globally defined user context, and persists the user identity in local storage
|
|
140
|
-
*
|
|
141
|
-
* @param identity the new identity of the user, or undefined if logged out
|
|
142
|
-
*/
|
|
143
|
-
export function setUserIdentity(identity?: UserIdentity) {
|
|
144
|
-
const globalProvider = lazilyStartUserContext();
|
|
145
|
-
|
|
146
|
-
if (!equal(globalProvider.context, identity)) {
|
|
147
|
-
debug(`Setting user context value [${JSON.stringify(identity)}]`);
|
|
148
|
-
globalProvider.context = identity;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Portals store identity in local storage
|
|
152
|
-
if (identity && getEnvironmentSDK().type === "SquatchPortal") {
|
|
153
|
-
localStorage.setItem(USER_CONTEXT_NAME, JSON.stringify(identity));
|
|
154
|
-
} else if (!identity) {
|
|
155
|
-
localStorage.removeItem(USER_CONTEXT_NAME);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Get the current value of the user context
|
|
161
|
-
*/
|
|
162
|
-
export function getUserIdentity() {
|
|
163
|
-
return window.squatchUserIdentity?.context;
|
|
164
|
-
}
|
package/src/debug.ts
DELETED
package/src/environment.ts
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Environment,
|
|
3
|
-
EnvironmentSDK,
|
|
4
|
-
EngagementMedium,
|
|
5
|
-
DEFAULT_MEDIUM,
|
|
6
|
-
} from "./types";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Get the type of environment that this widget is being rendered in
|
|
10
|
-
*
|
|
11
|
-
* Should never return null.
|
|
12
|
-
*/
|
|
13
|
-
export function getEnvironment(): Environment {
|
|
14
|
-
return getEnvironmentSDK().type;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Get the SDK for interacting with the host environment
|
|
19
|
-
*/
|
|
20
|
-
export function getEnvironmentSDK(): EnvironmentSDK {
|
|
21
|
-
//@ts-ignore
|
|
22
|
-
if (window["SquatchAndroid"]) {
|
|
23
|
-
return {
|
|
24
|
-
type: "SquatchAndroid",
|
|
25
|
-
//@ts-ignore
|
|
26
|
-
android: window["SquatchAndroid"] as SquatchAndroid,
|
|
27
|
-
//@ts-ignore
|
|
28
|
-
widgetIdent: window["widgetIdent"],
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
//@ts-ignore
|
|
33
|
-
if (window["SquatchPortal"]) {
|
|
34
|
-
return {
|
|
35
|
-
type: "SquatchPortal",
|
|
36
|
-
//@ts-ignore
|
|
37
|
-
env: window["SquatchPortal"],
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
//@ts-ignore
|
|
42
|
-
if (window["SquatchAdmin"]) {
|
|
43
|
-
return {
|
|
44
|
-
type: "SquatchAdmin",
|
|
45
|
-
//@ts-ignore
|
|
46
|
-
adminSDK: window["SquatchAdmin"] as SquatchAdmin,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Vanilla components mutates `widgetIdent` for portal, causing boilerplate to render as SquatchJS2
|
|
51
|
-
if (window["widgetIdent"] && window["widgetIdent"]?.env !== "demo") {
|
|
52
|
-
return {
|
|
53
|
-
type: "SquatchJS2",
|
|
54
|
-
//@ts-ignore
|
|
55
|
-
api: window.frameElement?.["squatchJsApi"],
|
|
56
|
-
//@ts-ignore
|
|
57
|
-
widgetIdent: window["widgetIdent"],
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
type: "None",
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function isDemo() {
|
|
67
|
-
const sdk = getEnvironmentSDK();
|
|
68
|
-
return sdk.type === "None" || sdk.type === "SquatchAdmin";
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Fake tenant alias in demo mode
|
|
72
|
-
const FAKE_TENANT = "demo";
|
|
73
|
-
|
|
74
|
-
export function getTenantAlias(): string {
|
|
75
|
-
const sdk = getEnvironmentSDK();
|
|
76
|
-
switch (sdk.type) {
|
|
77
|
-
case "SquatchAndroid":
|
|
78
|
-
case "SquatchJS2":
|
|
79
|
-
return sdk.widgetIdent.tenantAlias;
|
|
80
|
-
case "SquatchAdmin":
|
|
81
|
-
case "None":
|
|
82
|
-
return FAKE_TENANT;
|
|
83
|
-
case "SquatchPortal":
|
|
84
|
-
return sdk.env.tenantAlias;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const DEFAULT_DOMAIN = "https://app.referralsaasquatch.com";
|
|
89
|
-
export function getAppDomain(): string {
|
|
90
|
-
const sdk = getEnvironmentSDK();
|
|
91
|
-
switch (sdk.type) {
|
|
92
|
-
case "SquatchAndroid":
|
|
93
|
-
case "SquatchJS2":
|
|
94
|
-
return sdk.widgetIdent.appDomain;
|
|
95
|
-
case "SquatchPortal":
|
|
96
|
-
return sdk.env?.appDomain || DEFAULT_DOMAIN;
|
|
97
|
-
case "SquatchAdmin":
|
|
98
|
-
case "None":
|
|
99
|
-
return DEFAULT_DOMAIN;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export function getEngagementMedium(): EngagementMedium {
|
|
104
|
-
const sdk = getEnvironmentSDK();
|
|
105
|
-
switch (sdk.type) {
|
|
106
|
-
case "SquatchJS2":
|
|
107
|
-
return sdk.widgetIdent.engagementMedium || DEFAULT_MEDIUM;
|
|
108
|
-
case "SquatchAndroid":
|
|
109
|
-
case "SquatchPortal":
|
|
110
|
-
case "SquatchAdmin":
|
|
111
|
-
case "None":
|
|
112
|
-
return DEFAULT_MEDIUM;
|
|
113
|
-
}
|
|
114
|
-
}
|
package/src/fetchLocale.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { getAppDomain, getTenantAlias } from "./environment";
|
|
2
|
-
import { getUserIdentity } from "./contexts/UserIdentityContext";
|
|
3
|
-
import { LOCALE_CONTEXT_NAME } from "./types";
|
|
4
|
-
import { debug as _debug } from "./debug";
|
|
5
|
-
|
|
6
|
-
const debug = (...args: any[]) => _debug(LOCALE_CONTEXT_NAME, ...args);
|
|
7
|
-
|
|
8
|
-
const GET_LOCALE = `
|
|
9
|
-
query {
|
|
10
|
-
viewer {
|
|
11
|
-
... on User {
|
|
12
|
-
locale
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
`;
|
|
17
|
-
|
|
18
|
-
interface GetLocaleResponse {
|
|
19
|
-
data: {
|
|
20
|
-
viewer: {
|
|
21
|
-
locale: string | null;
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export async function fetchLocale(): Promise<string | undefined> {
|
|
27
|
-
debug("Fetching locale from GraphQL for current user");
|
|
28
|
-
|
|
29
|
-
try {
|
|
30
|
-
const result = await fetch(
|
|
31
|
-
`${getAppDomain()}/api/v1/${getTenantAlias()}/graphql`,
|
|
32
|
-
{
|
|
33
|
-
method: "POST",
|
|
34
|
-
headers: {
|
|
35
|
-
"Content-Type": "application/json",
|
|
36
|
-
Authorization: `Bearer ${getUserIdentity()?.jwt}`,
|
|
37
|
-
},
|
|
38
|
-
body: JSON.stringify({
|
|
39
|
-
query: GET_LOCALE,
|
|
40
|
-
}),
|
|
41
|
-
}
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
if (!result.ok) {
|
|
45
|
-
throw new Error("Failed to fetch locale");
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const json = await result.json();
|
|
49
|
-
if (json.errors) {
|
|
50
|
-
throw new Error(JSON.stringify(json.errors, null, 2));
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return (json as GetLocaleResponse).data.viewer.locale || undefined;
|
|
54
|
-
} catch (e) {
|
|
55
|
-
debug(`Failed to fetch locale for current user`, (e as Error).message);
|
|
56
|
-
return undefined;
|
|
57
|
-
}
|
|
58
|
-
}
|
package/src/index.ts
DELETED
package/src/listeners.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { ContextListener } from "dom-context";
|
|
2
|
-
import { USER_CONTEXT_NAME, LOCALE_CONTEXT_NAME } from "./types";
|
|
3
|
-
import { UserIdentity } from "./types";
|
|
4
|
-
import { lazilyStartLocaleContext } from "./contexts/LocaleContext";
|
|
5
|
-
import { fetchLocale } from "./fetchLocale";
|
|
6
|
-
import { debug as _debug } from "./debug";
|
|
7
|
-
|
|
8
|
-
const debug = (...args: any[]) => _debug(LOCALE_CONTEXT_NAME, ...args);
|
|
9
|
-
|
|
10
|
-
const userContextListenerDiv = (() => {
|
|
11
|
-
const id = "__environment_context_listener";
|
|
12
|
-
let div = document.getElementById(id);
|
|
13
|
-
if (!div) {
|
|
14
|
-
div = document.createElement("div");
|
|
15
|
-
div.id = id;
|
|
16
|
-
document.documentElement.appendChild(div);
|
|
17
|
-
}
|
|
18
|
-
return div;
|
|
19
|
-
})();
|
|
20
|
-
|
|
21
|
-
// Listens to user changes and refetches the locale from GraphQL
|
|
22
|
-
const userContextListenerForLocale = new ContextListener<
|
|
23
|
-
UserIdentity | undefined
|
|
24
|
-
>({
|
|
25
|
-
contextName: USER_CONTEXT_NAME,
|
|
26
|
-
element: userContextListenerDiv,
|
|
27
|
-
onChange: async (next) => {
|
|
28
|
-
const localeProvider = lazilyStartLocaleContext();
|
|
29
|
-
const defaultLocale =
|
|
30
|
-
window.widgetIdent?.locale || navigator.language.replace("-", "_");
|
|
31
|
-
|
|
32
|
-
let newLocale;
|
|
33
|
-
if (next) {
|
|
34
|
-
debug("User context changed, refetching locale");
|
|
35
|
-
const locale = await fetchLocale();
|
|
36
|
-
if (localeProvider.context !== locale) {
|
|
37
|
-
debug(`New value fetched from GraphQL [${locale}]`);
|
|
38
|
-
newLocale = locale || defaultLocale;
|
|
39
|
-
}
|
|
40
|
-
} else {
|
|
41
|
-
newLocale = defaultLocale;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
debug(`Setting locale context to [${newLocale}]`);
|
|
45
|
-
localeProvider.context = newLocale;
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
export function startUserContextListenerForLocale() {
|
|
50
|
-
debug("Starting user context listener for locale updates");
|
|
51
|
-
userContextListenerForLocale.start();
|
|
52
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import type { ContextProvider } from "dom-context";
|
|
2
|
-
|
|
3
|
-
declare global {
|
|
4
|
-
interface Window {
|
|
5
|
-
SquatchPortal?: PortalEnv;
|
|
6
|
-
widgetIdent?: WidgetIdent;
|
|
7
|
-
squatchUserIdentity?: ContextProvider<UserIdentity | undefined>;
|
|
8
|
-
squatchLocale?: ContextProvider<string | undefined>;
|
|
9
|
-
squatchProgramId?: ContextProvider<string | undefined>;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export type UserContextName = "sq:user-identity";
|
|
14
|
-
export type LocaleContextName = "sq:locale";
|
|
15
|
-
export type ProgramContextName = "sq:program-id";
|
|
16
|
-
|
|
17
|
-
export const USER_CONTEXT_NAME: UserContextName = "sq:user-identity" as const;
|
|
18
|
-
export const LOCALE_CONTEXT_NAME: LocaleContextName = "sq:locale" as const;
|
|
19
|
-
export const PROGRAM_CONTEXT_NAME: ProgramContextName =
|
|
20
|
-
"sq:program-id" as const;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* The value stored in the UserContext
|
|
24
|
-
*/
|
|
25
|
-
export type UserIdentity = {
|
|
26
|
-
id: string;
|
|
27
|
-
accountId: string;
|
|
28
|
-
jwt?: string;
|
|
29
|
-
managedIdentity?: {
|
|
30
|
-
email: string;
|
|
31
|
-
emailVerified: boolean;
|
|
32
|
-
sessionData?: { [key: string]: any };
|
|
33
|
-
};
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export type UserId = {
|
|
37
|
-
id: string;
|
|
38
|
-
accountId: string;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export interface DecodedSquatchJWT {
|
|
42
|
-
exp?: number;
|
|
43
|
-
user: {
|
|
44
|
-
accountId: string;
|
|
45
|
-
id: string;
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// NOTE: Classic theme-engine JWT's do not have a typical payload,
|
|
50
|
-
// they have a sub in the form base64(accountId):base64(userId)@tenantAlias:users
|
|
51
|
-
export interface DecodedWidgetAPIJWT {
|
|
52
|
-
exp?: number;
|
|
53
|
-
sub: string;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export type EngagementMedium = "EMBED" | "POPUP";
|
|
57
|
-
export const DEFAULT_MEDIUM: EngagementMedium = "EMBED" as const;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Provided by the SaaSquatch GraphQL backend when a widget is rendered.
|
|
61
|
-
*
|
|
62
|
-
* Source: https://github.com/saasquatch/saasquatch/blob/805e51284f818f8656b6458bcee6181f378819d3/packages/saasquatch-core/app/saasquatch/controllers/api/widget/WidgetApi.java
|
|
63
|
-
*
|
|
64
|
-
*/
|
|
65
|
-
export interface WidgetIdent {
|
|
66
|
-
tenantAlias: string;
|
|
67
|
-
appDomain: string;
|
|
68
|
-
token: string;
|
|
69
|
-
userId: string;
|
|
70
|
-
accountId: string;
|
|
71
|
-
locale?: string;
|
|
72
|
-
engagementMedium?: "POPUP" | "EMBED";
|
|
73
|
-
programId?: string;
|
|
74
|
-
env?: string;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Portal env doesn't include User Id
|
|
79
|
-
*/
|
|
80
|
-
export type PortalEnv = Pick<
|
|
81
|
-
WidgetIdent,
|
|
82
|
-
"tenantAlias" | "appDomain" | "programId"
|
|
83
|
-
>;
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* An interface for interacting with the SaaSquatch Admin Portal.
|
|
87
|
-
*
|
|
88
|
-
* Used for rendering widgets in a preview/demo mode.
|
|
89
|
-
*/
|
|
90
|
-
export interface SquatchAdmin {
|
|
91
|
-
/**
|
|
92
|
-
* Provides a way of providing user feedback when a widget is rendered in the SaaSquatch admin portal
|
|
93
|
-
*
|
|
94
|
-
* @param text
|
|
95
|
-
*/
|
|
96
|
-
showMessage(text: string): void;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Type for the Javascript environment added by https://github.com/saasquatch/squatch-android
|
|
101
|
-
*
|
|
102
|
-
* Should exist as `window.SquatchAndroid`
|
|
103
|
-
*/
|
|
104
|
-
export interface SquatchAndroid {
|
|
105
|
-
/**
|
|
106
|
-
*
|
|
107
|
-
* @param shareLink
|
|
108
|
-
* @param messageLink fallback URL to redirect to if the app is not installed
|
|
109
|
-
*/
|
|
110
|
-
shareOnFacebook(shareLink: string, messageLink: string): void;
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Shows a native Android toast
|
|
114
|
-
*
|
|
115
|
-
* @param text
|
|
116
|
-
*/
|
|
117
|
-
showToast(text: string): void;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* An interface provided by Squatch.js V2 for widgets.
|
|
122
|
-
*
|
|
123
|
-
* See: https://github.com/saasquatch/squatch-js/blob/8f2b218c9d55567e0cc12d27d635a5fb545e6842/src/widgets/Widget.ts#L47
|
|
124
|
-
*
|
|
125
|
-
*/
|
|
126
|
-
export interface SquatchJS2 {
|
|
127
|
-
/**
|
|
128
|
-
* Opens the current popup widget (if loaded as a popup)
|
|
129
|
-
*/
|
|
130
|
-
open?: () => void;
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Closes the current popup widget (if loaded as a popup)
|
|
134
|
-
*/
|
|
135
|
-
close?: () => void;
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* DEPRECATED used to update user details from inside the widget.
|
|
139
|
-
*
|
|
140
|
-
* Should no longer be used. Replace with natively using the GraphQL API and re-rendering locally. Will be removed in a future version of Squatch.js
|
|
141
|
-
*
|
|
142
|
-
* @deprecated
|
|
143
|
-
*/
|
|
144
|
-
reload(
|
|
145
|
-
userDetails: { email: string; firstName: string; lastName: string },
|
|
146
|
-
jwt: string
|
|
147
|
-
): void;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export type Environment = EnvironmentSDK["type"];
|
|
151
|
-
|
|
152
|
-
export type EnvironmentSDK =
|
|
153
|
-
| {
|
|
154
|
-
type: "SquatchJS2";
|
|
155
|
-
api: SquatchJS2;
|
|
156
|
-
widgetIdent: WidgetIdent;
|
|
157
|
-
}
|
|
158
|
-
| {
|
|
159
|
-
type: "SquatchAndroid";
|
|
160
|
-
android: SquatchAndroid;
|
|
161
|
-
widgetIdent: WidgetIdent;
|
|
162
|
-
}
|
|
163
|
-
| {
|
|
164
|
-
type: "SquatchPortal";
|
|
165
|
-
env: PortalEnv;
|
|
166
|
-
}
|
|
167
|
-
| {
|
|
168
|
-
type: "SquatchAdmin";
|
|
169
|
-
adminSDK: SquatchAdmin;
|
|
170
|
-
}
|
|
171
|
-
| {
|
|
172
|
-
type: "None";
|
|
173
|
-
};
|
package/tsconfig.json
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2021",
|
|
4
|
-
"lib": ["ES2021", "DOM"],
|
|
5
|
-
"module": "commonjs",
|
|
6
|
-
"moduleResolution": "node",
|
|
7
|
-
"outDir": "./dist",
|
|
8
|
-
"esModuleInterop": true,
|
|
9
|
-
"forceConsistentCasingInFileNames": true,
|
|
10
|
-
"strict": true,
|
|
11
|
-
"skipLibCheck": true,
|
|
12
|
-
"declaration": true
|
|
13
|
-
},
|
|
14
|
-
"include": ["src/**/*"]
|
|
15
|
-
}
|