This commit is contained in:
trashtemp 2022-01-15 14:33:26 +01:00
commit afa76f0623
No known key found for this signature in database
GPG key ID: D1F0DB65081B0FC6
8 changed files with 142 additions and 78 deletions

View file

@ -145,8 +145,8 @@
"react-scroll": "^1.8.2", "react-scroll": "^1.8.2",
"react-virtualized-auto-sizer": "^1.0.5", "react-virtualized-auto-sizer": "^1.0.5",
"react-virtuoso": "^1.10.4", "react-virtuoso": "^1.10.4",
"revolt-api": "0.5.3-alpha.10", "revolt-api": "0.5.3-alpha.12",
"revolt.js": "5.2.5", "revolt.js": "5.2.7",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"sass": "^1.35.1", "sass": "^1.35.1",
"shade-blend-color": "^1.0.0", "shade-blend-color": "^1.0.0",

View file

@ -3,9 +3,10 @@ import { Message as MessageObject } from "revolt.js/dist/maps/Messages";
import { attachContextMenu } from "preact-context-menu"; import { attachContextMenu } from "preact-context-menu";
import { memo } from "preact/compat"; import { memo } from "preact/compat";
import { useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import { internalEmit } from "../../../lib/eventEmitter"; import { internalEmit } from "../../../lib/eventEmitter";
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
import { QueuedMessage } from "../../../mobx/stores/MessageQueue"; import { QueuedMessage } from "../../../mobx/stores/MessageQueue";
@ -88,6 +89,7 @@ const Message = observer(
// ! FIXME(?): animate on hover // ! FIXME(?): animate on hover
const [mouseHovering, setAnimate] = useState(false); const [mouseHovering, setAnimate] = useState(false);
useEffect(() => setAnimate(false), [replacement]);
return ( return (
<div id={message._id}> <div id={message._id}>
@ -175,12 +177,14 @@ const Message = observer(
{message.embeds?.map((embed, index) => ( {message.embeds?.map((embed, index) => (
<Embed key={index} embed={embed} /> <Embed key={index} embed={embed} />
))} ))}
{mouseHovering && !replacement && ( {mouseHovering &&
<MessageOverlayBar !replacement &&
message={message} !isTouchscreenDevice && (
queued={queued} <MessageOverlayBar
/> message={message}
)} queued={queued}
/>
)}
</MessageContent> </MessageContent>
</MessageBase> </MessageBase>
</div> </div>

View file

@ -12,8 +12,11 @@ import { Message as MessageObject } from "revolt.js/dist/maps/Messages";
import styled from "styled-components"; import styled from "styled-components";
import { openContextMenu } from "preact-context-menu"; import { openContextMenu } from "preact-context-menu";
import { useEffect, useState } from "preact/hooks";
import { internalEmit } from "../../../../lib/eventEmitter"; import { internalEmit } from "../../../../lib/eventEmitter";
import { shiftKeyPressed } from "../../../../lib/modifiers";
import { getRenderer } from "../../../../lib/renderer/Singleton";
import { QueuedMessage } from "../../../../mobx/stores/MessageQueue"; import { QueuedMessage } from "../../../../mobx/stores/MessageQueue";
@ -89,9 +92,24 @@ const Divider = styled.div`
export const MessageOverlayBar = observer(({ message, queued }: Props) => { export const MessageOverlayBar = observer(({ message, queued }: Props) => {
const client = useClient(); const client = useClient();
const { openScreen } = useIntermediate(); const { openScreen, writeClipboard } = useIntermediate();
const isAuthor = message.author_id === client.user!._id; const isAuthor = message.author_id === client.user!._id;
const [copied, setCopied] = useState<"link" | "id">(null!);
const [extraActions, setExtra] = useState(shiftKeyPressed);
useEffect(() => {
const handler = (ev: KeyboardEvent) => setExtra(ev.shiftKey);
document.addEventListener("keyup", handler);
document.addEventListener("keydown", handler);
return () => {
document.removeEventListener("keyup", handler);
document.removeEventListener("keydown", handler);
};
});
return ( return (
<OverlayBar> <OverlayBar>
<Tooltip content="Reply"> <Tooltip content="Reply">
@ -120,12 +138,14 @@ export const MessageOverlayBar = observer(({ message, queued }: Props) => {
ChannelPermission.ManageMessages) ? ( ChannelPermission.ManageMessages) ? (
<Tooltip content="Delete"> <Tooltip content="Delete">
<Entry <Entry
onClick={() => onClick={(e) =>
openScreen({ e.shiftKey
id: "special_prompt", ? message.delete()
type: "delete_message", : openScreen({
target: message, id: "special_prompt",
} as unknown as Screen) type: "delete_message",
target: message,
} as unknown as Screen)
}> }>
<Trash size={18} color={"var(--error)"} /> <Trash size={18} color={"var(--error)"} />
</Entry> </Entry>
@ -143,43 +163,54 @@ export const MessageOverlayBar = observer(({ message, queued }: Props) => {
<DotsVerticalRounded size={18} /> <DotsVerticalRounded size={18} />
</Entry> </Entry>
</Tooltip> </Tooltip>
<Divider /> {extraActions && (
<Tooltip content="Mark as Unread"> <>
<Entry <Divider />
onClick={() => <Tooltip content="Mark as Unread">
openContextMenu("Menu", { <Entry
message, onClick={() => {
contextualChannel: message.channel_id, const messages = getRenderer(
queued, message.channel!,
}) ).messages;
}> const index = messages.findIndex(
<Notification size={18} /> (x) => x._id === message._id,
</Entry> );
</Tooltip>
<Tooltip content="Copy Link"> let unread_id = message._id;
<Entry if (index > 0) {
onClick={() => unread_id = messages[index - 1]._id;
openContextMenu("Menu", { }
message,
contextualChannel: message.channel_id, internalEmit("NewMessages", "mark", unread_id);
queued, message.channel?.ack(unread_id, true);
}) }}>
}> <Notification size={18} />
<LinkAlt size={18} /> </Entry>
</Entry> </Tooltip>
</Tooltip> <Tooltip
<Tooltip content="Copy ID"> content={copied === "link" ? "Copied!" : "Copy Link"}
<Entry hideOnClick={false}>
onClick={() => <Entry
openContextMenu("Menu", { onClick={() => {
message, setCopied("link");
contextualChannel: message.channel_id, writeClipboard(message.url);
queued, }}>
}) <LinkAlt size={18} />
}> </Entry>
<InfoSquare size={18} /> </Tooltip>
</Entry> <Tooltip
</Tooltip> content={copied === "id" ? "Copied!" : "Copy ID"}
hideOnClick={false}>
<Entry
onClick={() => {
setCopied("id");
writeClipboard(message._id);
}}>
<InfoSquare size={18} />
</Entry>
</Tooltip>
</>
)}
</OverlayBar> </OverlayBar>
); );
}); });

View file

@ -153,12 +153,13 @@ export const ChannelButton = observer((props: ChannelProps) => {
} }
const { openScreen } = useIntermediate(); const { openScreen } = useIntermediate();
const alerting = alert && !muted && !active;
return ( return (
<div <div
{...divProps} {...divProps}
data-active={active} data-active={active}
data-alert={typeof alert === "string" && !muted} data-alert={alerting}
data-muted={muted} data-muted={muted}
aria-label={channel.name} aria-label={channel.name}
className={classNames(styles.item, { [styles.compact]: compact })} className={classNames(styles.item, { [styles.compact]: compact })}
@ -190,7 +191,7 @@ export const ChannelButton = observer((props: ChannelProps) => {
)} )}
</div> </div>
<div className={styles.button}> <div className={styles.button}>
{alert && !muted && ( {alerting && (
<div className={styles.alert} data-style={alert}> <div className={styles.alert} data-style={alert}>
{alertCount} {alertCount}
</div> </div>

View file

@ -30,3 +30,4 @@ export function internalEmit(ns: string, event: string, ...args: unknown[]) {
// - Modal/close // - Modal/close
// - PWA/update // - PWA/update
// - NewMessages/hide // - NewMessages/hide
// - NewMessages/mark

18
src/lib/modifiers.ts Normal file
View file

@ -0,0 +1,18 @@
/**
* Utility file for detecting whether the
* shift key is currently pressed or not.
*/
export let shiftKeyPressed = false;
if (typeof window !== "undefined") {
document.addEventListener("keydown", (ev) => {
if (ev.shiftKey) shiftKeyPressed = true;
else shiftKeyPressed = false;
});
document.addEventListener("keyup", (ev) => {
if (ev.shiftKey) shiftKeyPressed = true;
else shiftKeyPressed = false;
});
}

View file

@ -7,9 +7,10 @@ import { Channel as ChannelI } from "revolt.js/dist/maps/Channels";
import styled from "styled-components/macro"; import styled from "styled-components/macro";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useEffect, useMemo } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import ErrorBoundary from "../../lib/ErrorBoundary"; import ErrorBoundary from "../../lib/ErrorBoundary";
import { internalSubscribe } from "../../lib/eventEmitter";
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice"; import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
import { useApplicationState } from "../../mobx/State"; import { useApplicationState } from "../../mobx/State";
@ -123,17 +124,30 @@ export function Channel({ id, server_id }: { id: string; server_id: string }) {
const TextChannel = observer(({ channel }: { channel: ChannelI }) => { const TextChannel = observer(({ channel }: { channel: ChannelI }) => {
const layout = useApplicationState().layout; const layout = useApplicationState().layout;
// Cache the unread location. // Store unread location.
const last_id = useMemo( const [lastId, setLastId] = useState<string | undefined>(undefined);
useEffect(
() => () =>
(channel.unread internalSubscribe("NewMessages", "hide", () =>
? channel.client.unreads?.getUnread(channel._id)?.last_id setLastId(undefined),
: undefined) ?? undefined, ),
[channel], [],
);
useEffect(
() => internalSubscribe("NewMessages", "mark", setLastId as any),
[],
); );
// Mark channel as read. // Mark channel as read.
useEffect(() => { useEffect(() => {
setLastId(
channel.unread
? channel.client.unreads?.getUnread(channel._id)?.last_id
: undefined ?? undefined,
);
const checkUnread = () => const checkUnread = () =>
channel.unread && channel.unread &&
channel.client.unreads!.markRead( channel.client.unreads!.markRead(
@ -165,8 +179,8 @@ const TextChannel = observer(({ channel }: { channel: ChannelI }) => {
<ErrorBoundary section="renderer"> <ErrorBoundary section="renderer">
<ChannelContent> <ChannelContent>
<VoiceHeader id={channel._id} /> <VoiceHeader id={channel._id} />
<NewMessages channel={channel} last_id={last_id} /> <NewMessages channel={channel} last_id={lastId} />
<MessageArea channel={channel} last_id={last_id} /> <MessageArea channel={channel} last_id={lastId} />
<TypingIndicator channel={channel} /> <TypingIndicator channel={channel} />
<JumpToBottom channel={channel} /> <JumpToBottom channel={channel} />
<MessageBox channel={channel} /> <MessageBox channel={channel} />

View file

@ -4203,20 +4203,15 @@ reusify@^1.0.4:
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
revolt-api@0.5.3-alpha.10: revolt-api@0.5.3-alpha.12:
version "0.5.3-alpha.10" version "0.5.3-alpha.12"
resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.3-alpha.10.tgz#973f7d63dbce5ddb0c5ec17c7e89a0474770d66f" resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.3-alpha.12.tgz#78f25b567b840c1fd072595526592a422cb01f25"
integrity sha512-v+eSPLWpiqmfHafPDeCF1nvd4Ff43ktyvVgGHt7aQN2v9Qm1BFrmpWHWoDPeZpXlW6mtpo+1t74nLK6VCsZ+VA== integrity sha512-MM42oI5+5JJMnAs3JiOwSQOy/SUYzYs3M8YRC5QI4G6HU7CfyB2HNWh5jFsyRlcLdSi13dGazHm31FUPHsxOzw==
revolt-api@^0.5.3-alpha.9: revolt.js@5.2.7:
version "0.5.3-alpha.9" version "5.2.7"
resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.3-alpha.9.tgz#46e75b7d8f9c6702df39039b829dddbb7897f237" resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-5.2.7.tgz#7b887329913494a2caf02c9828685d63551890db"
integrity sha512-L8K9uPV3ME8bLdtWm8L9iPQvFM0GghA+5LzmWFjd6Gbn56u22ZYub2lABi4iHrWgeA2X41dGSsuSBgHSlts9Og== integrity sha512-KNoQqLrdd/B8zryu2fhWim9rO5OEkouhCZj4nU+upwrekz30DjxqWgZCup/apKXE8PSmrhSgWdKT8SHCBXOxFQ==
revolt.js@5.2.5:
version "5.2.5"
resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-5.2.5.tgz#ffd2b759a807f3430f6018459acdf57e95f842a5"
integrity sha512-rS6FOc7XPfe/BuGnuCvM1N/CEGMtR/bHy4le5154MF9bbpebLw205niq7rq5TMsXT/f7J4qlOI7xX0rvKprn6w==
dependencies: dependencies:
"@insertish/exponential-backoff" "3.1.0-patch.0" "@insertish/exponential-backoff" "3.1.0-patch.0"
"@insertish/isomorphic-ws" "^4.0.1" "@insertish/isomorphic-ws" "^4.0.1"
@ -4226,7 +4221,7 @@ revolt.js@5.2.5:
lodash.flatten "^4.4.0" lodash.flatten "^4.4.0"
lodash.isequal "^4.5.0" lodash.isequal "^4.5.0"
mobx "^6.3.2" mobx "^6.3.2"
revolt-api "^0.5.3-alpha.9" revolt-api "0.5.3-alpha.12"
ulid "^2.3.0" ulid "^2.3.0"
ws "^8.2.2" ws "^8.2.2"