mirror of
https://github.com/revoltchat/revite.git
synced 2024-12-26 15:32:11 -05:00
Add search back.
This commit is contained in:
parent
f19e334d56
commit
7a80c0edb5
7 changed files with 219 additions and 114 deletions
|
@ -33,6 +33,7 @@ interface Props {
|
||||||
contrast?: boolean;
|
contrast?: boolean;
|
||||||
content?: Children;
|
content?: Children;
|
||||||
head?: boolean;
|
head?: boolean;
|
||||||
|
hideReply?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Message = observer(
|
const Message = observer(
|
||||||
|
@ -44,6 +45,7 @@ const Message = observer(
|
||||||
content: replacement,
|
content: replacement,
|
||||||
head: preferHead,
|
head: preferHead,
|
||||||
queued,
|
queued,
|
||||||
|
hideReply,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const client = useClient();
|
const client = useClient();
|
||||||
const user = message.author;
|
const user = message.author;
|
||||||
|
@ -72,23 +74,26 @@ const Message = observer(
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id={message._id}>
|
<div id={message._id}>
|
||||||
{message.reply_ids?.map((message_id, index) => (
|
{!hideReply &&
|
||||||
<MessageReply
|
message.reply_ids?.map((message_id, index) => (
|
||||||
key={message_id}
|
<MessageReply
|
||||||
index={index}
|
key={message_id}
|
||||||
id={message_id}
|
index={index}
|
||||||
channel={message.channel!}
|
id={message_id}
|
||||||
/>
|
channel={message.channel!}
|
||||||
))}
|
/>
|
||||||
|
))}
|
||||||
<MessageBase
|
<MessageBase
|
||||||
highlight={highlight}
|
highlight={highlight}
|
||||||
head={
|
head={
|
||||||
(head &&
|
hideReply
|
||||||
!(
|
? false
|
||||||
message.reply_ids &&
|
: (head &&
|
||||||
message.reply_ids.length > 0
|
!(
|
||||||
)) ??
|
message.reply_ids &&
|
||||||
false
|
message.reply_ids.length > 0
|
||||||
|
)) ??
|
||||||
|
false
|
||||||
}
|
}
|
||||||
contrast={contrast}
|
contrast={contrast}
|
||||||
sending={typeof queued !== "undefined"}
|
sending={typeof queued !== "undefined"}
|
||||||
|
|
|
@ -1,9 +1,31 @@
|
||||||
import { Route, Switch } from "react-router";
|
import { Route, Switch } from "react-router";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
|
import { internalSubscribe } from "../../lib/eventEmitter";
|
||||||
|
|
||||||
import SidebarBase from "./SidebarBase";
|
import SidebarBase from "./SidebarBase";
|
||||||
import MemberSidebar from "./right/MemberSidebar";
|
import MemberSidebar from "./right/MemberSidebar";
|
||||||
|
import { SearchSidebar } from "./right/Search";
|
||||||
|
|
||||||
export default function RightSidebar() {
|
export default function RightSidebar() {
|
||||||
|
const [sidebar, setSidebar] = useState<"search" | undefined>();
|
||||||
|
const close = () => setSidebar(undefined);
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() =>
|
||||||
|
internalSubscribe(
|
||||||
|
"RightSidebar",
|
||||||
|
"open",
|
||||||
|
setSidebar as (...args: unknown[]) => void,
|
||||||
|
),
|
||||||
|
[setSidebar],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (sidebar === "search") {
|
||||||
|
return <SearchSidebar close={close} />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarBase>
|
<SidebarBase>
|
||||||
<Switch>
|
<Switch>
|
||||||
|
|
|
@ -16,10 +16,10 @@ import {
|
||||||
import { GenericSidebarBase } from "../SidebarBase";
|
import { GenericSidebarBase } from "../SidebarBase";
|
||||||
import MemberList, { MemberListGroup } from "./MemberList";
|
import MemberList, { MemberListGroup } from "./MemberList";
|
||||||
|
|
||||||
export default function MemberSidebar({ channel: obj }: { channel?: Channel }) {
|
export default function MemberSidebar() {
|
||||||
const { channel: channel_id } = useParams<{ channel: string }>();
|
const channel = useClient().channels.get(
|
||||||
const client = useClient();
|
useParams<{ channel: string }>().channel,
|
||||||
const channel = obj ?? client.channels.get(channel_id);
|
);
|
||||||
|
|
||||||
switch (channel?.channel_type) {
|
switch (channel?.channel_type) {
|
||||||
case "Group":
|
case "Group":
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
// this is the search code
|
|
||||||
|
|
||||||
function Search({ channel }: { channel: Channel }) {
|
|
||||||
if (!getState().experiments.enabled?.includes("search")) return null;
|
|
||||||
|
|
||||||
type Sort = "Relevance" | "Latest" | "Oldest";
|
|
||||||
const [sort, setSort] = useState<Sort>("Relevance");
|
|
||||||
|
|
||||||
const [query, setV] = useState("");
|
|
||||||
const [results, setResults] = useState<Message[]>([]);
|
|
||||||
|
|
||||||
async function search() {
|
|
||||||
const data = await channel.searchWithUsers({ query, sort });
|
|
||||||
setResults(data.messages);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CollapsibleSection
|
|
||||||
sticky
|
|
||||||
id="search"
|
|
||||||
defaultValue={false}
|
|
||||||
summary={
|
|
||||||
<>
|
|
||||||
<Text id="app.main.channel.search.title" /> (BETA)
|
|
||||||
</>
|
|
||||||
}>
|
|
||||||
<div style={{ display: "flex" }}>
|
|
||||||
{["Relevance", "Latest", "Oldest"].map((key) => (
|
|
||||||
<Button
|
|
||||||
key={key}
|
|
||||||
style={{ flex: 1, minWidth: 0 }}
|
|
||||||
compact
|
|
||||||
error={sort === key}
|
|
||||||
onClick={() => setSort(key as Sort)}>
|
|
||||||
<Text
|
|
||||||
id={`app.main.channel.search.sort.${key.toLowerCase()}`}
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<InputBox
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
onKeyDown={(e) => e.key === "Enter" && search()}
|
|
||||||
value={query}
|
|
||||||
onChange={(e) => setV(e.currentTarget.value)}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
gap: "4px",
|
|
||||||
marginTop: "8px",
|
|
||||||
}}>
|
|
||||||
{results.map((message) => {
|
|
||||||
let href = "";
|
|
||||||
if (channel?.channel_type === "TextChannel") {
|
|
||||||
href += `/server/${channel.server_id}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
href += `/channel/${message.channel_id}/${message._id}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link to={href} key={message._id}>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
margin: "2px",
|
|
||||||
padding: "6px",
|
|
||||||
background: "var(--primary-background)",
|
|
||||||
}}>
|
|
||||||
<b>@{message.author?.username}</b>
|
|
||||||
<br />
|
|
||||||
{message.content}
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</CollapsibleSection>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,12 +1,154 @@
|
||||||
/*export const SearchSidebar = observer(
|
import { Link, useParams } from "react-router-dom";
|
||||||
({ channel }: { channel: Channel }) => {
|
import { Message as MessageI } from "revolt.js/dist/maps/Messages";
|
||||||
const keys = [...channel.recipient_ids!];
|
import styled from "styled-components";
|
||||||
const entries = useEntries(channel, keys);
|
|
||||||
|
|
||||||
return (
|
import { Text } from "preact-i18n";
|
||||||
<GenericSidebarBase>
|
import { useEffect, useState } from "preact/hooks";
|
||||||
<MemberList entries={entries} context={channel} />
|
|
||||||
</GenericSidebarBase>
|
import { useClient } from "../../../context/revoltjs/RevoltClient";
|
||||||
);
|
|
||||||
},
|
import Message from "../../common/messaging/Message";
|
||||||
);*/
|
import Button from "../../ui/Button";
|
||||||
|
import InputBox from "../../ui/InputBox";
|
||||||
|
import Overline from "../../ui/Overline";
|
||||||
|
import Preloader from "../../ui/Preloader";
|
||||||
|
|
||||||
|
import { GenericSidebarBase } from "../SidebarBase";
|
||||||
|
|
||||||
|
type SearchState =
|
||||||
|
| {
|
||||||
|
type: "waiting";
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "loading";
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "results";
|
||||||
|
results: MessageI[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const SearchBase = styled.div`
|
||||||
|
padding: 6px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list {
|
||||||
|
gap: 4px;
|
||||||
|
margin: 8px 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
margin: 2px;
|
||||||
|
padding: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
background: var(--primary-background);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
> * {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort {
|
||||||
|
gap: 4px;
|
||||||
|
margin: 6px 0;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
close: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SearchSidebar({ close }: Props) {
|
||||||
|
const channel = useClient().channels.get(
|
||||||
|
useParams<{ channel: string }>().channel,
|
||||||
|
)!;
|
||||||
|
|
||||||
|
type Sort = "Relevance" | "Latest" | "Oldest";
|
||||||
|
const [sort, setSort] = useState<Sort>("Latest");
|
||||||
|
const [query, setQuery] = useState("");
|
||||||
|
|
||||||
|
const [state, setState] = useState<SearchState>({ type: "waiting" });
|
||||||
|
|
||||||
|
async function search() {
|
||||||
|
if (!query) return;
|
||||||
|
setState({ type: "loading" });
|
||||||
|
const data = await channel.searchWithUsers({ query, sort });
|
||||||
|
setState({ type: "results", results: data.messages });
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
search();
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, [sort]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GenericSidebarBase>
|
||||||
|
<SearchBase>
|
||||||
|
<Overline type="error" onClick={close} block>
|
||||||
|
go back to members
|
||||||
|
</Overline>
|
||||||
|
<Overline type="subtle" block>
|
||||||
|
<Text id="app.main.channel.search.title" />
|
||||||
|
</Overline>
|
||||||
|
<InputBox
|
||||||
|
value={query}
|
||||||
|
onKeyDown={(e) => e.key === "Enter" && search()}
|
||||||
|
onChange={(e) => setQuery(e.currentTarget.value)}
|
||||||
|
/>
|
||||||
|
<div class="sort">
|
||||||
|
{["Latest", "Oldest", "Relevance"].map((key) => (
|
||||||
|
<Button
|
||||||
|
key={key}
|
||||||
|
compact
|
||||||
|
error={sort === key}
|
||||||
|
onClick={() => setSort(key as Sort)}>
|
||||||
|
<Text
|
||||||
|
id={`app.main.channel.search.sort.${key.toLowerCase()}`}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{state.type === "loading" && <Preloader type="ring" />}
|
||||||
|
{state.type === "results" && (
|
||||||
|
<div class="list">
|
||||||
|
{state.results.map((message) => {
|
||||||
|
let href = "";
|
||||||
|
if (channel?.channel_type === "TextChannel") {
|
||||||
|
href += `/server/${channel.server_id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
href += `/channel/${message.channel_id}/${message._id}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link to={href} key={message._id}>
|
||||||
|
<div class="message">
|
||||||
|
<Message
|
||||||
|
message={message}
|
||||||
|
head
|
||||||
|
hideReply
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</SearchBase>
|
||||||
|
</GenericSidebarBase>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ export function internalEmit(ns: string, event: string, ...args: unknown[]) {
|
||||||
// Event structure: namespace/event
|
// Event structure: namespace/event
|
||||||
|
|
||||||
/// Event List
|
/// Event List
|
||||||
|
// - RightSidebar/open
|
||||||
// - MessageArea/jump_to_bottom
|
// - MessageArea/jump_to_bottom
|
||||||
// - MessageRenderer/edit_last
|
// - MessageRenderer/edit_last
|
||||||
// - MessageRenderer/edit_message
|
// - MessageRenderer/edit_message
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
/* eslint-disable react-hooks/rules-of-hooks */
|
/* eslint-disable react-hooks/rules-of-hooks */
|
||||||
|
import { Search } from "@styled-icons/boxicons-regular";
|
||||||
import {
|
import {
|
||||||
UserPlus,
|
UserPlus,
|
||||||
Cog,
|
Cog,
|
||||||
|
@ -8,6 +9,7 @@ import {
|
||||||
} from "@styled-icons/boxicons-solid";
|
} from "@styled-icons/boxicons-solid";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
|
|
||||||
|
import { internalEmit } from "../../../lib/eventEmitter";
|
||||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||||
import { voiceState, VoiceStatus } from "../../../lib/vortex/VoiceState";
|
import { voiceState, VoiceStatus } from "../../../lib/vortex/VoiceState";
|
||||||
|
|
||||||
|
@ -25,13 +27,17 @@ export default function HeaderActions({
|
||||||
const { openScreen } = useIntermediate();
|
const { openScreen } = useIntermediate();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
|
function openRightSidebar() {
|
||||||
|
const panels = document.querySelector("#app > div > div");
|
||||||
|
panels?.scrollTo({
|
||||||
|
behavior: "smooth",
|
||||||
|
left: panels.clientWidth * 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function openSidebar() {
|
function openSidebar() {
|
||||||
if (isTouchscreenDevice) {
|
if (isTouchscreenDevice) {
|
||||||
const panels = document.querySelector("#app > div > div");
|
openRightSidebar();
|
||||||
panels?.scrollTo({
|
|
||||||
behavior: "smooth",
|
|
||||||
left: panels.clientWidth * 3,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
toggleSidebar?.();
|
toggleSidebar?.();
|
||||||
}
|
}
|
||||||
|
@ -65,6 +71,15 @@ export default function HeaderActions({
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<VoiceActions channel={channel} />
|
<VoiceActions channel={channel} />
|
||||||
|
{channel.channel_type !== "VoiceChannel" && (
|
||||||
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
internalEmit("RightSidebar", "open", "search");
|
||||||
|
openRightSidebar();
|
||||||
|
}}>
|
||||||
|
<Search size={25} />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
{(channel.channel_type === "Group" ||
|
{(channel.channel_type === "Group" ||
|
||||||
channel.channel_type === "TextChannel") && (
|
channel.channel_type === "TextChannel") && (
|
||||||
<IconButton onClick={openSidebar}>
|
<IconButton onClick={openSidebar}>
|
||||||
|
|
Loading…
Reference in a new issue