mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-21 22:50:59 -05:00
Merge pull request #199 from brecert/mentions-methods
This commit is contained in:
commit
efbbb6f1aa
7 changed files with 138 additions and 32 deletions
|
@ -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;
|
||||||
|
@ -61,15 +62,28 @@ const Message = observer(
|
||||||
// bree: Fatal please...
|
// bree: Fatal please...
|
||||||
const userContext = attachContext
|
const userContext = attachContext
|
||||||
? (attachContextMenu("Menu", {
|
? (attachContextMenu("Menu", {
|
||||||
user: message.author_id,
|
user: message.author_id,
|
||||||
contextualChannel: message.channel_id,
|
contextualChannel: message.channel_id,
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}) as any)
|
}) as any)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
@ -91,11 +105,11 @@ const Message = observer(
|
||||||
hideReply
|
hideReply
|
||||||
? false
|
? false
|
||||||
: (head &&
|
: (head &&
|
||||||
!(
|
!(
|
||||||
message.reply_ids &&
|
message.reply_ids &&
|
||||||
message.reply_ids.length > 0
|
message.reply_ids.length > 0
|
||||||
)) ??
|
)) ??
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
contrast={contrast}
|
contrast={contrast}
|
||||||
sending={typeof queued !== "undefined"}
|
sending={typeof queued !== "undefined"}
|
||||||
|
@ -104,10 +118,10 @@ const Message = observer(
|
||||||
onContextMenu={
|
onContextMenu={
|
||||||
attachContext
|
attachContext
|
||||||
? attachContextMenu("Menu", {
|
? attachContextMenu("Menu", {
|
||||||
message,
|
message,
|
||||||
contextualChannel: message.channel_id,
|
contextualChannel: message.channel_id,
|
||||||
queued,
|
queued,
|
||||||
})
|
})
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
onMouseEnter={() => setAnimate(true)}
|
onMouseEnter={() => setAnimate(true)}
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -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": {
|
||||||
|
|
|
@ -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}`}>
|
||||||
|
|
|
@ -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 => {
|
||||||
openScreen({
|
if (e.shiftKey) {
|
||||||
id: "profile",
|
internalEmit(
|
||||||
user_id: item._id,
|
"MessageBox",
|
||||||
})
|
"append",
|
||||||
}
|
`<@${item._id}>`,
|
||||||
|
"mention",
|
||||||
|
);
|
||||||
|
} else[
|
||||||
|
openScreen({
|
||||||
|
id: "profile",
|
||||||
|
user_id: item._id,
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -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} />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue