feat: Lets users insert connected portal article into replies [CW-2282] (#8117)

- Lets users insert connected portal articles into replies

https://linear.app/chatwoot/issue/CW-2282/list-all-the-top-articles-from-the-connected-help-center
https://linear.app/chatwoot/issue/CW-1453/container-view-for-showing-search-input-and-result-items
This commit is contained in:
Nithin David Thomas
2023-11-04 15:27:25 +05:30
committed by GitHub
parent b4d20689b7
commit 39d0748a5b
14 changed files with 327 additions and 54 deletions

View File

@@ -109,6 +109,16 @@
</h4>
</div>
</transition>
<woot-button
v-if="enableInsertArticleInReply"
v-tooltip.top-end="$t('HELP_CENTER.ARTICLE_SEARCH.OPEN_ARTICLE_SEARCH')"
icon="document-text-link"
color-scheme="secondary"
variant="smooth"
size="small"
:title="$t('HELP_CENTER.ARTICLE_SEARCH.OPEN_ARTICLE_SEARCH')"
@click="toggleInsertArticle"
/>
</div>
<div class="right-wrap">
<woot-button
@@ -233,6 +243,10 @@ export default {
type: Boolean,
default: false,
},
portalSlug: {
type: String,
required: true,
},
},
computed: {
...mapGetters({
@@ -307,6 +321,13 @@ export default {
? this.$t('CONVERSATION.FOOTER.DISABLE_SIGN_TOOLTIP')
: this.$t('CONVERSATION.FOOTER.ENABLE_SIGN_TOOLTIP');
},
enableInsertArticleInReply() {
const isFeatEnabled = this.isFeatureEnabledonAccount(
this.accountId,
FEATURE_FLAGS.INSERT_ARTICLE_IN_REPLY
);
return isFeatEnabled && this.portalSlug;
},
},
mounted() {
ActiveStorage.start();
@@ -325,6 +346,9 @@ export default {
replaceText(text) {
this.$emit('replace-text', text);
},
toggleInsertArticle() {
this.$emit('toggle-insert-article');
},
},
};
</script>

View File

@@ -18,6 +18,12 @@
:popout-reply-box="popoutReplyBox"
@click="$emit('click')"
/>
<article-search-popover
v-if="showArticleSearchPopover && connectedPortalSlug"
:selected-portal-slug="connectedPortalSlug"
@insert="handleInsert"
@close="onSearchPopoverClose"
/>
<div class="reply-box__top">
<reply-to-message
v-if="shouldShowReplyToMessage"
@@ -35,7 +41,7 @@
v-if="showEmojiPicker"
v-on-clickaway="hideEmojiPicker"
:class="emojiDialogClassOnExpandedLayoutAndRTLView"
:on-click="emojiOnClick"
:on-click="addIntoEditor"
/>
<reply-email-head
v-if="showReplyHead"
@@ -121,10 +127,12 @@
:toggle-audio-recorder="toggleAudioRecorder"
:toggle-emoji-picker="toggleEmojiPicker"
:message="message"
:portal-slug="connectedPortalSlug"
:new-conversation-modal-active="newConversationModalActive"
@selectWhatsappTemplate="openWhatsappTemplateModal"
@toggle-editor="toggleRichContentEditor"
@replace-text="replaceText"
@toggle-insert-article="toggleInsertArticle"
/>
<whatsapp-templates
:inbox-id="inbox.id"
@@ -154,6 +162,7 @@ import AttachmentPreview from 'dashboard/components/widgets/AttachmentsPreview.v
import ReplyTopPanel from 'dashboard/components/widgets/WootWriter/ReplyTopPanel.vue';
import ReplyEmailHead from './ReplyEmailHead.vue';
import ReplyBottomPanel from 'dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue';
import ArticleSearchPopover from 'dashboard/routes/dashboard/helpcenter/components/ArticleSearch/SearchPopover.vue';
import MessageSignatureMissingAlert from './MessageSignatureMissingAlert';
import Banner from 'dashboard/components/ui/Banner.vue';
import { REPLY_EDITOR_MODES } from 'dashboard/components/widgets/WootWriter/constants';
@@ -206,6 +215,7 @@ export default {
Banner,
WhatsappTemplates,
MessageSignatureMissingAlert,
ArticleSearchPopover,
},
mixins: [
clickaway,
@@ -248,6 +258,7 @@ export default {
showCannedMenu: false,
showVariablesMenu: false,
newConversationModalActive: false,
showArticleSearchPopover: false,
};
},
computed: {
@@ -506,6 +517,11 @@ export default {
? this.messageSignature
: extractTextFromMarkdown(this.messageSignature);
},
connectedPortalSlug() {
const { help_center: portal = {} } = this.inbox;
const { slug = '' } = portal;
return slug;
},
},
watch: {
currentChat(conversation) {
@@ -597,6 +613,23 @@ export default {
);
},
methods: {
handleInsert(article) {
const { url, title } = article;
if (this.isRichEditorEnabled) {
// Removing empty lines from the title
const lines = title.split('\n');
const nonEmptyLines = lines.filter(line => line.trim() !== '');
const filteredMarkdown = nonEmptyLines.join(' ');
bus.$emit(
BUS_EVENTS.INSERT_INTO_RICH_EDITOR,
`[${filteredMarkdown}](${url})`
);
} else {
this.addIntoEditor(
`${this.$t('CONVERSATION.REPLYBOX.INSERT_READ_MORE')} ${url}`
);
}
},
toggleRichContentEditor() {
this.updateUISettings({
display_rich_content_editor: !this.showRichContentEditor,
@@ -862,22 +895,22 @@ export default {
clearEditorSelection() {
this.updateEditorSelectionWith = '';
},
insertEmoji(emoji, selectionStart, selectionEnd) {
insertIntoTextEditor(text, selectionStart, selectionEnd) {
const { message } = this;
const newMessage =
message.slice(0, selectionStart) +
emoji +
text +
message.slice(selectionEnd, message.length);
this.message = newMessage;
},
emojiOnClick(emoji) {
addIntoEditor(content) {
if (this.showRichContentEditor) {
this.updateEditorSelectionWith = emoji;
this.updateEditorSelectionWith = content;
this.onFocus();
}
if (!this.showRichContentEditor) {
const { selectionStart, selectionEnd } = this.$refs.messageInput.$el;
this.insertEmoji(emoji, selectionStart, selectionEnd);
this.insertIntoTextEditor(content, selectionStart, selectionEnd);
}
},
clearMessage() {
@@ -1136,6 +1169,12 @@ export default {
// When new conversation modal is open
this.newConversationModalActive = isActive;
},
onSearchPopoverClose() {
this.showArticleSearchPopover = false;
},
toggleInsertArticle() {
this.showArticleSearchPopover = !this.showArticleSearchPopover;
},
},
};
</script>
@@ -1154,7 +1193,7 @@ export default {
}
.reply-box {
@apply border-t border-slate-50 dark:border-slate-700 bg-white dark:bg-slate-900;
@apply relative border-t border-slate-50 dark:border-slate-700 bg-white dark:bg-slate-900;
&.is-private {
@apply bg-yellow-50 dark:bg-yellow-200;

View File

@@ -17,4 +17,5 @@ export const FEATURE_FLAGS = {
VOICE_RECORDER: 'voice_recorder',
AUDIT_LOGS: 'audit_logs',
MESSAGE_REPLY_TO: 'message_reply_to',
INSERT_ARTICLE_IN_REPLY: 'insert_article_in_reply',
};

View File

@@ -139,6 +139,7 @@
"PRIVATE_NOTE": "Private Note",
"SEND": "Send",
"CREATE": "Add Note",
"INSERT_READ_MORE": "Read more",
"DISMISS_REPLY": "Dismiss reply",
"REPLYING_TO": "Replying to:",
"TIP_FORMAT_ICON": "Show rich text editor",

View File

@@ -76,6 +76,9 @@
},
"ARTICLE_SEARCH_RESULT": {
"UNCATEGORIZED": "Uncategorized",
"SEARCH_RESULTS": "Search results for %{query}",
"EMPTY_TEXT": "Search for articles to insert into replies.",
"SEARCH_LOADER": "Searching...",
"INSERT_ARTICLE": "Insert",
"NO_RESULT": "No articles found",
"COPY_LINK": "Copy article link to clipboard",
@@ -426,6 +429,21 @@
}
}
},
"ARTICLE_SEARCH": {
"TITLE": "Search articles",
"PLACEHOLDER": "Search articles",
"NO_RESULT": "No articles found",
"SEARCHING": "Searching...",
"SEARCH_BUTTON": "Search",
"INSERT_ARTICLE": "Insert link",
"IFRAME_ERROR": "URL is empty or invalid. Unable to display content.",
"OPEN_ARTICLE_SEARCH": "Insert article from Help Center",
"SUCCESS_ARTICLE_INSERTED": "Article inserted successfully",
"PREVIEW_LINK": "Preview article",
"CANCEL": "Close",
"BACK": "Back",
"BACK_RESULTS": "Back to results"
},
"UPGRADE_PAGE": {
"TITLE": "Help Center",
"DESCRIPTION": "Create user-friendly self-service portals. Help your users to access the articles and get support 24/7. Upgrade your subscription to enable this feature.",

View File

@@ -1,17 +1,18 @@
<template>
<div
class="flex flex-col gap-1 bg-white dark:bg-slate-900 hover:bg-slate-25 hover:dark:bg-slate-800 rounded-md py-1 px-2 w-full group"
<button
class="flex flex-col gap-1 bg-white dark:bg-slate-900 hover:bg-slate-25 hover:dark:bg-slate-800 rounded-md py-1 px-2 w-full group border border-transparent border-solid focus:outline-none focus:bg-slate-25 focus:border-slate-500 dark:focus:border-slate-400 dark:focus:bg-slate-800 cursor-pointer"
@click="handlePreview"
>
<button @click="handlePreview">
<h4
class="text-block-title text-left mb-0 text-slate-900 dark:text-slate-25 px-1 -mx-1 rounded-sm hover:underline cursor-pointer width-auto"
class="text-block-title text-left mb-0 text-slate-900 dark:text-slate-25 px-1 -mx-1 rounded-sm width-auto hover:underline group-hover:underline"
>
{{ title }}
</h4>
</button>
<div class="flex content-between items-center gap-0.5 w-full">
<p class="text-sm text-slate-600 dark:text-slate-300 mb-0 w-full">
<p
class="text-sm text-left text-slate-600 dark:text-slate-300 mb-0 w-full"
>
{{ locale }}
{{ ` / ` }}
{{ category || $t('HELP_CENTER.ARTICLE_SEARCH_RESULT.UNCATEGORIZED') }}
@@ -26,26 +27,6 @@
class="invisible group-hover:visible"
@click="handleCopy"
/>
<a
:href="url"
class="button hollow button--only-icon tiny secondary invisible group-hover:visible"
rel="noopener noreferrer nofollow"
target="_blank"
:title="$t('HELP_CENTER.ARTICLE_SEARCH_RESULT.OPEN_LINK')"
>
<fluent-icon size="12" icon="arrow-up-right" />
<span class="show-for-sr">{{ url }}</span>
</a>
<woot-button
variant="hollow"
color-scheme="secondary"
size="tiny"
icon="preview-link"
class="invisible group-hover:visible"
:title="$t('HELP_CENTER.ARTICLE_SEARCH_RESULT.PREVIEW_LINK')"
@click="handlePreview"
/>
<woot-button
class="insert-button"
variant="smooth"
@@ -57,14 +38,16 @@
</woot-button>
</div>
</div>
</div>
</button>
</template>
<script>
import { copyTextToClipboard } from 'shared/helpers/clipboard';
import alertMixin from 'shared/mixins/alertMixin';
export default {
name: 'ArticleSearchResultItem',
mixins: [alertMixin],
props: {
id: {
type: Number,
@@ -92,13 +75,16 @@ export default {
},
},
methods: {
handleInsert() {
handleInsert(e) {
e.stopPropagation();
this.$emit('insert', this.id);
},
handlePreview() {
handlePreview(e) {
e.stopPropagation();
this.$emit('preview', this.id);
},
async handleCopy() {
async handleCopy(e) {
e.stopPropagation();
await copyTextToClipboard(this.url);
this.showAlert(this.$t('CONTACT_PANEL.COPY_SUCCESSFUL'));
},

View File

@@ -10,9 +10,11 @@
{{ $t('HELP_CENTER.ARTICLE_SEARCH.BACK_RESULTS') }}
</woot-button>
</div>
<div class="w-full h-full overflow-auto min-h-0">
<div class="-ml-4 h-full overflow-y-auto">
<div class="w-full h-full min-h-0">
<iframe-loader :url="url" />
</div>
</div>
<div class="flex justify-end gap-2 py-2">
<woot-button
@@ -41,7 +43,7 @@
import IframeLoader from 'shared/components/IframeLoader.vue';
export default {
name: 'ChatwootSearch',
name: 'ArticleView',
components: {
IframeLoader,
},
@@ -52,10 +54,12 @@ export default {
},
},
methods: {
onBack() {
onBack(e) {
e.stopPropagation();
this.$emit('back');
},
onInsert() {
onInsert(e) {
e.stopPropagation();
this.$emit('insert');
},
},

View File

@@ -18,10 +18,13 @@
<fluent-icon icon="search" class="" size="16" />
</div>
<input
ref="searchInput"
type="text"
:placeholder="$t('HELP_CENTER.ARTICLE_SEARCH.PLACEHOLDER')"
class="block w-full pl-8 h-8 text-sm dark:bg-slate-700 bg-slate-25 rounded-md leading-8 py-1 text-slate-700 shadow-sm ring-2 ring-transparent ring-slate-300 border border-solid border-slate-300 placeholder:text-slate-400 focus:border-woot-600 focus:ring-2 focus:ring-woot-100 mb-0 focus:bg-slate-25 dark:focus:bg-slate-700"
class="block w-full pl-8 h-8 text-sm dark:bg-slate-700 bg-slate-25 rounded-md leading-8 py-1 text-slate-700 shadow-sm ring-2 ring-transparent ring-slate-300 border border-solid border-slate-300 placeholder:text-slate-400 focus:border-woot-600 focus:ring-woot-200 mb-0 focus:bg-slate-25 dark:focus:bg-slate-700 dark:focus:ring-woot-700"
:value="searchQuery"
@focus="onFocus"
@blur="onBlur"
@input="onInput"
/>
</div>
@@ -29,8 +32,15 @@
</template>
<script>
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
import {
buildHotKeys,
isActiveElementTypeable,
} from 'shared/helpers/KeyboardHelpers';
export default {
name: 'ChatwootSearch',
mixins: [eventListenerMixins],
props: {
title: {
type: String,
@@ -40,8 +50,12 @@ export default {
data() {
return {
searchQuery: '',
isInputFocused: false,
};
},
mounted() {
this.$refs.searchInput.focus();
},
methods: {
onInput(e) {
this.$emit('search', e.target.value);
@@ -49,6 +63,20 @@ export default {
onClose() {
this.$emit('close');
},
onFocus() {
this.isInputFocused = true;
},
onBlur() {
this.isInputFocused = false;
},
handleKeyEvents(e) {
const keyPattern = buildHotKeys(e);
if (keyPattern === '/' && !isActiveElementTypeable(e)) {
e.preventDefault();
this.$refs.searchInput.focus();
}
},
},
};
</script>

View File

@@ -0,0 +1,169 @@
<template>
<div
class="fixed flex items-center justify-center w-screen h-screen bg-white/70 top-0 left-0 z-50"
>
<div
v-on-clickaway="onClose"
class="flex flex-col px-4 pb-4 rounded-md shadow-md border border-solid border-slate-50 dark:border-slate-800 bg-white dark:bg-slate-900 z-[1000] max-w-[720px] md:w-[20rem] lg:w-[24rem] xl:w-[28rem] 2xl:w-[32rem] h-[calc(100vh-20rem)] max-h-[40rem]"
>
<search-header
:title="$t('HELP_CENTER.ARTICLE_SEARCH.TITLE')"
class="w-full sticky top-0 bg-[inherit]"
@close="onClose"
@search="onSearch"
/>
<article-view
v-if="activeId"
:url="articleViewerUrl"
@back="onBack"
@insert="onInsert"
/>
<search-results
v-else
:search-query="searchQuery"
:is-loading="isLoading"
:portal-slug="selectedPortalSlug"
:articles="searchResultsWithUrl"
@preview="handlePreview"
@insert="onInsert"
/>
</div>
</div>
</template>
<script>
import { debounce } from '@chatwoot/utils';
import { mixin as clickaway } from 'vue-clickaway';
import {
isEscape,
isActiveElementTypeable,
} from 'shared/helpers/KeyboardHelpers';
import SearchHeader from './Header.vue';
import SearchResults from './SearchResults.vue';
import ArticleView from './ArticleView.vue';
import ArticlesAPI from 'dashboard/api/helpCenter/articles';
import { buildPortalArticleURL } from 'dashboard/helper/portalHelper';
import portalMixin from '../../mixins/portalMixin';
import alertMixin from 'shared/mixins/alertMixin';
export default {
name: 'ArticleSearchPopover',
components: {
SearchHeader,
SearchResults,
ArticleView,
},
mixins: [clickaway, portalMixin, alertMixin],
props: {
selectedPortalSlug: {
type: String,
required: true,
},
},
data() {
return {
searchQuery: '',
isLoading: false,
searchResults: [],
activeId: '',
debounceSearch: () => {},
};
},
computed: {
articleViewerUrl() {
const article = this.activeArticle(this.activeId);
if (!article) return '';
const isDark = document.body.classList.contains('dark');
const url = new URL(article.url);
url.searchParams.set('show_plain_layout', 'true');
if (isDark) {
url.searchParams.set('theme', 'dark');
}
return `${url}`;
},
searchResultsWithUrl() {
return this.searchResults.map(article => ({
...article,
localeName: this.localeName(article.category.locale || 'en'),
url: this.generateArticleUrl(article),
}));
},
},
mounted() {
this.fetchArticlesByQuery(this.searchQuery);
this.debounceSearch = debounce(this.fetchArticlesByQuery, 500, false);
document.body.addEventListener('keydown', this.closeOnEsc);
},
beforeDestroy() {
document.body.removeEventListener('keydown', this.closeOnEsc);
},
methods: {
generateArticleUrl(article) {
return buildPortalArticleURL(
this.selectedPortalSlug,
'',
'',
article.slug
);
},
activeArticle(id) {
return this.searchResultsWithUrl.find(article => article.id === id);
},
onSearch(query) {
this.searchQuery = query;
this.activeId = '';
this.debounceSearch(query);
},
onClose() {
this.$emit('close');
this.searchQuery = '';
this.activeId = '';
this.searchResults = [];
},
async fetchArticlesByQuery(query) {
try {
const sort = query ? '' : 'views';
this.isLoading = true;
this.searchResults = [];
const { data } = await ArticlesAPI.searchArticles({
portalSlug: this.selectedPortalSlug,
query,
sort,
});
this.searchResults = data.payload;
this.isLoading = true;
} catch (error) {
// Show something wrong message
} finally {
this.isLoading = false;
}
},
handlePreview(id) {
this.activeId = id;
},
onBack() {
this.activeId = '';
},
onInsert(id) {
const article = this.activeArticle(id || this.activeId);
this.$emit('insert', article);
this.showAlert(
this.$t('HELP_CENTER.ARTICLE_SEARCH.SUCCESS_ARTICLE_INSERTED')
);
this.onClose();
},
closeOnEsc(e) {
if (isEscape(e) && !isActiveElementTypeable(e)) {
e.preventDefault();
this.onClose();
}
},
},
};
</script>

View File

@@ -1,8 +1,9 @@
<template>
<div class="flex justify-end gap-1 py-4 bg-white dark:bg-slate-900">
<div
class="flex justify-end gap-1 py-4 bg-white dark:bg-slate-900 h-full overflow-y-auto"
>
<div class="flex flex-col gap-1 w-full">
<div v-if="isLoading" class="empty-state-message">
<spinner />
{{ $t('HELP_CENTER.ARTICLE_SEARCH_RESULT.SEARCH_LOADER') }}
</div>
<div v-else-if="showNoResults" class="empty-state-message">
@@ -17,7 +18,7 @@
:body="article.content"
:url="article.url"
:category="article.category.name"
:locale="article.category.locale"
:locale="article.localeName"
@preview="handlePreview"
@insert="handleInsert"
/>
@@ -26,13 +27,11 @@
</template>
<script>
import Spinner from 'shared/components/Spinner.vue';
import SearchResultItem from 'dashboard/routes/dashboard/helpcenter/components/ArticleSearch/ArticleSearchResultItem.vue';
import SearchResultItem from './ArticleSearchResultItem.vue';
export default {
name: 'SearchResults',
components: {
Spinner,
SearchResultItem,
},
props: {

View File

@@ -96,6 +96,7 @@
"dismiss-outline": "m4.397 4.554.073-.084a.75.75 0 0 1 .976-.073l.084.073L12 10.939l6.47-6.47a.75.75 0 1 1 1.06 1.061L13.061 12l6.47 6.47a.75.75 0 0 1 .072.976l-.073.084a.75.75 0 0 1-.976.073l-.084-.073L12 13.061l-6.47 6.47a.75.75 0 0 1-1.06-1.061L10.939 12l-6.47-6.47a.75.75 0 0 1-.072-.976l.073-.084-.073.084Z",
"document-outline": "M18.5 20a.5.5 0 0 1-.5.5H6a.5.5 0 0 1-.5-.5V4a.5.5 0 0 1 .5-.5h6V8a2 2 0 0 0 2 2h4.5v10Zm-5-15.379L17.378 8.5H14a.5.5 0 0 1-.5-.5V4.621Zm5.914 3.793-5.829-5.828c-.026-.026-.058-.046-.085-.07a2.072 2.072 0 0 0-.219-.18c-.04-.027-.086-.045-.128-.068-.071-.04-.141-.084-.216-.116a1.977 1.977 0 0 0-.624-.138C12.266 2.011 12.22 2 12.172 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9.828a2 2 0 0 0-.586-1.414Z",
"document-error-outline": "M6 2a2 2 0 0 0-2 2v5.207a5.48 5.48 0 0 1 1-.185V4a1 1 0 0 1 1-1h4v3.5A1.5 1.5 0 0 0 11.5 8H15v8a1 1 0 0 1-1 1h-3.6a5.507 5.507 0 0 1-.657 1H14a2 2 0 0 0 2-2V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 10.586 2H6Zm8.793 5H11.5a.5.5 0 0 1-.5-.5V3.207L14.793 7ZM10 14.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0ZM5.5 12a.5.5 0 0 0-.5.5v2a.5.5 0 0 0 1 0v-2a.5.5 0 0 0-.5-.5Zm0 5.125a.625.625 0 1 0 0-1.25a.625.625 0 0 0 0 1.25Z",
"document-text-link-outline": "M18 20.5a.5.5 0 0 0 .5-.5V10H14a2 2 0 0 1-2-2V3.5H6a.5.5 0 0 0-.5.5v10h-.75c-.255 0-.506.02-.75.059V4a2 2 0 0 1 2-2h6.172c.028 0 .055.004.082.007.02.003.04.006.059.007.215.015.427.056.624.138.057.024.112.056.166.087l.05.029.047.024a.652.652 0 0 1 .081.044c.078.053.148.116.219.18a.63.63 0 0 0 .036.03.491.491 0 0 1 .049.04l5.829 5.828A2 2 0 0 1 20 9.828V20a2 2 0 0 1-2 2h-6.286c.406-.432.731-.94.953-1.5H18Zm-.622-12L13.5 4.621V8a.5.5 0 0 0 .5.5h3.378Zm-7.603 5.75c.854.29 1.6.815 2.158 1.5h3.317a.75.75 0 0 0 0-1.5H9.775ZM12.667 17c.186.468.3.973.326 1.5h2.257a.75.75 0 0 0 0-1.5h-2.583ZM8.75 11.5a.75.75 0 0 0 0 1.5h6.5a.75.75 0 0 0 0-1.5h-6.5ZM12 18.75A3.75 3.75 0 0 0 8.25 15l-.102.007A.75.75 0 0 0 8.25 16.5l.154.005A2.25 2.25 0 0 1 8.25 21l-.003.005-.102.007a.75.75 0 0 0 .108 1.493V22.5l.2-.005A3.75 3.75 0 0 0 12 18.75Zm-6.5-3a.75.75 0 0 0-.75-.75l-.2.005a3.75 3.75 0 0 0 .2 7.495l.102-.007A.75.75 0 0 0 4.75 21l-.154-.005A2.25 2.25 0 0 1 4.75 16.5l.102-.007a.75.75 0 0 0 .648-.743Zm3.5 3a.75.75 0 0 0-.75-.75h-3.5l-.102.007A.75.75 0 0 0 4.75 19.5h3.5l.102-.007A.75.75 0 0 0 9 18.75Z",
"draft-outline": "m20.877 2.826.153.144.145.153a3.579 3.579 0 0 1-.145 4.908L9.062 19.999a2.25 2.25 0 0 1-1 .58l-5.115 1.395a.75.75 0 0 1-.92-.921l1.394-5.116a2.25 2.25 0 0 1 .58-.999L15.97 2.97a3.579 3.579 0 0 1 4.908-.144ZM15 6.06l-9.938 9.938a.75.75 0 0 0-.193.333l-1.05 3.85 3.85-1.05A.75.75 0 0 0 8 18.938L17.94 9 15 6.06ZM6.525 11l-1.5 1.5H2.75a.75.75 0 0 1 0-1.5h3.775Zm4-4-1.5 1.5H2.75a.75.75 0 1 1 0-1.5h7.775Zm6.505-2.97-.97.97 2.939 2.94.97-.97a2.078 2.078 0 1 0-2.939-2.94ZM14.525 3l-1.5 1.5H2.75a.75.75 0 1 1 0-1.5h11.775Z",
"drag-outline": "M15 3.707V8.5a.5.5 0 0 0 1 0V3.707l1.146 1.147a.5.5 0 0 0 .708-.708l-2-2a.499.499 0 0 0-.708 0l-2 2a.5.5 0 0 0 .708.708L15 3.707ZM2 4.5a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 0 1h-6a.5.5 0 0 1-.5-.5Zm0 5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5Zm.5 4.5a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1h-6ZM15 16.293V11.5a.5.5 0 0 1 1 0v4.793l1.146-1.147a.5.5 0 0 1 .708.708l-2 2a.5.5 0 0 1-.708 0l-2-2a.5.5 0 0 1 .708-.708L15 16.293Z",
"dual-screen-clock-outline": "M10.019 6.002a6.632 6.632 0 0 0 .058 1.5H3.75a.25.25 0 0 0-.25.25v12.494c0 .138.112.25.25.25h7.498l-.001-10.167c.416.57.924 1.07 1.5 1.479v8.69h7.498a.25.25 0 0 0 .25-.25v-8.62a6.535 6.535 0 0 0 1.501-1.656V20.25a1.75 1.75 0 0 1-1.75 1.75h-8.998l-.001-.003H3.75A1.75 1.75 0 0 1 2 20.246V7.751c0-.966.784-1.75 1.75-1.75h6.269Zm6.22 11.498a.75.75 0 0 1 .101 1.493L16.24 19h-1.5a.75.75 0 0 1-.102-1.493l.102-.007h1.5Zm-6.996 0a.75.75 0 0 1 .102 1.493L9.243 19H7.74a.75.75 0 0 1-.102-1.493l.102-.007h1.502ZM16.498 1a5.5 5.5 0 1 1 0 11 5.5 5.5 0 0 1 0-11Zm-1 2a.5.5 0 0 0-.5.5v4a.5.5 0 0 0 .5.5h3.001a.5.5 0 0 0 0-1h-2.501V3.5a.5.5 0 0 0-.5-.5Z",

View File

@@ -24,7 +24,7 @@
import ArticleSkeletonLoader from 'shared/components/ArticleSkeletonLoader.vue';
export default {
name: 'IframeRenderer',
name: 'IframeLoader',
components: {
ArticleSkeletonLoader,
},

View File

@@ -11,4 +11,5 @@ export const BUS_EVENTS = {
TOGGLE_REPLY_TO_MESSAGE: 'TOGGLE_REPLY_TO_MESSAGE',
SHOW_TOAST: 'newToastMessage',
NEW_CONVERSATION_MODAL: 'newConversationModal',
INSERT_INTO_RICH_EDITOR: 'insertIntoRichEditor',
};

View File

@@ -59,3 +59,5 @@
enabled: false
- name: message_reply_to
enabled: false
- name: insert_article_in_reply
enabled: false