hardlounge/client/components/Windows/SearchResults.vue

238 lines
5.3 KiB
Vue

<template>
<div id="chat-container" class="window">
<div
id="chat"
:class="{
'colored-nicks': $store.state.settings.coloredNicks,
'show-seconds': $store.state.settings.showSeconds,
}"
>
<div
class="chat-view"
data-type="search-results"
aria-label="Search results"
role="tabpanel"
>
<div class="header">
<SidebarToggle />
<span class="title"
>Search results for "{{ $route.params.term }}" in
{{ $route.params.target }}</span
>
<span class="topic"></span>
<MessageSearchForm :network="network" :channel="channel" />
</div>
<div class="chat-content">
<div ref="chat" class="chat" tabindex="-1">
<div v-show="moreResultsAvailable" class="show-more">
<button
ref="loadMoreButton"
:disabled="
$store.state.messageSearchInProgress ||
!$store.state.isConnected
"
class="btn"
@click="onShowMoreClick"
>
<span v-if="$store.state.messageSearchInProgress">Loading…</span>
<span v-else>Show older messages</span>
</button>
</div>
<div
v-if="$store.state.messageSearchInProgress && !offset"
class="search-status"
>
Searching…
</div>
<div v-else-if="!messages.length && !offset" class="search-status">
No results found.
</div>
<div
v-else
class="messages"
role="log"
aria-live="polite"
aria-relevant="additions"
>
<template v-for="(message, id) in messages">
<div :key="message.id" class="result" @:click="jump(message, id)">
<DateMarker
v-if="shouldDisplayDateMarker(message, id)"
:key="message.date"
:message="message"
/>
<Message
:key="message.id"
:channel="channel"
:network="network"
:message="message"
:data-id="message.id"
/>
</div>
</template>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import socket from "../../js/socket";
import SidebarToggle from "../SidebarToggle.vue";
import Message from "../Message.vue";
import MessageSearchForm from "../MessageSearchForm.vue";
import DateMarker from "../DateMarker.vue";
export default {
name: "SearchResults",
components: {
SidebarToggle,
Message,
DateMarker,
MessageSearchForm,
},
data() {
return {
offset: 0,
moreResultsAvailable: false,
oldScrollTop: 0,
oldChatHeight: 0,
};
},
computed: {
search() {
return this.$store.state.messageSearchResults;
},
messages() {
if (!this.search) {
return [];
}
return this.search.results.slice().reverse();
},
chan() {
if (!this.search) {
return;
}
const chan = this.$store.getters.findChannelOnNetwork(
this.search.networkUuid,
this.search.target
);
return chan;
},
network() {
if (!this.chan) {
return null;
}
return this.chan.network;
},
channel() {
if (!this.chan) {
return null;
}
return this.chan.channel;
},
},
watch: {
"$route.params.uuid"() {
this.doSearch();
},
"$route.params.target"() {
this.doSearch();
},
"$route.params.term"() {
this.doSearch();
},
messages() {
this.moreResultsAvailable = this.messages.length && !(this.messages.length % 100);
if (!this.offset) {
this.jumpToBottom();
} else {
this.$nextTick(() => {
const currentChatHeight = this.$refs.chat.scrollHeight;
this.$refs.chat.scrollTop =
this.oldScrollTop + currentChatHeight - this.oldChatHeight;
});
}
},
chan() {
this.setActiveChannel();
},
},
mounted() {
this.doSearch();
},
methods: {
setActiveChannel() {
this.$store.commit("activeChannel", this.chan);
},
shouldDisplayDateMarker(message, id) {
const previousMessage = this.messages[id - 1];
if (!previousMessage) {
return true;
}
return new Date(previousMessage.time).getDay() !== new Date(message.time).getDay();
},
doSearch() {
this.offset = 0;
this.$store.commit("messageSearchInProgress", true);
if (!this.offset) {
this.$store.commit("messageSearchResults", null); // Only reset if not getting offset
}
socket.emit("search", {
networkUuid: this.$route.params.uuid,
channelName: this.$route.params.target,
searchTerm: this.$route.params.term,
offset: this.offset,
});
},
onShowMoreClick() {
this.offset += 100;
this.$store.commit("messageSearchInProgress", true);
this.oldScrollTop = this.$refs.chat.scrollTop;
this.oldChatHeight = this.$refs.chat.scrollHeight;
socket.emit("search", {
networkUuid: this.$route.params.uuid,
channelName: this.$route.params.target,
searchTerm: this.$route.params.term,
offset: this.offset + 1,
});
},
jumpToBottom() {
this.$nextTick(() => {
const el = this.$refs.chat;
el.scrollTop = el.scrollHeight;
});
},
jump(message, id) {
// TODO: Implement jumping to messages!
// This is difficult because it means client will need to handle a potentially nonlinear message set
// (loading IntersectionObserver both before AND after the messages)
this.$router.push({
name: "MessageList",
params: {
id: this.chan.id,
},
query: {
focused: id,
},
});
},
},
};
</script>