iachat/app/javascript/dashboard/routes/dashboard/conversation/search/Search.vue
Pranav Raj S 7718cf7d2c
feat: Add global message search (#1385)
* feat: Search messages by content

* Fix search UI

* Add specs

* chore: Filter search results

* Update highlight logic

* Rename query to searchTerm

Co-authored-by: Sojan <sojan@pepalo.com>
2020-11-08 01:46:45 +05:30

174 lines
3.9 KiB
Vue

<template>
<woot-modal
class="message-search--modal"
:show.sync="show"
:on-close="onClose"
>
<woot-modal-header :header-title="$t('CONVERSATION.SEARCH.TITLE')" />
<div class="search--container">
<input
v-model="searchTerm"
v-focus
:placeholder="$t('CONVERSATION.SEARCH.PLACEHOLDER')"
type="text"
/>
<div v-if="uiFlags.isFetching" class="search--activity-message">
<woot-spinner size="" />
{{ $t('CONVERSATION.SEARCH.LOADING_MESSAGE') }}
</div>
<div
v-if="searchTerm && conversations.length && !uiFlags.isFetching"
class="search-results--container"
>
<div v-for="conversation in conversations" :key="conversation.id">
<button
v-for="message in conversation.messages"
:key="message.id"
class="search--messages"
@click="() => onClick(conversation)"
>
<div class="search--messages__metadata">
<span>#{{ conversation.id }}</span>
<span>{{ dynamicTime(message.created_at) }}</span>
</div>
<div v-html="prepareContent(message.content)" />
</button>
</div>
</div>
<div
v-else-if="
searchTerm &&
!conversations.length &&
!uiFlags.isFetching &&
hasSearched
"
class="search--activity-message"
>
{{ $t('CONVERSATION.SEARCH.NO_MATCHING_RESULTS') }}
</div>
</div>
</woot-modal>
</template>
<script>
import { mapGetters } from 'vuex';
import { frontendURL, conversationUrl } from '../../../../helper/URLHelper';
import timeMixin from '../../../../mixins/time';
export default {
directives: {
focus: {
inserted(el) {
el.focus();
},
},
},
mixins: [timeMixin],
props: {
show: {
type: Boolean,
default: false,
},
onClose: {
type: Function,
default: () => {},
},
},
data() {
return {
searchTerm: '',
hasSearched: false,
};
},
computed: {
...mapGetters({
conversations: 'conversationSearch/getConversations',
uiFlags: 'conversationSearch/getUIFlags',
accountId: 'getCurrentAccountId',
}),
},
watch: {
searchTerm(newValue) {
if (this.typingTimer) {
clearTimeout(this.typingTimer);
}
this.typingTimer = setTimeout(() => {
this.hasSearched = true;
this.$store.dispatch('conversationSearch/get', { q: newValue });
}, 1000);
},
},
mounted() {
this.$store.dispatch('conversationSearch/get', { q: '' });
},
methods: {
prepareContent(content = '') {
return content.replace(
new RegExp(`(${this.searchTerm})`, 'ig'),
'<span class="searchkey--highlight">$1</span>'
);
},
onClick(conversation) {
const path = conversationUrl({
accountId: this.accountId,
id: conversation.id,
});
window.location = frontendURL(path);
},
},
};
</script>
<style lang="scss">
.search--container {
font-size: var(--font-size-default);
padding: var(--space-normal) var(--space-large);
}
.search-results--container {
max-height: 300px;
overflow: scroll;
}
.searchkey--highlight {
background: var(--w-500);
color: var(--white);
}
.search--activity-message {
color: var(--s-800);
text-align: center;
}
.search--messages {
border-bottom: 1px solid var(--b-100);
color: var(--color-body);
cursor: pointer;
font-size: var(--font-size-small);
line-height: 1.5;
padding: var(--space-normal);
text-align: left;
width: 100%;
&:hover {
background: var(--w-50);
}
}
.message-search--modal .modal-container {
width: 800px;
min-height: 460px;
}
.search--messages__metadata {
display: flex;
justify-content: space-between;
margin-bottom: var(--space-small);
color: var(--s-500);
}
</style>