forked from abner/for-legacy-web
Make the linter happy.
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import { useStore } from "react-redux";
|
||||
import { SYSTEM_USER_ID } from "revolt.js";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
@@ -143,14 +142,16 @@ export function useAutoComplete(
|
||||
) as User[];
|
||||
break;
|
||||
case "TextChannel":
|
||||
const server = channel.server_id;
|
||||
users = [...client.members.keys()]
|
||||
.map((x) => JSON.parse(x))
|
||||
.filter((x) => x.server === server)
|
||||
.map((x) => client.users.get(x.user))
|
||||
.filter(
|
||||
(x) => typeof x !== "undefined",
|
||||
) as User[];
|
||||
{
|
||||
const server = channel.server_id;
|
||||
users = [...client.members.keys()]
|
||||
.map((x) => JSON.parse(x))
|
||||
.filter((x) => x.server === server)
|
||||
.map((x) => client.users.get(x.user))
|
||||
.filter(
|
||||
(x) => typeof x !== "undefined",
|
||||
) as User[];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@@ -304,7 +305,7 @@ export function useAutoComplete(
|
||||
|
||||
function onKeyUp(e: KeyboardEvent) {
|
||||
if (e.currentTarget !== null) {
|
||||
// @ts-expect-error
|
||||
// @ts-expect-error Type mis-match.
|
||||
onChange(e);
|
||||
}
|
||||
}
|
||||
@@ -391,6 +392,7 @@ export default function AutoComplete({
|
||||
{state.type === "emoji" &&
|
||||
state.matches.map((match, i) => (
|
||||
<button
|
||||
key={match}
|
||||
className={i === state.selected ? "active" : ""}
|
||||
onMouseEnter={() =>
|
||||
(i !== state.selected || !state.within) &&
|
||||
@@ -422,6 +424,7 @@ export default function AutoComplete({
|
||||
{state.type === "user" &&
|
||||
state.matches.map((match, i) => (
|
||||
<button
|
||||
key={match}
|
||||
className={i === state.selected ? "active" : ""}
|
||||
onMouseEnter={() =>
|
||||
(i !== state.selected || !state.within) &&
|
||||
@@ -446,6 +449,7 @@ export default function AutoComplete({
|
||||
{state.type === "channel" &&
|
||||
state.matches.map((match, i) => (
|
||||
<button
|
||||
key={match}
|
||||
className={i === state.selected ? "active" : ""}
|
||||
onMouseEnter={() =>
|
||||
(i !== state.selected || !state.within) &&
|
||||
|
||||
@@ -15,7 +15,11 @@ interface Props extends IconBaseProps<Channel> {
|
||||
|
||||
export default observer(
|
||||
(
|
||||
props: Props & Omit<JSX.HTMLAttributes<HTMLImageElement>, keyof Props>,
|
||||
props: Props &
|
||||
Omit<
|
||||
JSX.HTMLAttributes<HTMLImageElement>,
|
||||
keyof Props | "children" | "as"
|
||||
>,
|
||||
) => {
|
||||
const client = useContext(AppContext);
|
||||
|
||||
@@ -25,8 +29,6 @@ export default observer(
|
||||
attachment,
|
||||
isServerChannel: server,
|
||||
animate,
|
||||
children,
|
||||
as,
|
||||
...imgProps
|
||||
} = props;
|
||||
const iconURL = client.generateFileURL(
|
||||
|
||||
@@ -22,7 +22,7 @@ export function LocaleSelector(props: Props) {
|
||||
{Object.keys(Languages).map((x) => {
|
||||
const l = Languages[x as keyof typeof Languages];
|
||||
return (
|
||||
<option value={x}>
|
||||
<option value={x} key={x}>
|
||||
{l.emoji} {l.display}
|
||||
</option>
|
||||
);
|
||||
|
||||
@@ -22,23 +22,19 @@ const ServerText = styled.div`
|
||||
background: var(--primary-background);
|
||||
`;
|
||||
|
||||
const fallback = "/assets/group.png";
|
||||
// const fallback = "/assets/group.png";
|
||||
export default observer(
|
||||
(
|
||||
props: Props & Omit<JSX.HTMLAttributes<HTMLImageElement>, keyof Props>,
|
||||
props: Props &
|
||||
Omit<
|
||||
JSX.HTMLAttributes<HTMLImageElement>,
|
||||
keyof Props | "children" | "as"
|
||||
>,
|
||||
) => {
|
||||
const client = useContext(AppContext);
|
||||
|
||||
const {
|
||||
target,
|
||||
attachment,
|
||||
size,
|
||||
animate,
|
||||
server_name,
|
||||
children,
|
||||
as,
|
||||
...imgProps
|
||||
} = props;
|
||||
const { target, attachment, size, animate, server_name, ...imgProps } =
|
||||
props;
|
||||
const iconURL = client.generateFileURL(
|
||||
target?.icon ?? attachment,
|
||||
{ max_side: 256 },
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function Tooltip(props: Props) {
|
||||
return (
|
||||
<Tippy content={content} {...tippyProps}>
|
||||
{/*
|
||||
// @ts-expect-error */}
|
||||
// @ts-expect-error Type mis-match. */}
|
||||
<div style={`display: flex;`}>{children}</div>
|
||||
</Tippy>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Download } from "@styled-icons/boxicons-regular";
|
||||
import { CloudDownload } from "@styled-icons/boxicons-regular";
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { Download, CloudDownload } from "@styled-icons/boxicons-regular";
|
||||
|
||||
import { useContext, useEffect, useState } from "preact/hooks";
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ const Message = observer(
|
||||
? (attachContextMenu("Menu", {
|
||||
user: message.author_id,
|
||||
contextualChannel: message.channel_id,
|
||||
// eslint-disable-next-line
|
||||
}) as any)
|
||||
: undefined;
|
||||
|
||||
@@ -73,6 +74,7 @@ const Message = observer(
|
||||
<div id={message._id}>
|
||||
{message.reply_ids?.map((message_id, index) => (
|
||||
<MessageReply
|
||||
key={message_id}
|
||||
index={index}
|
||||
id={message_id}
|
||||
channel={message.channel!}
|
||||
|
||||
@@ -6,7 +6,6 @@ import { decodeTime } from "ulid";
|
||||
import { Text } from "preact-i18n";
|
||||
|
||||
import { useDictionary } from "../../../lib/i18n";
|
||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||
|
||||
import { dayjs } from "../../../context/Locale";
|
||||
|
||||
|
||||
@@ -141,22 +141,25 @@ export default observer(({ channel }: Props) => {
|
||||
);
|
||||
}
|
||||
|
||||
function setMessage(content?: string) {
|
||||
setDraft(content ?? "");
|
||||
const setMessage = useCallback(
|
||||
(content?: string) => {
|
||||
setDraft(content ?? "");
|
||||
|
||||
if (content) {
|
||||
dispatch({
|
||||
type: "SET_DRAFT",
|
||||
channel: channel._id,
|
||||
content,
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: "CLEAR_DRAFT",
|
||||
channel: channel._id,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (content) {
|
||||
dispatch({
|
||||
type: "SET_DRAFT",
|
||||
channel: channel._id,
|
||||
content,
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: "CLEAR_DRAFT",
|
||||
channel: channel._id,
|
||||
});
|
||||
}
|
||||
},
|
||||
[channel._id],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
function append(content: string, action: "quote" | "mention") {
|
||||
@@ -175,8 +178,12 @@ export default observer(({ channel }: Props) => {
|
||||
}
|
||||
}
|
||||
|
||||
return internalSubscribe("MessageBox", "append", append);
|
||||
}, [draft]);
|
||||
return internalSubscribe(
|
||||
"MessageBox",
|
||||
"append",
|
||||
append as (...args: unknown[]) => void,
|
||||
);
|
||||
}, [draft, setMessage]);
|
||||
|
||||
async function send() {
|
||||
if (uploadState.type === "uploading" || uploadState.type === "sending")
|
||||
@@ -344,9 +351,11 @@ export default observer(({ channel }: Props) => {
|
||||
}
|
||||
}
|
||||
|
||||
const debouncedStopTyping = useCallback(debounce(stopTyping, 1000), [
|
||||
channel._id,
|
||||
]);
|
||||
// eslint-disable-next-line
|
||||
const debouncedStopTyping = useCallback(
|
||||
debounce(stopTyping as (...args: unknown[]) => void, 1000),
|
||||
[channel._id],
|
||||
);
|
||||
const {
|
||||
onChange,
|
||||
onKeyUp,
|
||||
@@ -478,7 +487,7 @@ export default observer(({ channel }: Props) => {
|
||||
: channel.channel_type === "SavedMessages"
|
||||
? translate("app.main.channel.message_saved")
|
||||
: translate("app.main.channel.message_where", {
|
||||
channel_name: channel.name,
|
||||
channel_name: channel.name ?? undefined,
|
||||
})
|
||||
}
|
||||
disabled={
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Reply } from "@styled-icons/boxicons-regular";
|
||||
import { File } from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useHistory } from "react-router-dom";
|
||||
@@ -13,8 +12,6 @@ import { useLayoutEffect, useState } from "preact/hooks";
|
||||
|
||||
import { useRenderState } from "../../../../lib/renderer/Singleton";
|
||||
|
||||
import { useClient } from "../../../../context/revoltjs/RevoltClient";
|
||||
|
||||
import Markdown from "../../../markdown/Markdown";
|
||||
import UserShort from "../../user/UserShort";
|
||||
import { SystemMessage } from "../SystemMessage";
|
||||
@@ -136,10 +133,6 @@ export const ReplyBase = styled.div<{
|
||||
`}
|
||||
`;
|
||||
|
||||
const Arrow = styled.div`
|
||||
|
||||
`;
|
||||
|
||||
export const MessageReply = observer(({ index, channel, id }: Props) => {
|
||||
const view = useRenderState(channel._id);
|
||||
if (view?.type !== "RENDER") return null;
|
||||
@@ -155,7 +148,7 @@ export const MessageReply = observer(({ index, channel, id }: Props) => {
|
||||
} else {
|
||||
channel.fetchMessage(id).then(setMessage);
|
||||
}
|
||||
}, [view.messages]);
|
||||
}, [id, channel, view.messages]);
|
||||
|
||||
if (!message) {
|
||||
return (
|
||||
@@ -204,9 +197,12 @@ export const MessageReply = observer(({ index, channel, id }: Props) => {
|
||||
{message.attachments && (
|
||||
<>
|
||||
<File size={16} />
|
||||
<em>{message.attachments.length > 1 ?
|
||||
<Text id="app.main.channel.misc.sent_multiple_files" /> :
|
||||
<Text id="app.main.channel.misc.sent_file" /> }
|
||||
<em>
|
||||
{message.attachments.length > 1 ? (
|
||||
<Text id="app.main.channel.misc.sent_multiple_files" />
|
||||
) : (
|
||||
<Text id="app.main.channel.misc.sent_file" />
|
||||
)}
|
||||
</em>
|
||||
</>
|
||||
)}
|
||||
@@ -223,4 +219,4 @@ export const MessageReply = observer(({ index, channel, id }: Props) => {
|
||||
)}
|
||||
</ReplyBase>
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -60,7 +60,7 @@ export default function TextFile({ attachment }: Props) {
|
||||
setLoading(false);
|
||||
});
|
||||
}
|
||||
}, [content, loading, status]);
|
||||
}, [content, loading, status, attachment._id, attachment.size, url]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { XCircle, Plus, Share, X, File } from "@styled-icons/boxicons-regular";
|
||||
import styled from "styled-components";
|
||||
|
||||
@@ -186,7 +187,9 @@ export default function FilePreview({ state, addFile, removeFile }: Props) {
|
||||
<Container>
|
||||
<Carousel>
|
||||
{state.files.map((file, index) => (
|
||||
<>
|
||||
// @ts-expect-error brokey
|
||||
// eslint-disable-next-line react/jsx-no-undef
|
||||
<Fragment key={file.name}>
|
||||
{index === CAN_UPLOAD_AT_ONCE && <Divider />}
|
||||
<FileEntry
|
||||
index={index}
|
||||
@@ -198,7 +201,7 @@ export default function FilePreview({ state, addFile, removeFile }: Props) {
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</>
|
||||
</Fragment>
|
||||
))}
|
||||
{state.type === "attached" && (
|
||||
<EmptyEntry onClick={addFile}>
|
||||
|
||||
@@ -83,9 +83,9 @@ export default observer(({ channel, replies, setReplies }: Props) => {
|
||||
(id) =>
|
||||
replies.length < MAX_REPLIES &&
|
||||
!replies.find((x) => x.id === id) &&
|
||||
setReplies([...replies, { id, mention: false }]),
|
||||
setReplies([...replies, { id: id as string, mention: false }]),
|
||||
);
|
||||
}, [replies]);
|
||||
}, [replies, setReplies]);
|
||||
|
||||
const view = useRenderState(channel);
|
||||
if (view?.type !== "RENDER") return null;
|
||||
@@ -116,25 +116,28 @@ export default observer(({ channel, replies, setReplies }: Props) => {
|
||||
<UserShort user={message.author} size={16} />
|
||||
</div>
|
||||
<div class="message">
|
||||
{message.attachments && (
|
||||
{message.attachments && (
|
||||
<>
|
||||
<File size={16} />
|
||||
<em>{message.attachments.length > 1 ?
|
||||
<Text id="app.main.channel.misc.sent_multiple_files" /> :
|
||||
<Text id="app.main.channel.misc.sent_file" /> }
|
||||
<em>
|
||||
{message.attachments.length > 1 ? (
|
||||
<Text id="app.main.channel.misc.sent_multiple_files" />
|
||||
) : (
|
||||
<Text id="app.main.channel.misc.sent_file" />
|
||||
)}
|
||||
</em>
|
||||
</>
|
||||
)}
|
||||
{message.author_id === SYSTEM_USER_ID ? (
|
||||
<SystemMessage message={message} />
|
||||
) : (
|
||||
<Markdown
|
||||
disallowBigEmoji
|
||||
content={(
|
||||
message.content as string
|
||||
).replace(/\n/g, " ")}
|
||||
/>
|
||||
)}
|
||||
)}
|
||||
{message.author_id === SYSTEM_USER_ID ? (
|
||||
<SystemMessage message={message} />
|
||||
) : (
|
||||
<Markdown
|
||||
disallowBigEmoji
|
||||
content={(
|
||||
message.content as string
|
||||
).replace(/\n/g, " ")}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</ReplyBase>
|
||||
<span class="actions">
|
||||
|
||||
@@ -5,10 +5,6 @@ import styled from "styled-components";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
||||
import { connectState } from "../../../../redux/connector";
|
||||
|
||||
import { useClient } from "../../../../context/revoltjs/RevoltClient";
|
||||
|
||||
interface Props {
|
||||
channel: Channel;
|
||||
}
|
||||
@@ -104,6 +100,7 @@ export default observer(({ channel }: Props) => {
|
||||
<div className="avatars">
|
||||
{users.map((user) => (
|
||||
<img
|
||||
key={user!._id}
|
||||
loading="eager"
|
||||
src={user!.generateAvatarURL({ max_side: 256 })}
|
||||
/>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { Embed } from "revolt-api/types/January";
|
||||
|
||||
import styles from "./Embed.module.scss";
|
||||
|
||||
@@ -5,8 +5,7 @@ import { User } from "revolt.js/dist/maps/Users";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { openContextMenu } from "preact-context-menu";
|
||||
import { Text } from "preact-i18n";
|
||||
import { Localizer } from "preact-i18n";
|
||||
import { Text, Localizer } from "preact-i18n";
|
||||
|
||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||
|
||||
|
||||
@@ -52,20 +52,23 @@ const VoiceIndicator = styled.div<{ status: VoiceStatus }>`
|
||||
`;
|
||||
|
||||
export default observer(
|
||||
(props: Props & Omit<JSX.SVGAttributes<SVGSVGElement>, keyof Props>) => {
|
||||
(
|
||||
props: Props &
|
||||
Omit<
|
||||
JSX.SVGAttributes<SVGSVGElement>,
|
||||
keyof Props | "children" | "as"
|
||||
>,
|
||||
) => {
|
||||
const client = useContext(AppContext);
|
||||
|
||||
const {
|
||||
target,
|
||||
attachment,
|
||||
size,
|
||||
voice,
|
||||
status,
|
||||
animate,
|
||||
mask,
|
||||
hover,
|
||||
children,
|
||||
as,
|
||||
...svgProps
|
||||
} = props;
|
||||
const iconURL =
|
||||
|
||||
@@ -31,10 +31,10 @@ export const Username = observer(
|
||||
}
|
||||
|
||||
if (member.roles && member.roles.length > 0) {
|
||||
let srv = client.servers.get(member._id.server);
|
||||
const srv = client.servers.get(member._id.server);
|
||||
if (srv?.roles) {
|
||||
for (let role of member.roles) {
|
||||
let c = srv.roles[role].colour;
|
||||
for (const role of member.roles) {
|
||||
const c = srv.roles[role].colour;
|
||||
if (c) {
|
||||
color = c;
|
||||
continue;
|
||||
|
||||
@@ -9,7 +9,7 @@ export interface MarkdownProps {
|
||||
|
||||
export default function Markdown(props: MarkdownProps) {
|
||||
return (
|
||||
// @ts-expect-error
|
||||
// @ts-expect-error Typings mis-match.
|
||||
<Suspense fallback={props.content}>
|
||||
<Renderer {...props} />
|
||||
</Suspense>
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import MarkdownKatex from "@traptitech/markdown-it-katex";
|
||||
import MarkdownSpoilers from "@traptitech/markdown-it-spoiler";
|
||||
import "katex/dist/katex.min.css";
|
||||
import MarkdownIt from "markdown-it";
|
||||
// @ts-ignore
|
||||
// @ts-expect-error No typings.
|
||||
import MarkdownEmoji from "markdown-it-emoji/dist/markdown-it-emoji-bare";
|
||||
// @ts-ignore
|
||||
// @ts-expect-error No typings.
|
||||
import MarkdownSub from "markdown-it-sub";
|
||||
// @ts-ignore
|
||||
// @ts-expect-error No typings.
|
||||
import MarkdownSup from "markdown-it-sup";
|
||||
import Prism from "prismjs";
|
||||
import "prismjs/themes/prism-tomorrow.css";
|
||||
import { RE_MENTIONS } from "revolt.js";
|
||||
|
||||
import styles from "./Markdown.module.scss";
|
||||
import { useCallback, useContext, useRef } from "preact/hooks";
|
||||
import { useCallback, useContext } from "preact/hooks";
|
||||
|
||||
import { internalEmit } from "../../lib/eventEmitter";
|
||||
|
||||
@@ -95,8 +96,8 @@ export default function Renderer({ content, disallowBigEmoji }: MarkdownProps) {
|
||||
// We replace the message with the mention at the time of render.
|
||||
// We don't care if the mention changes.
|
||||
const newContent = content
|
||||
.replace(RE_MENTIONS, (sub: string, ...args: any[]) => {
|
||||
const id = args[0],
|
||||
.replace(RE_MENTIONS, (sub: string, ...args: unknown[]) => {
|
||||
const id = args[0] as string,
|
||||
user = client.users.get(id);
|
||||
|
||||
if (user) {
|
||||
@@ -105,8 +106,8 @@ export default function Renderer({ content, disallowBigEmoji }: MarkdownProps) {
|
||||
|
||||
return sub;
|
||||
})
|
||||
.replace(RE_CHANNELS, (sub: string, ...args: any[]) => {
|
||||
const id = args[0],
|
||||
.replace(RE_CHANNELS, (sub: string, ...args: unknown[]) => {
|
||||
const id = args[0] as string,
|
||||
channel = client.channels.get(id);
|
||||
|
||||
if (channel?.channel_type === "TextChannel") {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Wrench, Microphone, VolumeFull } from "@styled-icons/boxicons-solid";
|
||||
import { Wrench } from "@styled-icons/boxicons-solid";
|
||||
import styled from "styled-components";
|
||||
|
||||
import Tooltip from "../common/Tooltip";
|
||||
import UpdateIndicator from "../common/UpdateIndicator";
|
||||
|
||||
const TitlebarBase = styled.div`
|
||||
@@ -16,11 +15,12 @@ const TitlebarBase = styled.div`
|
||||
margin-top: 10px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
.quick {
|
||||
color: var(--secondary-foreground);
|
||||
|
||||
> div, > div > div {
|
||||
> div,
|
||||
> div > div {
|
||||
width: var(--titlebar-height) !important;
|
||||
}
|
||||
|
||||
@@ -99,7 +99,9 @@ export function Titlebar() {
|
||||
stroke-width="1"
|
||||
/>
|
||||
</svg>
|
||||
{window.native.getConfig().build === "dev" && <Wrench size="12.5"/>}
|
||||
{window.native.getConfig().build === "dev" && (
|
||||
<Wrench size="12.5" />
|
||||
)}
|
||||
</div>
|
||||
{/*<div class="actions quick">
|
||||
<Tooltip
|
||||
@@ -121,13 +123,50 @@ export function Titlebar() {
|
||||
<UpdateIndicator style="titlebar" />
|
||||
<div class="actions">
|
||||
<div onClick={window.native.min}>
|
||||
<svg aria-hidden="false" width="12" height="12" viewBox="0 0 12 12"><rect fill="currentColor" width="10" height="1" x="1" y="6"></rect></svg>
|
||||
<svg
|
||||
aria-hidden="false"
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12">
|
||||
<rect
|
||||
fill="currentColor"
|
||||
width="10"
|
||||
height="1"
|
||||
x="1"
|
||||
y="6"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div onClick={window.native.max}>
|
||||
<svg aria-hidden="false" width="12" height="12" viewBox="0 0 12 12"><rect width="9" height="9" x="1.5" y="1.5" fill="none" stroke="currentColor"></rect></svg>
|
||||
<svg
|
||||
aria-hidden="false"
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12">
|
||||
<rect
|
||||
width="9"
|
||||
height="9"
|
||||
x="1.5"
|
||||
y="1.5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div onClick={window.native.close} class="error">
|
||||
<svg aria-hidden="false" width="12" height="12" viewBox="0 0 12 12"><polygon fill="currentColor" stroke-width="1" fill-rule="evenodd" points="11 1.576 6.583 6 11 10.424 10.424 11 6 6.583 1.576 11 1 10.424 5.417 6 1 1.576 1.576 1 6 5.417 10.424 1" style="stroke:currentColor;stroke-width:0.4"></polygon></svg>
|
||||
<svg
|
||||
aria-hidden="false"
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12">
|
||||
<polygon
|
||||
fill="currentColor"
|
||||
stroke-width="1"
|
||||
fill-rule="evenodd"
|
||||
points="11 1.576 6.583 6 11 10.424 10.424 11 6 6.583 1.576 11 1 10.424 5.417 6 1 1.576 1.576 1 6 5.417 10.424 1"
|
||||
style="stroke:currentColor;stroke-width:0.4"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</TitlebarBase>
|
||||
|
||||
@@ -148,6 +148,7 @@ const HomeSidebar = observer((props: Props) => {
|
||||
|
||||
return (
|
||||
<ConditionalLink
|
||||
key={x.channel._id}
|
||||
active={x.channel._id === channel}
|
||||
to={`/channel/${x.channel._id}`}>
|
||||
<ChannelButton
|
||||
|
||||
@@ -279,6 +279,7 @@ export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
|
||||
|
||||
return (
|
||||
<ConditionalLink
|
||||
key={entry.server._id}
|
||||
active={active}
|
||||
to={`/server/${entry.server._id}${
|
||||
id ? `/channel/${id}` : ""
|
||||
|
||||
@@ -7,11 +7,11 @@ import { useEffect } from "preact/hooks";
|
||||
|
||||
import ConditionalLink from "../../../lib/ConditionalLink";
|
||||
import PaintCounter from "../../../lib/PaintCounter";
|
||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||
|
||||
import { dispatch } from "../../../redux";
|
||||
import { connectState } from "../../../redux/connector";
|
||||
import { Unreads } from "../../../redux/reducers/unreads";
|
||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||
|
||||
import { useClient } from "../../../context/revoltjs/RevoltClient";
|
||||
|
||||
@@ -38,9 +38,9 @@ const ServerBase = styled.div`
|
||||
overflow: hidden;
|
||||
|
||||
${isTouchscreenDevice &&
|
||||
css`
|
||||
padding-bottom: 50px;
|
||||
`}
|
||||
css`
|
||||
padding-bottom: 50px;
|
||||
`}
|
||||
`;
|
||||
|
||||
const ServerList = styled.div`
|
||||
@@ -73,7 +73,7 @@ const ServerSidebar = observer((props: Props) => {
|
||||
parent: server_id!,
|
||||
child: channel_id!,
|
||||
});
|
||||
}, [channel_id]);
|
||||
}, [channel_id, server_id]);
|
||||
|
||||
const uncategorised = new Set(server.channel_ids);
|
||||
const elements = [];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { autorun, isObservableProp, reaction } from "mobx";
|
||||
import { reaction } from "mobx";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
|
||||
import { useLayoutEffect } from "preact/hooks";
|
||||
@@ -6,16 +6,12 @@ import { useLayoutEffect } from "preact/hooks";
|
||||
import { dispatch } from "../../../redux";
|
||||
import { Unreads } from "../../../redux/reducers/unreads";
|
||||
|
||||
import { useClient } from "../../../context/revoltjs/RevoltClient";
|
||||
|
||||
type UnreadProps = {
|
||||
channel: Channel;
|
||||
unreads: Unreads;
|
||||
};
|
||||
|
||||
export function useUnreads({ channel, unreads }: UnreadProps) {
|
||||
const client = useClient();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
function checkUnread(target: Channel) {
|
||||
if (!target) return;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { useRenderState } from "../../../lib/renderer/Singleton";
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
import { Presence } from "revolt-api/types/Users";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import Members, { Member } from "revolt.js/dist/maps/Members";
|
||||
import { Message } from "revolt.js/dist/maps/Messages";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { ClientboundNotification } from "revolt.js/dist/websocket/notifications";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
import { useContext, useEffect, useState } from "preact/hooks";
|
||||
@@ -14,7 +12,6 @@ import { getState } from "../../../redux";
|
||||
|
||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||
import {
|
||||
AppContext,
|
||||
ClientStatus,
|
||||
StatusContext,
|
||||
useClient,
|
||||
@@ -50,7 +47,6 @@ export const GroupMemberSidebar = observer(
|
||||
({ channel }: { channel: Channel }) => {
|
||||
const { openScreen } = useIntermediate();
|
||||
|
||||
const client = useClient();
|
||||
const members = channel.recipients?.filter(
|
||||
(x) => typeof x !== "undefined",
|
||||
);
|
||||
@@ -173,9 +169,9 @@ export const ServerMemberSidebar = observer(
|
||||
if (status === ClientStatus.ONLINE) {
|
||||
channel.server!.fetchMembers();
|
||||
}
|
||||
}, [status]);
|
||||
}, [status, channel.server]);
|
||||
|
||||
let users = [...client.members.keys()]
|
||||
const users = [...client.members.keys()]
|
||||
.map((x) => JSON.parse(x))
|
||||
.filter((x) => x.server === channel.server_id)
|
||||
.map((y) => client.users.get(y.user)!)
|
||||
@@ -247,7 +243,6 @@ export const ServerMemberSidebar = observer(
|
||||
function Search({ channel }: { channel: Channel }) {
|
||||
if (!getState().experiments.enabled?.includes("search")) return null;
|
||||
|
||||
const client = useContext(AppContext);
|
||||
type Sort = "Relevance" | "Latest" | "Oldest";
|
||||
const [sort, setSort] = useState<Sort>("Relevance");
|
||||
|
||||
@@ -272,6 +267,7 @@ function Search({ channel }: { channel: Channel }) {
|
||||
<div style={{ display: "flex" }}>
|
||||
{["Relevance", "Latest", "Oldest"].map((key) => (
|
||||
<Button
|
||||
key={key}
|
||||
style={{ flex: 1, minWidth: 0 }}
|
||||
compact
|
||||
error={sort === key}
|
||||
@@ -304,7 +300,7 @@ function Search({ channel }: { channel: Channel }) {
|
||||
href += `/channel/${message.channel_id}/${message._id}`;
|
||||
|
||||
return (
|
||||
<Link to={href}>
|
||||
<Link to={href} key={message._id}>
|
||||
<div
|
||||
style={{
|
||||
margin: "2px",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import styled, { css, keyframes } from "styled-components";
|
||||
|
||||
import { createPortal, useEffect, useState } from "preact/compat";
|
||||
import { createPortal, useCallback, useEffect, useState } from "preact/compat";
|
||||
|
||||
import { internalSubscribe } from "../../lib/eventEmitter";
|
||||
|
||||
@@ -134,7 +135,7 @@ interface Props {
|
||||
dontModal?: boolean;
|
||||
padding?: boolean;
|
||||
|
||||
onClose: () => void;
|
||||
onClose?: () => void;
|
||||
actions?: Action[];
|
||||
disabled?: boolean;
|
||||
border?: boolean;
|
||||
@@ -163,12 +164,12 @@ export default function Modal(props: Props) {
|
||||
|
||||
const [animateClose, setAnimateClose] = useState(false);
|
||||
isModalClosing = animateClose;
|
||||
function onClose() {
|
||||
const onClose = useCallback(() => {
|
||||
setAnimateClose(true);
|
||||
setTimeout(() => props.onClose(), 2e2);
|
||||
}
|
||||
setTimeout(() => props.onClose?.(), 2e2);
|
||||
}, [setAnimateClose, props]);
|
||||
|
||||
useEffect(() => internalSubscribe("Modal", "close", onClose), []);
|
||||
useEffect(() => internalSubscribe("Modal", "close", onClose), [onClose]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.disallowClosing) return;
|
||||
@@ -181,7 +182,7 @@ export default function Modal(props: Props) {
|
||||
|
||||
document.body.addEventListener("keydown", keyDown);
|
||||
return () => document.body.removeEventListener("keydown", keyDown);
|
||||
}, [props.disallowClosing, props.onClose]);
|
||||
}, [props.disallowClosing, onClose]);
|
||||
|
||||
const confirmationAction = props.actions?.find(
|
||||
(action) => action.confirmation,
|
||||
@@ -211,8 +212,12 @@ export default function Modal(props: Props) {
|
||||
{content}
|
||||
{props.actions && (
|
||||
<ModalActions>
|
||||
{props.actions.map((x) => (
|
||||
<Button {...x} disabled={props.disabled} />
|
||||
{props.actions.map((x, index) => (
|
||||
<Button
|
||||
key={index}
|
||||
{...x}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
))}
|
||||
</ModalActions>
|
||||
)}
|
||||
|
||||
@@ -64,7 +64,7 @@ export default function Tip(
|
||||
{!hideSeparator && <Separator />}
|
||||
<TipBase {...tipProps}>
|
||||
<InfoCircle size={20} />
|
||||
<span>{props.children}</span>
|
||||
<span>{children}</span>
|
||||
</TipBase>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import styled, { css } from "styled-components";
|
||||
import { Children } from "../../../types/Preact";
|
||||
|
||||
interface BaseProps {
|
||||
readonly hover?: boolean;
|
||||
readonly account?: boolean;
|
||||
readonly disabled?: boolean;
|
||||
readonly largeDescription?: boolean;
|
||||
@@ -25,8 +26,6 @@ const CategoryBase = styled.div<BaseProps>`
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
@@ -41,8 +40,6 @@ const CategoryBase = styled.div<BaseProps>`
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.description {
|
||||
${(props) =>
|
||||
props.largeDescription
|
||||
@@ -66,7 +63,7 @@ const CategoryBase = styled.div<BaseProps>`
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
typeof props.onClick !== "undefined" &&
|
||||
props.hover &&
|
||||
css`
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
@@ -80,7 +77,7 @@ const CategoryBase = styled.div<BaseProps>`
|
||||
${(props) =>
|
||||
props.disabled &&
|
||||
css`
|
||||
opacity: .4;
|
||||
opacity: 0.4;
|
||||
/*.content,
|
||||
.action {
|
||||
color: var(--tertiary-foreground);
|
||||
@@ -133,17 +130,19 @@ export default function CategoryButton({
|
||||
account,
|
||||
disabled,
|
||||
onClick,
|
||||
hover,
|
||||
action,
|
||||
}: Props) {
|
||||
return (
|
||||
<CategoryBase
|
||||
hover={hover || typeof onClick !== "undefined"}
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
account={account}>
|
||||
{icon}
|
||||
<div class="content">
|
||||
<div className="title">{children}</div>
|
||||
|
||||
|
||||
<div className="description">{description}</div>
|
||||
</div>
|
||||
<div class="action">
|
||||
@@ -159,4 +158,4 @@ export default function CategoryButton({
|
||||
</div>
|
||||
</CategoryBase>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user