Skip to content

Commit

Permalink
Persist nudge state
Browse files Browse the repository at this point in the history
  • Loading branch information
peterszerzo committed Jul 30, 2024
1 parent 7e41c24 commit e4109ef
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 41 deletions.
7 changes: 6 additions & 1 deletion packages/journey-manager/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,12 @@ export const run = async (props: RunProps): Promise<RunOutput> => {
// ----- Set up UI -----
await dom.contentLoaded();

const ui = createUi(props.ui, client, findActiveTriggers);
const ui = createUi(
props.config.conversationId,
props.ui,
client,
findActiveTriggers,
);

// --- Set up Digression detection ---
const checkForDigressions = prepareDigression(triggers, () => {
Expand Down
106 changes: 66 additions & 40 deletions packages/journey-manager/src/ui/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,7 @@
/* eslint-disable jsdoc/require-jsdoc */
import { type Client } from "@nlxai/multimodal";
import { type FunctionComponent as FC, type ComponentChildren } from "preact";
import {
useEffect,
useState,
useRef,
type Dispatch,
type StateUpdater,
} from "preact/hooks";
import { useEffect, useState, useRef } from "preact/hooks";
import {
MultimodalIcon,
SupportAgentIcon,
Expand Down Expand Up @@ -266,58 +260,90 @@ const PinBubble: FC<PinBubbleProps> = ({ isActive, content, onClick }) => (
</div>
);

const useOpenStateWithHistory = (): [
boolean,
boolean,
Dispatch<StateUpdater<boolean>>,
] => {
const [isOpen, setIsOpen] = useState<boolean>(false);
const nudgeStateLocalStorageKey = (conversationId: string): string => {
return `jb-nudge-state-${conversationId}`;
};

const hasBeenOpened = useRef<boolean>(false);
type NudgeState = "visible" | "hidden" | "dismissed";

useEffect(() => {
if (isOpen) {
hasBeenOpened.current = true;
}
}, [isOpen]);
const saveNudgeState = (
conversationId: string,
nudgeState: NudgeState,
): void => {
localStorage.setItem(nudgeStateLocalStorageKey(conversationId), nudgeState);
};

return [isOpen, isOpen || hasBeenOpened.current, setIsOpen];
const retrieveNudgeState = (conversationId: string): NudgeState => {
const val = localStorage.getItem(nudgeStateLocalStorageKey(conversationId));
if (val === "visible") {
return "visible";
}
if (val === "dismissed") {
return "dismissed";
}
return "hidden";
};

export const ControlCenter: FC<{
config: UiConfig;
conversationId: string;
client: Client;
triggeredSteps: TriggeredStep[];
digression: boolean;
highlightElements: HTMLElement[];
}> = ({ config, client, triggeredSteps, highlightElements, digression }) => {
const [isOpen, hasBeenOpened, setIsOpen] = useOpenStateWithHistory();
}> = ({
config,
client,
conversationId,
triggeredSteps,
highlightElements,
digression,
}) => {
const [isOpen, setIsOpen] = useState<boolean>();
const drawerContentRef = useRef<HTMLDivElement | null>(null);
const drawerDialogRef = useRef<HTMLDivElement | null>(null);
const [isNudgeVisible, setIsNudgeVisible] = useState<boolean>(false);
const [nudgeState, setNudgeState] = useState<NudgeState>(
retrieveNudgeState(conversationId),
);

useEffect(() => {
if (hasBeenOpened || config.nudgeContent == null) {
setIsNudgeVisible(false);
return;
if (isOpen) {
setNudgeState("dismissed");
}
}, [isOpen]);

let hideTimeout: null | NodeJS.Timeout = null;
const showTimeout = setTimeout(() => {
setIsNudgeVisible(true);
hideTimeout = setTimeout(() => {
setIsNudgeVisible(false);
}, config.nudgeHideAfterMs ?? 20000);
}, config.nudgeShowAfterMs ?? 3000);
return () => {
clearTimeout(showTimeout);
if (hideTimeout) clearTimeout(hideTimeout);
};
useEffect(() => {
saveNudgeState(conversationId, nudgeState);
}, [nudgeState, conversationId]);

useEffect(() => {
if (config.nudgeContent == null) {
return;
}
if (nudgeState === "hidden") {
const showTimeout = setTimeout(() => {
setNudgeState("visible");
}, config.nudgeShowAfterMs ?? 3000);
return () => {
clearTimeout(showTimeout);
};
} else if (nudgeState === "visible") {
const hideTimeout = setTimeout(
() => {
setNudgeState("dismissed");
},
(config.nudgeHideAfterMs ?? 20000) - (config.nudgeShowAfterMs ?? 3000),
);
return () => {
clearTimeout(hideTimeout);
};
}
}, [
config.nudgeContent,
config.nudgeShowAfterMs,
config.nudgeHideAfterMs,
hasBeenOpened,
nudgeState,
setNudgeState,
]);

const onPreviousStep = config.onPreviousStep
Expand Down Expand Up @@ -345,9 +371,9 @@ export const ControlCenter: FC<{
<>
<style>{styles(mergeWithDefault(config.theme))}</style>
<PinBubble
isActive={isNudgeVisible}
isActive={nudgeState === "visible"}
onClick={() => {
setIsNudgeVisible(false);
setNudgeState("dismissed");
}}
content={config.nudgeContent ?? ""}
/>
Expand Down
11 changes: 11 additions & 0 deletions packages/journey-manager/src/ui/custom-element.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { UiConfig, TriggeredStep } from "../configuration";
export default class JourneyManagerElement extends HTMLElement {
private _shadowRoot: ShadowRoot | null = null;
private _client: Client | null = null;
private _conversationId: string | null = null;
private _triggeredSteps: TriggeredStep[] | null = null;
private _config: UiConfig | null = null;
private _digression: boolean = false;
Expand Down Expand Up @@ -41,6 +42,14 @@ export default class JourneyManagerElement extends HTMLElement {
this.render();
}

/**
* Conversation ID
*/
set conversationId(value: string) {
this._conversationId = value;
this.render();
}

/**
* Set triggered steps
*/
Expand All @@ -65,6 +74,7 @@ export default class JourneyManagerElement extends HTMLElement {
if (
this._config == null ||
this._client == null ||
this._conversationId == null ||
this._triggeredSteps == null
) {
return;
Expand All @@ -74,6 +84,7 @@ export default class JourneyManagerElement extends HTMLElement {
config={this._config}
digression={this._digression}
client={this._client}
conversationId={this._conversationId}
triggeredSteps={this._triggeredSteps}
highlightElements={this._highlightElements}
/>,
Expand Down
2 changes: 2 additions & 0 deletions packages/journey-manager/src/ui/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface Ui {
}

const create = (
conversationId: string,
config: UiConfig | undefined,
client: Client,
findActiveTriggers: (
Expand All @@ -40,6 +41,7 @@ const create = (
uiElement.style.zIndex = "1000";
uiElement.config = config;
uiElement.client = client;
uiElement.conversationId = conversationId;
document.body.appendChild(uiElement);

const updateHighlights = config.highlights
Expand Down

0 comments on commit e4109ef

Please sign in to comment.