customerio-gist-web 0.0.0-test-oidc
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/.github/workflows/eslint.yml +26 -0
- package/.github/workflows/release.yml +46 -0
- package/.github/workflows/release_hotfix.yml +41 -0
- package/.github/workflows/release_version.yml +57 -0
- package/.mise/config.toml +2 -0
- package/CODEOWNERS +2 -0
- package/LICENSE +21 -0
- package/README.md +29 -0
- package/dist/gist.min.js +1 -0
- package/eslint.config.mjs +10 -0
- package/examples/index.html +126 -0
- package/examples/styles.css +55 -0
- package/index.d.ts +1 -0
- package/index.js +2 -0
- package/package.json +49 -0
- package/src/gist.js +128 -0
- package/src/index.js +2 -0
- package/src/managers/custom-attribute-manager.js +82 -0
- package/src/managers/gist-properties-manager.js +36 -0
- package/src/managers/locale-manager.js +16 -0
- package/src/managers/message-broadcast-manager.js +107 -0
- package/src/managers/message-component-manager.js +192 -0
- package/src/managers/message-manager.js +314 -0
- package/src/managers/message-user-queue-manager.js +80 -0
- package/src/managers/page-component-manager.js +19 -0
- package/src/managers/queue-manager.js +216 -0
- package/src/managers/user-manager.js +88 -0
- package/src/services/log-service.js +19 -0
- package/src/services/network.js +81 -0
- package/src/services/queue-service.js +75 -0
- package/src/services/settings.js +65 -0
- package/src/templates/embed.js +69 -0
- package/src/templates/message.js +57 -0
- package/src/utilities/event-emitter.js +12 -0
- package/src/utilities/local-storage.js +86 -0
- package/src/utilities/log.js +7 -0
- package/src/utilities/preview-mode.js +20 -0
- package/test.sh +4 -0
- package/webpack.config.js +11 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
import { log } from '../utilities/log';
|
|
3
|
+
import { setKeyToLocalStore, getKeyFromLocalStore, clearKeyFromLocalStore } from '../utilities/local-storage';
|
|
4
|
+
import { userQueueNextPullCheckLocalStoreName } from '../services/queue-service';
|
|
5
|
+
|
|
6
|
+
const userTokenLocalStoreName = "gist.web.userToken";
|
|
7
|
+
const usingGuestUserTokenLocalStoreName = "gist.web.usingGuestUserToken";
|
|
8
|
+
const guestUserTokenLocalStoreName = "gist.web.guestUserToken";
|
|
9
|
+
const defaultExpiryInDays = 30;
|
|
10
|
+
|
|
11
|
+
export function isUsingGuestUserToken() {
|
|
12
|
+
return (getKeyFromLocalStore(usingGuestUserTokenLocalStoreName) !== null);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getUserToken() {
|
|
16
|
+
return getKeyFromLocalStore(userTokenLocalStoreName);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function setUserToken(userToken, expiryDate) {
|
|
20
|
+
if (expiryDate === undefined) {
|
|
21
|
+
expiryDate = new Date();
|
|
22
|
+
expiryDate.setDate(expiryDate.getDate() + defaultExpiryInDays);
|
|
23
|
+
}
|
|
24
|
+
setKeyToLocalStore(userTokenLocalStoreName, userToken, expiryDate);
|
|
25
|
+
|
|
26
|
+
if (isUsingGuestUserToken()) {
|
|
27
|
+
// Removing pull check time key so that we check the queue immediately after the userToken is set.
|
|
28
|
+
clearKeyFromLocalStore(userQueueNextPullCheckLocalStoreName);
|
|
29
|
+
clearKeyFromLocalStore(usingGuestUserTokenLocalStoreName);
|
|
30
|
+
}
|
|
31
|
+
log(`Set user token "${userToken}" with expiry date set to ${expiryDate}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function useGuestSession() {
|
|
35
|
+
// Guest sessions should never override existing sessions
|
|
36
|
+
if (getUserToken() === null) {
|
|
37
|
+
var guestUserToken = getKeyFromLocalStore(guestUserTokenLocalStoreName);
|
|
38
|
+
if (guestUserToken == null) {
|
|
39
|
+
guestUserToken = uuidv4();
|
|
40
|
+
setKeyToLocalStore(guestUserTokenLocalStoreName, guestUserToken);
|
|
41
|
+
log(`Set guest user token "${guestUserToken}" with expiry date set to 1 year from today`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
setKeyToLocalStore(userTokenLocalStoreName, guestUserToken);
|
|
45
|
+
setKeyToLocalStore(usingGuestUserTokenLocalStoreName, true);
|
|
46
|
+
log(`Using anonymous session with token: "${guestUserToken}"`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function isAnonymousUser() {
|
|
51
|
+
return isUsingGuestUserToken();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function getHashedUserToken() {
|
|
55
|
+
var userToken = getUserToken();
|
|
56
|
+
if (userToken === null) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return await hashString(userToken);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function getEncodedUserToken() {
|
|
63
|
+
var userToken = getUserToken();
|
|
64
|
+
if (userToken === null) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return btoa(userToken);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function clearUserToken() {
|
|
71
|
+
clearKeyFromLocalStore(userTokenLocalStoreName);
|
|
72
|
+
log(`Cleared user token`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function hashString(message) {
|
|
76
|
+
// Encode the message as a Uint8Array
|
|
77
|
+
const encoder = new TextEncoder();
|
|
78
|
+
const data = encoder.encode(message);
|
|
79
|
+
|
|
80
|
+
// Hash the message using the SHA-256 algorithm
|
|
81
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
82
|
+
|
|
83
|
+
// Convert the hash to a hexadecimal string
|
|
84
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
85
|
+
const hashHex = hashArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
|
|
86
|
+
|
|
87
|
+
return hashHex;
|
|
88
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { UserNetworkInstance } from './network';
|
|
2
|
+
|
|
3
|
+
export async function logUserMessageView(queueId) {
|
|
4
|
+
try {
|
|
5
|
+
var response = await UserNetworkInstance().post(`/api/v1/logs/queue/${queueId}`);
|
|
6
|
+
return response;
|
|
7
|
+
} catch (error) {
|
|
8
|
+
return error.response;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function logMessageView(messageId) {
|
|
13
|
+
try {
|
|
14
|
+
var response = await UserNetworkInstance().post(`/api/v1/logs/message/${messageId}`);
|
|
15
|
+
return response;
|
|
16
|
+
} catch (error) {
|
|
17
|
+
return error.response;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import Gist from '../gist';
|
|
2
|
+
import { settings } from './settings';
|
|
3
|
+
import { getEncodedUserToken } from "../managers/user-manager";
|
|
4
|
+
|
|
5
|
+
export function UserNetworkInstance() {
|
|
6
|
+
const baseURL = settings.GIST_QUEUE_API_ENDPOINT[Gist.config.env]
|
|
7
|
+
const defaultHeaders = {
|
|
8
|
+
'X-CIO-Site-Id': Gist.config.siteId,
|
|
9
|
+
'X-CIO-Client-Platform': 'web',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const userToken = getEncodedUserToken();
|
|
13
|
+
if (userToken != null) {
|
|
14
|
+
defaultHeaders['X-Gist-Encoded-User-Token'] = userToken;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function request(path, options = {}) {
|
|
18
|
+
const url = baseURL + path;
|
|
19
|
+
const controller = new AbortController();
|
|
20
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const response = await fetch(url, {
|
|
24
|
+
method: options.method || 'GET',
|
|
25
|
+
headers: {
|
|
26
|
+
...defaultHeaders,
|
|
27
|
+
...(options.headers || {}),
|
|
28
|
+
},
|
|
29
|
+
body: options.method && options.method.toUpperCase() !== 'GET' ? options.body : undefined,
|
|
30
|
+
signal: controller.signal,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
clearTimeout(timeout);
|
|
34
|
+
|
|
35
|
+
const isJSON = response.headers.get("content-type")?.includes("application/json");
|
|
36
|
+
const data = isJSON ? await response.json() : await response.text();
|
|
37
|
+
|
|
38
|
+
const headersObj = Object.fromEntries(response.headers.entries());
|
|
39
|
+
|
|
40
|
+
if (response.status < 200 || response.status >= 400) {
|
|
41
|
+
throw {
|
|
42
|
+
response: {
|
|
43
|
+
status: response.status,
|
|
44
|
+
data,
|
|
45
|
+
headers: headersObj,
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
status: response.status,
|
|
52
|
+
headers: headersObj,
|
|
53
|
+
data,
|
|
54
|
+
};
|
|
55
|
+
} catch (err) {
|
|
56
|
+
clearTimeout(timeout);
|
|
57
|
+
|
|
58
|
+
if (!err.response) {
|
|
59
|
+
err.response = {
|
|
60
|
+
status: 0,
|
|
61
|
+
data: err.message || 'Unknown error',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
request.post = (path, data = {}, config = {}) => {
|
|
70
|
+
return request(path, {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
body: JSON.stringify(data),
|
|
73
|
+
headers: {
|
|
74
|
+
'Content-Type': 'application/json',
|
|
75
|
+
...(config.headers || {})
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return request;
|
|
81
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import Gist from '../gist';
|
|
2
|
+
import { UserNetworkInstance } from './network';
|
|
3
|
+
import { getKeyFromLocalStore, setKeyToLocalStore } from '../utilities/local-storage';
|
|
4
|
+
import { log } from "../utilities/log";
|
|
5
|
+
import { isUsingGuestUserToken, getEncodedUserToken } from '../managers/user-manager';
|
|
6
|
+
import { getUserLocale } from '../managers/locale-manager';
|
|
7
|
+
import { settings } from './settings';
|
|
8
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
9
|
+
|
|
10
|
+
const defaultPollingDelayInSeconds = 600;
|
|
11
|
+
var currentPollingDelayInSeconds = defaultPollingDelayInSeconds;
|
|
12
|
+
var checkInProgress = false;
|
|
13
|
+
|
|
14
|
+
export const userQueueNextPullCheckLocalStoreName = "gist.web.userQueueNextPullCheck";
|
|
15
|
+
export const sessionIdLocalStoreName = "gist.web.sessionId";
|
|
16
|
+
export async function getUserQueue() {
|
|
17
|
+
var response;
|
|
18
|
+
try {
|
|
19
|
+
if (!checkInProgress) {
|
|
20
|
+
checkInProgress = true;
|
|
21
|
+
var headers = {
|
|
22
|
+
"X-Gist-User-Anonymous": isUsingGuestUserToken(),
|
|
23
|
+
"Content-Language": getUserLocale()
|
|
24
|
+
}
|
|
25
|
+
response = await UserNetworkInstance().post(`/api/v3/users?sessionId=${getSessionId()}`, {}, { headers: headers });
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error.response) {
|
|
29
|
+
response = error.response;
|
|
30
|
+
} else {
|
|
31
|
+
log(`Error getting user queue: ${error}`);
|
|
32
|
+
}
|
|
33
|
+
} finally {
|
|
34
|
+
checkInProgress = false;
|
|
35
|
+
scheduleNextQueuePull(response);
|
|
36
|
+
setQueueUseSSE(response);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return response;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function setQueueUseSSE(response) {
|
|
43
|
+
const useSSE = response?.headers?.["x-cio-use-sse"]?.toLowerCase() === "true";
|
|
44
|
+
settings.setUseSSEFlag(useSSE);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getSessionId() {
|
|
48
|
+
var sessionId = getKeyFromLocalStore(sessionIdLocalStoreName);
|
|
49
|
+
if (!sessionId) {
|
|
50
|
+
sessionId = uuidv4();
|
|
51
|
+
}
|
|
52
|
+
// The session ID TTL is renewed with every poll request and extended by 30 minutes.
|
|
53
|
+
setKeyToLocalStore(sessionIdLocalStoreName, sessionId, new Date(new Date().getTime() + 1800000));
|
|
54
|
+
return sessionId;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function scheduleNextQueuePull(response) {
|
|
58
|
+
if (response && response.headers) {
|
|
59
|
+
var pollingInterval = response.headers['x-gist-queue-polling-interval'];
|
|
60
|
+
if (pollingInterval && pollingInterval > 0) {
|
|
61
|
+
currentPollingDelayInSeconds = pollingInterval;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
var expiryDate = new Date(new Date().getTime() + currentPollingDelayInSeconds * 1000);
|
|
65
|
+
setKeyToLocalStore(userQueueNextPullCheckLocalStoreName, currentPollingDelayInSeconds, expiryDate);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function getQueueSSEEndpoint() {
|
|
69
|
+
var encodedUserToken = getEncodedUserToken();
|
|
70
|
+
if (encodedUserToken === null) {
|
|
71
|
+
log("No user token available for SSE endpoint.");
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return settings.GIST_QUEUE_REALTIME_API_ENDPOINT[Gist.config.env] + `/api/v3/sse?userToken=${encodedUserToken}&siteId=${Gist.config.siteId}&sessionId=${getSessionId()}`;
|
|
75
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { getKeyFromLocalStore, setKeyToLocalStore, clearKeyFromLocalStore } from '../utilities/local-storage';
|
|
2
|
+
import { log } from '../utilities/log';
|
|
3
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
4
|
+
const userQueueUseSSELocalStoreName = "gist.web.userQueueUseSSE";
|
|
5
|
+
const userQueueActiveSSEConnectionLocalStoreName = "gist.web.activeSSEConnection";
|
|
6
|
+
const heartbeatSlop = 5;
|
|
7
|
+
let sseHeartbeat = 30;
|
|
8
|
+
let sdkId;
|
|
9
|
+
|
|
10
|
+
export const settings = {
|
|
11
|
+
RENDERER_HOST: "https://code.gist.build",
|
|
12
|
+
ENGINE_API_ENDPOINT: {
|
|
13
|
+
"prod": "https://engine.api.gist.build",
|
|
14
|
+
"dev": "https://engine.api.dev.gist.build",
|
|
15
|
+
"local": "http://engine.api.local.gist.build:82"
|
|
16
|
+
},
|
|
17
|
+
GIST_QUEUE_API_ENDPOINT: {
|
|
18
|
+
"prod": "https://consumer.cloud.gist.build",
|
|
19
|
+
"dev": "https://consumer.cloud.dev.gist.build",
|
|
20
|
+
"local": "http://api.local.gist.build:86"
|
|
21
|
+
},
|
|
22
|
+
GIST_QUEUE_REALTIME_API_ENDPOINT: {
|
|
23
|
+
"prod": "https://realtime.cloud.gist.build",
|
|
24
|
+
"dev": "https://realtime.cloud.dev.gist.build",
|
|
25
|
+
"local": "http://api.local.gist.build:3000"
|
|
26
|
+
},
|
|
27
|
+
GIST_VIEW_ENDPOINT: {
|
|
28
|
+
"prod": "https://renderer.gist.build/3.0",
|
|
29
|
+
"dev": "https://renderer.gist.build/3.0",
|
|
30
|
+
"local": "http://app.local.gist.build:8080/web"
|
|
31
|
+
},
|
|
32
|
+
getSdkId: function() {
|
|
33
|
+
if (!sdkId) {
|
|
34
|
+
sdkId = uuidv4();
|
|
35
|
+
}
|
|
36
|
+
return sdkId;
|
|
37
|
+
},
|
|
38
|
+
useSSE: function() {
|
|
39
|
+
return getKeyFromLocalStore(userQueueUseSSELocalStoreName) ?? false;
|
|
40
|
+
},
|
|
41
|
+
setUseSSEFlag: function(useSSE) {
|
|
42
|
+
setKeyToLocalStore(userQueueUseSSELocalStoreName, useSSE, new Date(new Date().getTime() + 60000));
|
|
43
|
+
log(`Set user uses SSE to "${useSSE}"`);
|
|
44
|
+
},
|
|
45
|
+
removeActiveSSEConnection: function() {
|
|
46
|
+
clearKeyFromLocalStore(userQueueActiveSSEConnectionLocalStoreName);
|
|
47
|
+
},
|
|
48
|
+
setActiveSSEConnection: function() {
|
|
49
|
+
setKeyToLocalStore(userQueueActiveSSEConnectionLocalStoreName, settings.getSdkId(), new Date(new Date().getTime() + settings.getSSEHeartbeat()));
|
|
50
|
+
},
|
|
51
|
+
hasActiveSSEConnection: function() {
|
|
52
|
+
return getKeyFromLocalStore(userQueueActiveSSEConnectionLocalStoreName) ?? false;
|
|
53
|
+
},
|
|
54
|
+
isSSEConnectionManagedBySDK: function() {
|
|
55
|
+
return getKeyFromLocalStore(userQueueActiveSSEConnectionLocalStoreName) === settings.getSdkId();
|
|
56
|
+
},
|
|
57
|
+
getSSEHeartbeat: function() {
|
|
58
|
+
return (sseHeartbeat + heartbeatSlop) * 1000;
|
|
59
|
+
},
|
|
60
|
+
setSSEHeartbeat: function(heartbeat) {
|
|
61
|
+
if (heartbeat && heartbeat > 0) {
|
|
62
|
+
sseHeartbeat = heartbeat;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export function embedHTMLTemplate(elementId, messageProperties, url) {
|
|
2
|
+
var maxWidthBreakpoint = 800;
|
|
3
|
+
if (messageProperties.messageWidth > maxWidthBreakpoint) {
|
|
4
|
+
maxWidthBreakpoint = messageProperties.messageWidth;
|
|
5
|
+
}
|
|
6
|
+
var template = `
|
|
7
|
+
<div id="gist-embed">
|
|
8
|
+
<style>
|
|
9
|
+
#x-gist-floating-top, #x-gist-floating-top-left, #x-gist-floating-top-right {
|
|
10
|
+
position: fixed;
|
|
11
|
+
top: 0px;
|
|
12
|
+
z-index: 1000000;
|
|
13
|
+
}
|
|
14
|
+
#x-gist-floating-bottom, #x-gist-floating-bottom-left, #x-gist-floating-bottom-right {
|
|
15
|
+
position: fixed;
|
|
16
|
+
bottom: 0px;
|
|
17
|
+
z-index: 1000000;
|
|
18
|
+
}
|
|
19
|
+
#x-gist-bottom, #x-gist-top, #x-gist-floating-top, #x-gist-floating-bottom {
|
|
20
|
+
left: 50%;
|
|
21
|
+
transform: translate(-50%, 0%);
|
|
22
|
+
}
|
|
23
|
+
#x-gist-floating-top-right, #x-gist-floating-bottom-right {
|
|
24
|
+
right: 0px;
|
|
25
|
+
}
|
|
26
|
+
#gist-embed {
|
|
27
|
+
position: relative;
|
|
28
|
+
height: 100%;
|
|
29
|
+
width: 100%;
|
|
30
|
+
}
|
|
31
|
+
#gist-embed-container {
|
|
32
|
+
position: relative;
|
|
33
|
+
height: 100%;
|
|
34
|
+
width: 100%;
|
|
35
|
+
}
|
|
36
|
+
#gist-embed-container .gist-frame {
|
|
37
|
+
height: 100%;
|
|
38
|
+
width: 100%;
|
|
39
|
+
border: none;
|
|
40
|
+
}
|
|
41
|
+
#x-gist-top.${elementId},
|
|
42
|
+
#x-gist-bottom.${elementId},
|
|
43
|
+
#x-gist-floating-top.${elementId},
|
|
44
|
+
#x-gist-floating-bottom.${elementId},
|
|
45
|
+
#x-gist-floating-top-left.${elementId},
|
|
46
|
+
#x-gist-floating-top-right.${elementId},
|
|
47
|
+
#x-gist-floating-bottom-left.${elementId},
|
|
48
|
+
#x-gist-floating-bottom-right.${elementId} {
|
|
49
|
+
transition: height 0.1s ease-in-out;
|
|
50
|
+
}
|
|
51
|
+
@media (max-width: ${maxWidthBreakpoint}px) {
|
|
52
|
+
#x-gist-top.${elementId},
|
|
53
|
+
#x-gist-bottom.${elementId},
|
|
54
|
+
#x-gist-floating-top.${elementId},
|
|
55
|
+
#x-gist-floating-bottom.${elementId},
|
|
56
|
+
#x-gist-floating-top-left.${elementId},
|
|
57
|
+
#x-gist-floating-top-right.${elementId},
|
|
58
|
+
#x-gist-floating-bottom-left.${elementId},
|
|
59
|
+
#x-gist-floating-bottom-right.${elementId} {
|
|
60
|
+
width: 100% !important;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
</style>
|
|
64
|
+
<div id="gist-embed-container">
|
|
65
|
+
<iframe id="${elementId}" class="gist-frame" src="${url}"></iframe>
|
|
66
|
+
</div>
|
|
67
|
+
</div>`;
|
|
68
|
+
return template
|
|
69
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export function messageHTMLTemplate(elementId, messageProperties, url) {
|
|
2
|
+
var maxWidthBreakpoint = 600;
|
|
3
|
+
if (messageProperties.messageWidth > maxWidthBreakpoint) {
|
|
4
|
+
maxWidthBreakpoint = messageProperties.messageWidth;
|
|
5
|
+
}
|
|
6
|
+
var template = `
|
|
7
|
+
<div id="gist-embed-message">
|
|
8
|
+
<style>
|
|
9
|
+
#gist-overlay.gist-background {
|
|
10
|
+
position: fixed;
|
|
11
|
+
z-index: 9999999998;
|
|
12
|
+
left: 0;
|
|
13
|
+
top: 0;
|
|
14
|
+
width: 100%;
|
|
15
|
+
height: 100%;
|
|
16
|
+
background-color: ${messageProperties.overlayColor};
|
|
17
|
+
visibility: hidden;
|
|
18
|
+
}
|
|
19
|
+
#gist-overlay.gist-background.gist-visible {
|
|
20
|
+
visibility: visible;
|
|
21
|
+
}
|
|
22
|
+
.gist-message {
|
|
23
|
+
width: ${messageProperties.messageWidth}px;
|
|
24
|
+
position: absolute;
|
|
25
|
+
border: none;
|
|
26
|
+
opacity: 0;
|
|
27
|
+
transition: opacity 0.3s ease-in-out, height 0.1s ease-in-out;
|
|
28
|
+
z-index: 9999999999;
|
|
29
|
+
left: 50%;
|
|
30
|
+
transform: translateX(-50%);
|
|
31
|
+
}
|
|
32
|
+
.gist-message.gist-visible {
|
|
33
|
+
opacity: 1;
|
|
34
|
+
pointer-events: auto;
|
|
35
|
+
}
|
|
36
|
+
.gist-message.gist-center {
|
|
37
|
+
transform: translate(-50%, -50%);
|
|
38
|
+
top: 50%;
|
|
39
|
+
}
|
|
40
|
+
.gist-message.gist-bottom {
|
|
41
|
+
bottom: 0;
|
|
42
|
+
}
|
|
43
|
+
.gist-message.gist-top {
|
|
44
|
+
top: 0;
|
|
45
|
+
}
|
|
46
|
+
@media (max-width: ${maxWidthBreakpoint}px) {
|
|
47
|
+
.gist-message {
|
|
48
|
+
width: 100%;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
</style>
|
|
52
|
+
<div id="gist-overlay" class="gist-background">
|
|
53
|
+
<iframe id="${elementId}" class="gist-message" src="${url}"></iframe>
|
|
54
|
+
</div>
|
|
55
|
+
</div>`;
|
|
56
|
+
return template;
|
|
57
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export default class EventEmitter {
|
|
2
|
+
on(name, callback) {
|
|
3
|
+
var callbacks = this[name];
|
|
4
|
+
if (!callbacks) this[name] = [callback];
|
|
5
|
+
else callbacks.push(callback);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
dispatch(name, event) {
|
|
9
|
+
var callbacks = this[name];
|
|
10
|
+
if (callbacks) callbacks.forEach(callback => callback(event));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { log } from "./log";
|
|
2
|
+
|
|
3
|
+
const maxExpiryDays = 365;
|
|
4
|
+
|
|
5
|
+
const isPersistingSessionLocalStoreName = "gist.web.isPersistingSession";
|
|
6
|
+
|
|
7
|
+
// Switches between local and session storage
|
|
8
|
+
export function shouldPersistSession(presisted) {
|
|
9
|
+
sessionStorage.setItem(isPersistingSessionLocalStoreName, presisted);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function setKeyToLocalStore(key, value, ttl = null) {
|
|
13
|
+
var expiryDate = ttl;
|
|
14
|
+
if (!expiryDate) {
|
|
15
|
+
expiryDate = new Date();
|
|
16
|
+
expiryDate.setDate(expiryDate.getDate() + maxExpiryDays);
|
|
17
|
+
}
|
|
18
|
+
const item = {
|
|
19
|
+
value: value,
|
|
20
|
+
expiry: expiryDate,
|
|
21
|
+
};
|
|
22
|
+
getStorage().setItem(key, JSON.stringify(item));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getKeyFromLocalStore(key) {
|
|
26
|
+
return checkKeyForExpiry(key);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function clearKeyFromLocalStore(key) {
|
|
30
|
+
getStorage().removeItem(key);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function clearExpiredFromLocalStore() {
|
|
34
|
+
const storage = getStorage();
|
|
35
|
+
for (let i = storage.length - 1; i >= 0; i--) {
|
|
36
|
+
checkKeyForExpiry(storage.key(i));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function isSessionBeingPersisted() {
|
|
41
|
+
const currentValue = sessionStorage.getItem(isPersistingSessionLocalStoreName);
|
|
42
|
+
if (currentValue === null) {
|
|
43
|
+
sessionStorage.setItem(isPersistingSessionLocalStoreName, "true");
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
return currentValue === "true";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Helper function to select the correct storage based on the session flag
|
|
50
|
+
function getStorage() {
|
|
51
|
+
return isSessionBeingPersisted() ? localStorage : sessionStorage;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function checkKeyForExpiry(key) {
|
|
55
|
+
if (!key) return null;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const itemStr = getStorage().getItem(key);
|
|
59
|
+
if (!itemStr) return null;
|
|
60
|
+
|
|
61
|
+
const item = JSON.parse(itemStr);
|
|
62
|
+
if (!item.expiry) return item.value;
|
|
63
|
+
|
|
64
|
+
const now = new Date();
|
|
65
|
+
const expiryTime = new Date(item.expiry);
|
|
66
|
+
|
|
67
|
+
// remove old cache entries with long expiry times
|
|
68
|
+
const isBroadcastOrUserKey = (key.startsWith("gist.web.message.broadcasts") && !key.endsWith("shouldShow") && !key.endsWith("numberOfTimesShown")) || (key.startsWith("gist.web.message.user") && !key.endsWith("seen"));
|
|
69
|
+
const sixtyMinutesFromNow = new Date(now.getTime() + 61 * 60 * 1000);
|
|
70
|
+
if (isBroadcastOrUserKey && expiryTime.getTime() > sixtyMinutesFromNow.getTime()) {
|
|
71
|
+
clearKeyFromLocalStore(key);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (now.getTime() > expiryTime.getTime()) {
|
|
76
|
+
clearKeyFromLocalStore(key);
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return item.value;
|
|
81
|
+
} catch (e) {
|
|
82
|
+
log(`Error checking key ${key} for expiry: ${e}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import Gist from '../gist';
|
|
2
|
+
import { log } from './log';
|
|
3
|
+
import { shouldPersistSession, isSessionBeingPersisted } from './local-storage';
|
|
4
|
+
|
|
5
|
+
const previewParamId = "cioPreviewId";
|
|
6
|
+
|
|
7
|
+
export function setupPreview() {
|
|
8
|
+
const cioPreviewId = fetchPreviewId();
|
|
9
|
+
if (cioPreviewId) {
|
|
10
|
+
shouldPersistSession(false);
|
|
11
|
+
Gist.setUserToken(cioPreviewId);
|
|
12
|
+
log(`Preview mode enabled with user token: ${cioPreviewId}`);
|
|
13
|
+
}
|
|
14
|
+
return !isSessionBeingPersisted();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function fetchPreviewId() {
|
|
18
|
+
const params = new URLSearchParams(window.location.search);
|
|
19
|
+
return params.get(previewParamId);
|
|
20
|
+
}
|
package/test.sh
ADDED