@yext/chat-headless
Version:
A state manager library powered by Redux for Yext Chat integrations
140 lines (130 loc) • 3.97 kB
text/typescript
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { ConversationState } from "../models/slices/ConversationState";
import { Message, MessageNotes } from "@yext/chat-core";
const BASE_STATE_LOCAL_STORAGE_KEY = "yext_chat_state";
export const initialState: ConversationState = {
messages: [],
isLoading: false,
canSendMessage: true,
};
export function getStateLocalStorageKey(
hostname: string,
botId: string
): string {
return `${BASE_STATE_LOCAL_STORAGE_KEY}__${hostname}__${botId}`;
}
/**
* Loads the {@link ConversationState} from local storage.
*/
export const loadSessionState = (botId: string): ConversationState => {
if (!localStorage) {
console.warn(
"Local storage is not available. State will not be persisted while navigating across pages."
);
return initialState;
}
const hostname = window?.location?.hostname;
if (!hostname) {
console.warn(
"Unable to get hostname of current page. State will not be persisted while navigating across pages."
);
return initialState;
}
const savedState = localStorage.getItem(
getStateLocalStorageKey(hostname, botId)
);
if (savedState) {
try {
const parsedState: ConversationState = JSON.parse(savedState);
if (parsedState.messages.length > 0) {
const lastTimestamp =
parsedState.messages[parsedState.messages.length - 1].timestamp;
const currentDate = new Date();
const lastDate = new Date(lastTimestamp || 0);
const diff = currentDate.getTime() - lastDate.getTime();
// If the last message was sent within the last day, we consider the session to be active
if (diff < 24 * 60 * 60 * 1000) {
return parsedState;
}
localStorage.removeItem(getStateLocalStorageKey(hostname, botId));
}
} catch (e) {
console.warn(
"Unabled to load saved state: error parsing state. Starting with a fresh state."
);
localStorage.removeItem(getStateLocalStorageKey(hostname, botId));
}
}
return initialState;
};
export const saveSessionState = (botId: string, state: ConversationState) => {
if (!localStorage) {
console.warn(
"Local storage is not available. State will not be persisted while navigating across pages."
);
return initialState;
}
const hostname = window?.location?.hostname;
if (!hostname) {
console.warn(
"Unable to get hostname of current page. State will not be persisted across page refreshes."
);
return initialState;
}
localStorage.setItem(
getStateLocalStorageKey(hostname, botId),
JSON.stringify(state)
);
};
/**
* Registers with Redux the slice of {@link State} pertaining to the loading status
* of Chat Headless.
*/
export const conversationSlice = createSlice({
name: "conversation",
initialState,
reducers: {
setConversationId: (
state: ConversationState,
action: PayloadAction<string | undefined>
) => {
state.conversationId = action.payload;
},
setMessages: (
state: ConversationState,
action: PayloadAction<Message[]>
) => {
state.messages = action.payload;
},
addMessage: (state: ConversationState, action: PayloadAction<Message>) => {
state.messages.push(action.payload);
},
setMessageNotes: (
state: ConversationState,
action: PayloadAction<MessageNotes>
) => {
state.notes = action.payload;
},
setIsLoading: (
state: ConversationState,
action: PayloadAction<boolean>
) => {
state.isLoading = action.payload;
},
setCanSendMessage: (
state: ConversationState,
action: PayloadAction<boolean>
) => {
state.canSendMessage = action.payload;
},
},
});
export const {
setMessages,
addMessage,
setMessageNotes,
setIsLoading,
setConversationId,
setCanSendMessage,
} = conversationSlice.actions;
export default conversationSlice.reducer;