@cxco/ui-faq
Version:
FAQ Module using @cxco default packages
471 lines (406 loc) • 15.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.actions = actions;
var _index = require("../utils/index.js");
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
var debug = require('debug')('cxco:ui-faq.actions');
function actions(core) {
/**
* Checks current url with regex pattern to retrieve
* Category and FAQ information
* @param {string} urlQuestion
*/
function getURLParameters(urlQuestion) {
var expression = (0, _index.urlRegexBuilder)(urlQuestion);
var exp = new RegExp(expression, 'i');
var matches = window.location.hash.match(exp);
/*
regex matches:
0: full match
1: category crumbs
2: category name
3: faq id
4: faq
*/
if (matches !== null) {
var ids = matches[1].split('-').map(function (i) {
return Number(i);
});
var faqId = isNaN(Number(matches[3])) ? undefined : Number(matches[3]);
return {
selectedCategories: ids,
categoryName: matches[2],
faqId: faqId,
faq: matches[4]
};
}
return undefined;
}
/**
* Shallow compare two objects via stringification. So property order matters.
* @param {Object} obj1
* @param {Object} obj2
*/
function shallowObjectEquals(obj1, obj2) {
if (_typeof(obj1) !== _typeof(obj2)) {
return false;
}
return JSON.stringify(Object.values(obj1)) === JSON.stringify(Object.values(obj2));
}
return {
/**
* Initialize FAQ Module
*/
botReady: function botReady() {
return function (state, actions) {
debug('init Default FAQ Module');
};
},
onAnswer: function onAnswer(response) {
return function (state, actions) {
// category type response is triggered when the module loads.
if (response.data.type === 'category') {
var categories = response.data.result;
actions.preLoadFaq(categories);
return {
categories: categories
};
} // answer type is the FAQ output request
if (response.data.type === 'answer') {
var faqs = state.faqs.map(function (f) {
if (f.id === response.metadata.id) {
f.answer = response.data.answer;
f.feedback = {
interactionId: response.metadata.interactionId,
shouldShowTextarea: false,
message: state.feedback.intro
};
}
return f;
});
var params = getURLParameters(state.urlQuestion);
if (params) {
(0, _index.scrollToEnd)();
}
return {
faqs: faqs
};
}
};
},
loadCategories: function loadCategories() {
return function (state, actions) {
var _core$config$classifi, _core$config;
var classificationId = (_core$config$classifi = (_core$config = core.config) === null || _core$config === void 0 ? void 0 : _core$config.classificationId) !== null && _core$config$classifi !== void 0 ? _core$config$classifi : state.classificationId;
var faqPayload = {
data: {
classificationId: classificationId
},
metadata: {
includeFaqs: true
}
};
core.getAllFaqs(faqPayload);
};
},
/**
* Updates URL hash
* @param {{ selectedCategories: Array.<number>, categoryName: string, faqId: number, faq: string }} urlProperties
*/
updateUrlProperties: function updateUrlProperties(urlProperties) {
return function (state, actions) {
var selectedCategories = urlProperties.selectedCategories,
categoryName = urlProperties.categoryName,
faqId = urlProperties.faqId,
faq = urlProperties.faq; // no category is selected
if (selectedCategories && selectedCategories[0] === -1) {
window.location.hash = '';
} else {
var hash = state.urlQuestion.replace(/{categorycrumbs}/i, selectedCategories.filter(function (id) {
return id > 0;
}).join('-')).replace(/{categoryname}/i, categoryName).replace(/{faqid}/i, faqId || '');
hash = faq ? hash.replace(/{question}/i, faq) : hash.replace(/\/?{question}/i, '');
window.location.hash = hash;
}
return {
urlProperties: urlProperties
};
};
},
/**
* checks if URL contains urlQuestion to pre-select elements
**/
preLoadFaq: function preLoadFaq(categories) {
return function (state, actions) {
var params = getURLParameters(state.urlQuestion);
var newState = {};
if (params && !shallowObjectEquals(newState.urlProperties, params)) {
actions.updateUrlProperties(params);
try {
var selectedCategories = params.selectedCategories;
var cat = (0, _index.getCategoryById)(selectedCategories[selectedCategories.length - 1], categories);
if (cat.subCategories.length > 0) {
selectedCategories = [].concat(_toConsumableArray(selectedCategories), [-1]);
}
newState.depth = selectedCategories.length - 1;
newState.selectedCategories = selectedCategories;
newState.catItem = cat;
newState.faqs = newState.catItem.items.map(function (faq) {
if (faq.id === params.faqId) {
faq.isSelected = true;
}
faq.feedback = faq.feedback || {};
return faq;
});
} catch (error) {
debug(error);
var _cat = (0, _index.prepareAndCategoriesByFaqId)(categories, params.faqId, '');
newState.selectedCategories = _cat.categoryCrumbs.split('-').map(function (n) {
return Number(n);
});
newState.depth = newState.selectedCategories.length - 1;
newState.catItem = (0, _index.getCategoryById)(newState.selectedCategories[newState.depth], categories);
newState.faqs = newState.catItem.items.map(function (faq) {
if (faq.id === params.faqId) {
faq.isSelected = true;
}
faq.feedback = faq.feedback || {};
return faq;
});
}
if (params.faqId) {
actions.faqClick({
id: params.faqId,
isPreloaded: true
});
}
}
return newState;
};
},
storeStateInStorage: function storeStateInStorage() {
return function (state) {
core.store(state, 'faq');
};
},
/**
* When the user clicks a category could be toggling it on or off.
* @param {Object} category
*/
categoryClick: function categoryClick(category) {
return function (state, actions) {
debug('category clicked', category);
var selectedCategories = (0, _index.toggleCategory)(category, state.selectedCategories);
if (state.updateUrl) {
// when toggling off the clicked category isn't selected anymore.
var isTogglingOff = !selectedCategories.some(function (c) {
return c === category.id;
});
var params = {
selectedCategories: selectedCategories,
categoryName: category.name,
faqId: '',
faq: ''
};
if (isTogglingOff) {
// remove any -1 in the list and get the last cat.
var id = selectedCategories.filter(function (catId) {
return catId !== -1;
}).pop();
if (id) {
// find the active cat
var activeCat = (0, _index.getCategoryById)(id, state.categories);
params.categoryName = activeCat.name;
} else {
// deselect all
params.categoryName = '';
}
}
actions.updateUrlProperties(params);
}
actions.resetActiveFaqs(); // when deselected empty FAQ array
return {
selectedCategories: selectedCategories,
faqs: category.isSelected ? [] : category.items,
depth: category.depth,
currentCategory: category.name
};
};
},
/**
* FAQ click action. Toggles the isSelected state and makes API call if needed.
*/
faqClick: function faqClick(faq) {
return function (state, actions) {
var id = faq.id,
question = faq.question,
answer = faq.answer,
isPreloaded = faq.isPreloaded;
debug('faq clicked', faq);
if (!answer && id) {
if (state.updateUrl) {
var params = getURLParameters(state.urlQuestion);
params.faqId = id;
params.faq = question || params.faq;
if (!shallowObjectEquals(state.urlProperties, params)) {
actions.updateUrlProperties(params);
}
} // to avoid double request
if (!state.faqs.some(function (f) {
return f.id === id && f.isSelected && typeof answer !== 'undefined';
})) {
core.faqAsk(faq.id);
}
}
var faqs = state.faqs.map(function (f) {
if (f.id === faq.id) {
if (typeof f.isSelected === 'undefined') {
f.isSelected = true;
} else {
if (isPreloaded) {
f.isSelected = true;
} else {
f.isSelected = !f.isSelected;
}
}
}
if (typeof f.feedback === 'undefined') {
f.feedback = {};
}
return f;
});
return {
faqs: faqs
};
};
},
handleClick: function handleClick(_ref) {
var interactionId = _ref.interactionId,
linkUrl = _ref.linkUrl,
linkId = _ref.linkId;
return function (state, actions) {
core.sendLinkClick({
data: {
interactionId: interactionId,
linkUrl: linkUrl,
linkId: linkId
}
});
};
},
setWidth: function setWidth(width) {
return function (state, actions) {
return {
width: width
};
};
},
setNodesInView: function setNodesInView(nodesInView) {
return function (state, actions) {
return {
nodesInView: nodesInView
};
};
},
resetActiveFaqs: function resetActiveFaqs() {
return function (state) {
var faqs = state.faqs.map(function (f) {
return f.isSelected = false;
});
return {
faqs: faqs
};
};
},
resize: function resize(elm) {
return function (state, actions) {
var fontSize = window.getComputedStyle(document.documentElement, null).fontSize;
fontSize = parseFloat(fontSize) || 16;
var appWidth = Math.ceil(elm.offsetWidth / fontSize);
var minWidthArr = []; // maximum amount of breakpoints.
var max = state.maxBreakpoints || 3; // amount of size increment in each "breakpoint".
var step = 20;
for (var i = 1; i <= max; i++) {
var val = step * i;
if (appWidth >= val) {
minWidthArr.push("".concat(val, "rem"));
}
}
actions.setWidth(minWidthArr.join(' '));
actions.setNodesInView(minWidthArr.length);
(0, _index.scrollToEnd)();
};
},
hashChanged: function hashChanged() {
return function (state, actions) {
var params = getURLParameters(state.urlQuestion);
if (params && typeof params.faq !== 'undefined') {
actions.preLoadFaq(state.categories);
}
};
},
/**
* Send Feedback or Show text area on negative feedback.
* If score is negative but there is no comment then show the textarea.
* Negative feedback is submitted once comment isn't empty.
* @param {Object} faq
*/
feedbackClick: function feedbackClick(faq) {
return function (state, actions) {
var feedback = faq.feedback;
var score = feedback.score,
interactionId = feedback.interactionId,
comment = feedback.comment,
label = feedback.label;
var hasComment = typeof comment !== 'undefined' && comment !== '';
var feedbackPayload = {
data: {
score: score,
interactionId: interactionId,
comment: comment,
label: label
}
};
if (feedback.score > 0) {
core.sendFeedback(feedbackPayload);
var faqs = state.faqs.map(function (f) {
if (f.id === faq.id) {
f.feedback.shouldShowTextarea = false;
f.feedback.message = state.feedback.thanks;
f.feedback.sent = true;
}
return f;
});
return {
faqs: faqs
};
} else {
var _faqs = state.faqs.map(function (f) {
if (f.id === faq.id) {
f.feedback.shouldShowTextarea = !hasComment;
if (hasComment) {
f.feedback.message = state.feedback.thanks;
f.feedback.sent = true;
}
}
return f;
});
if (hasComment) {
core.sendFeedback(feedbackPayload);
}
return {
faqs: _faqs
};
}
};
}
};
}