@saasquatch/component-environment 1.0.0-0
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/dist/LocaleContext.d.ts +7 -0
- package/dist/LocaleContext.js +43 -0
- package/dist/ProgramContext.d.ts +13 -0
- package/dist/ProgramContext.js +29 -0
- package/dist/UserIdentityContext.d.ts +10 -0
- package/dist/UserIdentityContext.js +124 -0
- package/dist/contexts/LocaleContext.d.ts +18 -0
- package/dist/contexts/LocaleContext.js +41 -0
- package/dist/contexts/ProgramContext.d.ts +18 -0
- package/dist/contexts/ProgramContext.js +42 -0
- package/dist/contexts/UserIdentityContext.d.ts +26 -0
- package/dist/contexts/UserIdentityContext.js +146 -0
- package/dist/debug.d.ts +1 -0
- package/dist/debug.js +6 -0
- package/dist/environment.d.ts +15 -0
- package/dist/environment.js +98 -0
- package/dist/fetchLocale.d.ts +1 -0
- package/dist/fetchLocale.js +37 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/listeners.d.ts +1 -0
- package/dist/listeners.js +44 -0
- package/dist/types.d.ts +147 -0
- package/dist/types.js +4 -0
- package/package.json +28 -0
- package/src/contexts/LocaleContext.ts +50 -0
- package/src/contexts/ProgramContext.ts +50 -0
- package/src/contexts/UserIdentityContext.ts +164 -0
- package/src/debug.ts +7 -0
- package/src/environment.ts +114 -0
- package/src/fetchLocale.ts +52 -0
- package/src/index.ts +5 -0
- package/src/listeners.ts +51 -0
- package/src/types.ts +173 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { getAppDomain, getTenantAlias } from "./environment";
|
|
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
|
+
const GET_LOCALE = `
|
|
8
|
+
query {
|
|
9
|
+
viewer {
|
|
10
|
+
... on User {
|
|
11
|
+
locale
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
interface GetLocaleResponse {
|
|
18
|
+
viewer: {
|
|
19
|
+
locale: string | null;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function fetchLocale(): Promise<string | undefined> {
|
|
24
|
+
debug("Fetching locale from GraphQL for current user");
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const result = await fetch(
|
|
28
|
+
`${getAppDomain()}/api/v1/${getTenantAlias()}/graphql`,
|
|
29
|
+
{
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: { "Content-Type": "application/json" },
|
|
32
|
+
body: JSON.stringify({
|
|
33
|
+
query: GET_LOCALE,
|
|
34
|
+
}),
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (!result.ok) {
|
|
39
|
+
throw new Error("Failed to fetch locale");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const json = await result.json();
|
|
43
|
+
if (json.errors) {
|
|
44
|
+
throw new Error(JSON.stringify(json.errors, null, 2));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (json as GetLocaleResponse).viewer.locale || undefined;
|
|
48
|
+
} catch (e) {
|
|
49
|
+
debug(`Failed to fetch locale for current user`, (e as Error).message);
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/index.ts
ADDED
package/src/listeners.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import equal from "@wry/equality";
|
|
2
|
+
import { ContextListener, ListenerConnectionStatus } from "dom-context";
|
|
3
|
+
import { USER_CONTEXT_NAME, LOCALE_CONTEXT_NAME } from "./types";
|
|
4
|
+
import { UserIdentity } from "./types";
|
|
5
|
+
import { lazilyStartLocaleContext } from "./contexts/LocaleContext";
|
|
6
|
+
import { lazilyStartUserContext } from "./contexts/UserIdentityContext";
|
|
7
|
+
import { fetchLocale } from "./fetchLocale";
|
|
8
|
+
import { debug as _debug } from "./debug";
|
|
9
|
+
|
|
10
|
+
const debug = (...args: any[]) => _debug(LOCALE_CONTEXT_NAME, ...args);
|
|
11
|
+
|
|
12
|
+
const userContextListenerDiv = (() => {
|
|
13
|
+
const id = "__environment_context_listener";
|
|
14
|
+
let div = document.getElementById(id);
|
|
15
|
+
if (!div) {
|
|
16
|
+
div = document.createElement("div");
|
|
17
|
+
div.id = id;
|
|
18
|
+
document.documentElement.appendChild(div);
|
|
19
|
+
}
|
|
20
|
+
return div;
|
|
21
|
+
})();
|
|
22
|
+
|
|
23
|
+
// Listens to user changes and refetches the locale from GraphQL
|
|
24
|
+
const userContextListenerForLocale = new ContextListener<
|
|
25
|
+
UserIdentity | undefined
|
|
26
|
+
>({
|
|
27
|
+
contextName: USER_CONTEXT_NAME,
|
|
28
|
+
element: userContextListenerDiv,
|
|
29
|
+
onChange: async (next) => {
|
|
30
|
+
if (next) {
|
|
31
|
+
const userProvider = lazilyStartUserContext();
|
|
32
|
+
if (!equal(userProvider.context, next)) {
|
|
33
|
+
debug("User context changed, refetching locale");
|
|
34
|
+
const locale = await fetchLocale();
|
|
35
|
+
const localeProvider = lazilyStartLocaleContext();
|
|
36
|
+
if (localeProvider.context !== locale) {
|
|
37
|
+
debug(`New value fetched from GraphQL [${locale}]`);
|
|
38
|
+
localeProvider.context =
|
|
39
|
+
locale ||
|
|
40
|
+
window.widgetIdent?.locale ||
|
|
41
|
+
navigator.language.replace("-", "_");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export function startUserContextListenerForLocale() {
|
|
49
|
+
debug("Starting user context listener for locale updates");
|
|
50
|
+
userContextListenerForLocale.start();
|
|
51
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2021",
|
|
4
|
+
"lib": ["ES2021", "DOM"],
|
|
5
|
+
"module": "es2015",
|
|
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
|
+
}
|