mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-10 01:03:36 -05:00
Merge branch 'master' of https://github.com/revoltchat/revite
This commit is contained in:
commit
afa76f0623
8 changed files with 142 additions and 78 deletions
|
@ -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",
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
18
src/lib/modifiers.ts
Normal 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;
|
||||||
|
});
|
||||||
|
}
|
|
@ -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} />
|
||||||
|
|
23
yarn.lock
23
yarn.lock
|
@ -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"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue