Add search back.

This commit is contained in:
Paul 2021-08-09 17:34:25 +01:00
parent f19e334d56
commit 7a80c0edb5
7 changed files with 219 additions and 114 deletions

View file

@ -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"}

View file

@ -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>

View file

@ -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":

View file

@ -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>
);
}

View file

@ -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>
);
}

View file

@ -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

View file

@ -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}>