Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7d23d9435 | ||
|
|
9f64d50011 | ||
|
|
f926f5e500 | ||
|
|
c9f6fb202c | ||
|
|
7830fec604 | ||
|
|
ba04e0b678 | ||
|
|
8625a00918 | ||
|
|
16e638aeb7 |
@@ -109,6 +109,15 @@ const runSDK = ({ baseUrl, websiteToken }) => {
|
||||
});
|
||||
},
|
||||
|
||||
openArticle(slug) {
|
||||
if (!slug) {
|
||||
throw new Error('Article slug is required');
|
||||
}
|
||||
|
||||
IFrameHelper.events.toggleBubble('open');
|
||||
IFrameHelper.sendMessage('open-article', { slug });
|
||||
},
|
||||
|
||||
setUser(identifier, user) {
|
||||
if (typeof identifier !== 'string' && typeof identifier !== 'number') {
|
||||
throw new Error('Identifier should be a string or a number');
|
||||
|
||||
@@ -38,3 +38,24 @@ export const getMatchingLocale = (selectedLocale = '', allowedLocales = []) => {
|
||||
// Return the first match that exists in the allowed list, or null
|
||||
return priorityMatches.find(l => l && allowedLocales.includes(l)) ?? null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the link consumed by the in-widget article viewer, appending the query
|
||||
* params it expects (plain layout, theme and locale).
|
||||
*
|
||||
* @export
|
||||
* @param {Object} options
|
||||
* @param {string} options.link Relative article/portal path (e.g. `hc/slug/articles/foo`).
|
||||
* @param {(string|null)} [options.locale] Resolved portal locale.
|
||||
* @param {boolean} [options.prefersDarkMode] Whether the widget is in dark mode.
|
||||
* @returns {string} The link with the article viewer query params appended.
|
||||
*/
|
||||
export const buildArticleViewerLink = ({ link, locale, prefersDarkMode }) => {
|
||||
const params = new URLSearchParams({
|
||||
show_plain_layout: 'true',
|
||||
theme: prefersDarkMode ? 'dark' : 'light',
|
||||
...(locale && { locale }),
|
||||
});
|
||||
|
||||
return `${link}?${params.toString()}`;
|
||||
};
|
||||
|
||||
@@ -22,6 +22,10 @@ import { useRouter } from 'vue-router';
|
||||
import { useAvailability } from 'widget/composables/useAvailability';
|
||||
import { SDK_SET_BUBBLE_VISIBILITY } from '../shared/constants/sharedFrameEvents';
|
||||
import { emitter } from 'shared/helpers/mitt';
|
||||
import {
|
||||
getMatchingLocale,
|
||||
buildArticleViewerLink,
|
||||
} from 'shared/helpers/portalHelper';
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
@@ -160,6 +164,26 @@ export default {
|
||||
this.$root.$i18n.locale = localeWithoutVariation;
|
||||
}
|
||||
},
|
||||
openArticle(slug) {
|
||||
const { portal } = window.chatwootWebChannel;
|
||||
if (!portal || !slug) return;
|
||||
|
||||
const locale = getMatchingLocale(
|
||||
this.$root.$i18n.locale,
|
||||
portal.config?.allowed_locales
|
||||
);
|
||||
const link = buildArticleViewerLink({
|
||||
link: `hc/${portal.slug}/articles/${slug}`,
|
||||
locale,
|
||||
prefersDarkMode: this.prefersDarkMode,
|
||||
});
|
||||
// Add a timestamp so the route always changes, even when the same article
|
||||
// is requested again or the iframe was browsed to another page.
|
||||
this.router.push({
|
||||
name: 'article-viewer',
|
||||
query: { link, v: Date.now() },
|
||||
});
|
||||
},
|
||||
registerUnreadEvents() {
|
||||
emitter.on(ON_AGENT_MESSAGE_RECEIVED, () => {
|
||||
const { name: routeName } = this.$route;
|
||||
@@ -316,6 +340,8 @@ export default {
|
||||
this.setBubbleLabel();
|
||||
} else if (message.event === 'set-color-scheme') {
|
||||
this.setColorScheme(message.darkMode);
|
||||
} else if (message.event === 'open-article') {
|
||||
this.openArticle(message.slug);
|
||||
} else if (message.event === 'toggle-open') {
|
||||
this.$store.dispatch('appConfig/toggleWidgetOpen', message.isOpen);
|
||||
|
||||
|
||||
@@ -7,7 +7,10 @@ import { useRouter } from 'vue-router';
|
||||
import { useStore } from 'dashboard/composables/store';
|
||||
import { useMapGetter } from 'dashboard/composables/store.js';
|
||||
import { useDarkMode } from 'widget/composables/useDarkMode';
|
||||
import { getMatchingLocale } from 'shared/helpers/portalHelper';
|
||||
import {
|
||||
getMatchingLocale,
|
||||
buildArticleViewerLink,
|
||||
} from 'shared/helpers/portalHelper';
|
||||
|
||||
const store = useStore();
|
||||
const router = useRouter();
|
||||
@@ -38,14 +41,11 @@ const fetchArticles = () => {
|
||||
};
|
||||
|
||||
const openArticleInArticleViewer = link => {
|
||||
const params = new URLSearchParams({
|
||||
show_plain_layout: 'true',
|
||||
theme: prefersDarkMode.value ? 'dark' : 'light',
|
||||
...(locale.value && { locale: locale.value }),
|
||||
const linkToOpen = buildArticleViewerLink({
|
||||
link,
|
||||
locale: locale.value,
|
||||
prefersDarkMode: prefersDarkMode.value,
|
||||
});
|
||||
|
||||
// Combine link with query parameters
|
||||
const linkToOpen = `${link}?${params.toString()}`;
|
||||
router.push({ name: 'article-viewer', query: { link: linkToOpen } });
|
||||
};
|
||||
|
||||
|
||||
@@ -11,6 +11,11 @@ export default {
|
||||
|
||||
<template>
|
||||
<div class="bg-white h-full">
|
||||
<IframeLoader :url="$route.query.link" />
|
||||
<!--
|
||||
Key by fullPath (not just the link) so the iframe remounts on every
|
||||
navigation here, including re-opening the same article via the SDK after
|
||||
the iframe was browsed to another help-center page. See App.vue#openArticle.
|
||||
-->
|
||||
<IframeLoader :key="$route.fullPath" :url="$route.query.link" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user