Merge pull request #199 from brecert/mentions-methods

This commit is contained in:
Paul Makles 2021-09-09 22:00:03 +01:00 committed by GitHub
commit efbbb6f1aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 138 additions and 32 deletions

View file

@ -25,6 +25,7 @@ import Attachment from "./attachments/Attachment";
import { MessageReply } from "./attachments/MessageReply"; import { MessageReply } from "./attachments/MessageReply";
import Embed from "./embed/Embed"; import Embed from "./embed/Embed";
import InviteList from "./embed/EmbedInvite"; import InviteList from "./embed/EmbedInvite";
import { internalEmit } from "../../../lib/eventEmitter";
interface Props { interface Props {
attachContext?: boolean; attachContext?: boolean;
@ -70,6 +71,19 @@ const Message = observer(
const openProfile = () => const openProfile = () =>
openScreen({ id: "profile", user_id: message.author_id }); openScreen({ id: "profile", user_id: message.author_id });
const handleUserClick = (e: MouseEvent) => {
if (e.shiftKey && user?._id) {
internalEmit(
"MessageBox",
"append",
`<@${user._id}>`,
"mention",
);
} else {
openProfile()
}
}
// ! FIXME(?): animate on hover // ! FIXME(?): animate on hover
const [animate, setAnimate] = useState(false); const [animate, setAnimate] = useState(false);
@ -118,7 +132,7 @@ const Message = observer(
target={user} target={user}
size={36} size={36}
onContextMenu={userContext} onContextMenu={userContext}
onClick={openProfile} onClick={handleUserClick}
animate={animate} animate={animate}
showServerIdentity showServerIdentity
/> />
@ -133,7 +147,7 @@ const Message = observer(
className="author" className="author"
user={user} user={user}
onContextMenu={userContext} onContextMenu={userContext}
onClick={openProfile} onClick={handleUserClick}
showServerIdentity showServerIdentity
/> />
<MessageDetail <MessageDetail

View file

@ -9,6 +9,7 @@ import { useIntermediate } from "../../../context/intermediate/Intermediate";
import { useClient } from "../../../context/revoltjs/RevoltClient"; import { useClient } from "../../../context/revoltjs/RevoltClient";
import UserIcon from "./UserIcon"; import UserIcon from "./UserIcon";
import { internalEmit } from "../../../lib/eventEmitter";
const BotBadge = styled.div` const BotBadge = styled.div`
display: inline-block; display: inline-block;
@ -25,17 +26,18 @@ const BotBadge = styled.div`
border-radius: calc(var(--border-radius) / 2); border-radius: calc(var(--border-radius) / 2);
`; `;
type UsernameProps = JSX.HTMLAttributes<HTMLElement> & {
user?: User;
prefixAt?: boolean;
showServerIdentity?: boolean;
}
export const Username = observer( export const Username = observer(
({ ({
user, user,
prefixAt, prefixAt,
showServerIdentity, showServerIdentity,
...otherProps ...otherProps
}: { }: UsernameProps) => {
user?: User;
prefixAt?: boolean;
showServerIdentity?: boolean;
} & JSX.HTMLAttributes<HTMLElement>) => {
let username = user?.username; let username = user?.username;
let color; let color;
@ -108,18 +110,32 @@ export default function UserShort({
const openProfile = () => const openProfile = () =>
user && openScreen({ id: "profile", user_id: user._id }); user && openScreen({ id: "profile", user_id: user._id });
const handleUserClick = (e: MouseEvent) => {
if (e.shiftKey && user?._id) {
e.preventDefault()
internalEmit(
"MessageBox",
"append",
`<@${user?._id}>`,
"mention",
);
} else {
openProfile()
}
}
return ( return (
<> <>
<UserIcon <UserIcon
size={size ?? 24} size={size ?? 24}
target={user} target={user}
onClick={openProfile} onClick={handleUserClick}
showServerIdentity={showServerIdentity} showServerIdentity={showServerIdentity}
/> />
<Username <Username
user={user} user={user}
showServerIdentity={showServerIdentity} showServerIdentity={showServerIdentity}
onClick={openProfile} onClick={handleUserClick}
prefixAt={prefixAt} prefixAt={prefixAt}
/> />
</> </>

View file

@ -44,7 +44,7 @@ if (typeof window !== "undefined") {
if (code) { if (code) {
navigator.clipboard.writeText(code.textContent?.trim() ?? ""); navigator.clipboard.writeText(code.textContent?.trim() ?? "");
} }
} catch (e) {} } catch (e) { }
}; };
} }
@ -140,7 +140,35 @@ export default function Renderer({ content, disallowBigEmoji }: MarkdownProps) {
(ev: MouseEvent) => { (ev: MouseEvent) => {
if (ev.currentTarget) { if (ev.currentTarget) {
const element = ev.currentTarget as HTMLAnchorElement; const element = ev.currentTarget as HTMLAnchorElement;
if (openLink(element.href)) ev.preventDefault();
if (ev.shiftKey) {
switch (element.dataset.type) {
case "mention": {
internalEmit(
"MessageBox",
"append",
`<@${element.dataset.mentionId}>`,
"mention",
);
ev.preventDefault()
return
}
case "channel_mention": {
internalEmit(
"MessageBox",
"append",
`<#${element.dataset.mentionId}>`,
"channel_mention",
);
ev.preventDefault()
return
}
}
}
if (openLink(element.href)) {
ev.preventDefault();
}
} }
}, },
[openLink], [openLink],
@ -162,15 +190,34 @@ export default function Renderer({ content, disallowBigEmoji }: MarkdownProps) {
element.removeEventListener("click", handleLink); element.removeEventListener("click", handleLink);
element.addEventListener("click", handleLink); element.addEventListener("click", handleLink);
element.removeAttribute("data-type"); element.removeAttribute("data-type");
element.removeAttribute("data-mention-id");
element.removeAttribute("target"); element.removeAttribute("target");
const link = determineLink(element.href); const link = determineLink(element.href);
console.log(link)
switch (link.type) { switch (link.type) {
case "profile": { case "profile": {
element.setAttribute( element.setAttribute(
"data-type", "data-type",
"mention", "mention",
); );
element.setAttribute(
"data-mention-id",
link.id
)
break;
}
case "navigate": {
if (link.navigation_type === 'channel') {
element.setAttribute(
"data-type",
"channel_mention",
);
element.setAttribute(
"data-mention-id",
link.channel_id
)
}
break; break;
} }
case "external": { case "external": {

View file

@ -22,6 +22,7 @@ import { mapChannelWithUnread, useUnreads } from "./common";
import { ChannelButton } from "../items/ButtonItem"; import { ChannelButton } from "../items/ButtonItem";
import ConnectionStatus from "../items/ConnectionStatus"; import ConnectionStatus from "../items/ConnectionStatus";
import { internalEmit } from "../../../lib/eventEmitter";
interface Props { interface Props {
unreads: Unreads; unreads: Unreads;
@ -90,6 +91,17 @@ const ServerSidebar = observer((props: Props) => {
return ( return (
<ConditionalLink <ConditionalLink
onClick={e => {
if (e.shiftKey) {
internalEmit(
"MessageBox",
"append",
`<#${entry._id}>`,
"channel_mention",
);
e.preventDefault()
}
}}
key={entry._id} key={entry._id}
active={active} active={active}
to={`/server/${server!._id}/channel/${entry._id}`}> to={`/server/${server!._id}/channel/${entry._id}`}>

View file

@ -12,6 +12,7 @@ import {
} from "../../../context/intermediate/Intermediate"; } from "../../../context/intermediate/Intermediate";
import { UserButton } from "../items/ButtonItem"; import { UserButton } from "../items/ButtonItem";
import { internalEmit } from "../../../lib/eventEmitter";
export type MemberListGroup = { export type MemberListGroup = {
type: "online" | "offline" | "role"; type: "online" | "offline" | "role";
@ -53,12 +54,21 @@ const ItemContent = memo(
user={item} user={item}
margin margin
context={context} context={context}
onClick={() => onClick={e => {
if (e.shiftKey) {
internalEmit(
"MessageBox",
"append",
`<@${item._id}>`,
"mention",
);
} else[
openScreen({ openScreen({
id: "profile", id: "profile",
user_id: item._id, user_id: item._id,
}) })
} ]
}}
/> />
), ),
); );

View file

@ -9,7 +9,7 @@ export default function ConditionalLink(props: Props) {
const { active, ...linkProps } = props; const { active, ...linkProps } = props;
if (active) { if (active) {
return <a>{props.children}</a>; return <a onClick={linkProps.onClick}>{props.children}</a>;
} }
return <Link {...linkProps} />; return <Link {...linkProps} />;
} }

View file

@ -1,6 +1,7 @@
type LinkType = type LinkType =
| { type: "profile"; id: string } | { type: "profile"; id: string }
| { type: "navigate"; path: string } | { type: "navigate"; path: string; navigation_type?: null }
| { type: "navigate"; path: string; navigation_type: 'channel'; channel_id: string }
| { type: "external"; href: string; url: URL } | { type: "external"; href: string; url: URL }
| { type: "none" }; | { type: "none" };
@ -11,6 +12,8 @@ const ALLOWED_ORIGINS = [
"local.revolt.chat", "local.revolt.chat",
]; ];
const CHANNEL_PATH_RE = /^\/server\/[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}\/channel\/[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$/
export function determineLink(href?: string): LinkType { export function determineLink(href?: string): LinkType {
let internal, let internal,
url: URL | null = null; url: URL | null = null;
@ -27,6 +30,10 @@ export function determineLink(href?: string): LinkType {
return { type: "profile", id }; return { type: "profile", id };
} }
} else { } else {
console.log(path)
if(CHANNEL_PATH_RE.test(path)) {
return { type: 'navigate', path, navigation_type: 'channel', channel_id: path.slice(43) }
}
return { type: "navigate", path }; return { type: "navigate", path };
} }