Skip to content

Commit

Permalink
feat: attachment click handler (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnyTheCarrot committed Aug 17, 2023
1 parent 0b9a72c commit c2b48c5
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 34 deletions.
6 changes: 6 additions & 0 deletions src/Content/Attachment/ImageAttachment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import React from "react";
import * as Styles from "./style";
import type { APIAttachment } from "discord-api-types/v10";
import { t } from "i18next";
import { useConfig } from "../../core/ConfigContext";

interface ImageAttachmentProps {
attachment: APIAttachment;
}

function ImageAttachment(props: ImageAttachmentProps) {
const { attachmentImageOnClick } = useConfig();

if (!props.attachment.width || !props.attachment.height) {
// todo: dev mode only
console.error(
Expand All @@ -28,6 +31,9 @@ function ImageAttachment(props: ImageAttachmentProps) {
src={props.attachment.url}
width={width}
height={height}
draggable={false}
clickable={attachmentImageOnClick !== undefined}
onClick={() => attachmentImageOnClick?.(props.attachment)}
placeholder={
<Styles.LazyImagePlaceholder style={{ width, height }}>
{t("loading")}
Expand Down
8 changes: 7 additions & 1 deletion src/Content/Attachment/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ export const ImageAttachment = styled.withConfig({
displayName: "image-attachment",
componentId: commonComponentId,
})(LazyLoadImage, {
cursor: "pointer",
variants: {
clickable: {
true: {
cursor: "pointer",
},
},
},
});

export const LazyImagePlaceholder = styled.withConfig({
Expand Down
29 changes: 29 additions & 0 deletions src/Content/Embed/EmbeddedImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as Styles from "./style";
import type { ComponentProps } from "react";
import React from "react";
import { useConfig } from "../../core/ConfigContext";
import type { APIEmbedImage } from "discord-api-types/v10";

interface Props extends ComponentProps<typeof Styles.Image> {
embedImage: APIEmbedImage;
width?: number;
height?: number;
}

// Not to be confused with ImageEmbed, this is images that are embedded in embeds
function EmbeddedImage({ width, height, embedImage, ...rest }: Props) {
const { embedImageOnClick } = useConfig();

return (
<Styles.Image
{...rest}
src={embedImage.proxy_url ?? embedImage.url}
clickable={embedImageOnClick !== undefined}
onClick={() => embedImageOnClick?.(embedImage)}
width={width}
height={height}
/>
);
}

export default EmbeddedImage;
11 changes: 2 additions & 9 deletions src/Content/Embed/ImageEmbed.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as Styles from "./style";
import React from "react";
import type { APIEmbed } from "discord-api-types/v10";
import useSize from "../Attachment/useSize";
import EmbeddedImage from "./EmbeddedImage";

export interface GifVEmbedProps {
embed: APIEmbed;
Expand All @@ -18,14 +18,7 @@ function ImageEmbed({ embed }: GifVEmbedProps) {
return null;
}

return (
<Styles.MediaEmbed
src={embed.thumbnail.proxy_url}
// originalUrl={embed.thumbnail.url}
width={size.width}
height={size.height}
/>
);
return <EmbeddedImage embedImage={embed.thumbnail} {...size} />;
}

export default ImageEmbed;
17 changes: 6 additions & 11 deletions src/Content/Embed/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import EmbedVideo from "./EmbedVideo";
import React, { useMemo } from "react";
import type { APIEmbed, APIEmbedImage } from "discord-api-types/v10";
import { EmbedType } from "discord-api-types/v10";
import EmbeddedImage from "./EmbeddedImage";
import ExternalLink from "../../ExternalLink";

export interface EmbedProps {
Expand Down Expand Up @@ -128,18 +129,16 @@ function Embed({ embed, images }: EmbedProps) {
)}
</Styles.Content>
{embed.thumbnail && embed.type !== EmbedType.Video && (
<Styles.Image
src={embed.thumbnail.proxy_url}
// originalUrl={embed.thumbnail.url}
<EmbeddedImage
embedImage={embed.thumbnail}
width={widthThumbnail ?? undefined}
height={heightThumbnail ?? undefined}
/>
)}
</Styles.ContentAndThumbnail>
{(images === undefined || images?.length === 0) && embed.image && (
<Styles.Image
src={embed.image.proxy_url}
// originalUrl={embed.image.url}
<EmbeddedImage
embedImage={embed.image}
width={widthImage ?? undefined}
height={heightImage ?? undefined}
/>
Expand All @@ -148,11 +147,7 @@ function Embed({ embed, images }: EmbedProps) {
<Styles.Images nImages={images.length as 1 | 2 | 3 | 4}>
{images.map((image) => (
<Styles.ImageGridImageContainer key={image.url}>
<Styles.Image
src={image.proxy_url}
// originalUrl={image.url}
withMargin
/>
<EmbeddedImage embedImage={image} withMargin />
</Styles.ImageGridImageContainer>
))}
</Styles.Images>
Expand Down
6 changes: 5 additions & 1 deletion src/Content/Embed/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,13 @@ export const Image = styled.withConfig({
componentId: commonComponentId,
})("img", {
borderRadius: 3,
cursor: "pointer",

variants: {
clickable: {
true: {
cursor: "pointer",
},
},
withMargin: {
true: {
marginTop: theme.space.large,
Expand Down
10 changes: 5 additions & 5 deletions src/Message/MessageAuthor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function MessageAuthor({
guildId,
...props
}: MessageAuthorProps) {
const { resolveRole, resolveMember, userMentionOnClick } = useConfig();
const { resolveRole, resolveMember, userOnClick } = useConfig();
const member = guildId ? resolveMember(author.id, guildId) : null;
const isGuildMember = member !== null;

Expand Down Expand Up @@ -74,9 +74,9 @@ function MessageAuthor({
if (onlyShowUsername) {
return (
<Styles.MessageAuthor
clickable={userMentionOnClick !== undefined}
clickable={userOnClick !== undefined}
{...props}
onClick={() => userMentionOnClick?.(author)}
onClick={() => userOnClick?.(author)}
>
<Styles.Username style={{ color: dominantRoleColor }}>
{displayName}
Expand All @@ -87,9 +87,9 @@ function MessageAuthor({

return (
<Styles.MessageAuthor
clickable={userMentionOnClick !== undefined}
clickable={userOnClick !== undefined}
{...props}
onClick={() => userMentionOnClick?.(author)}
onClick={() => userOnClick?.(author)}
>
<Styles.Avatar
src={getAvatar(author, {
Expand Down
6 changes: 5 additions & 1 deletion src/core/ConfigContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ReactElement } from "react";
import { createContext, useContext } from "react";
import type {
APIChannel,
APIEmbedImage,
APIGuild,
APIGuildMember,
APIMessage,
Expand All @@ -11,6 +12,7 @@ import type {
} from "discord-api-types/v10";
import type { SvgConfig } from "./svgs";
import type { Tag } from "../ChatTag/style";
import type { APIAttachment } from "discord-api-types/v10";

export type PartialSvgConfig = Partial<SvgConfig>;

Expand Down Expand Up @@ -39,11 +41,13 @@ export type Config<SvgConfig extends PartialSvgConfig> = {
// Click handlers
currentUser(): APIUser | null;
seeThreadOnClick?(messageId: Snowflake, thread: APIChannel): void;
userMentionOnClick?(user: APIUser): void;
userOnClick?(user: APIUser): void;
roleMentionOnClick?(role: APIRole): void;
channelMentionOnClick?(channel: APIChannel): void;
openPinnedMessagesOnClick?(channel: APIChannel): void;
messageComponentButtonOnClick?(message: APIMessage, customId: string): void;
attachmentImageOnClick?(image: APIAttachment): void;
embedImageOnClick?(image: APIEmbedImage): void;
externalLinkOpenRequested?(url: string): void;
};

Expand Down
6 changes: 3 additions & 3 deletions src/markdown/render/elements/mentions/userMention.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface UserMentionProps {
}

function UserMention({ userId }: UserMentionProps) {
const { resolveUser, userMentionOnClick } = useConfig();
const { resolveUser, userOnClick } = useConfig();

// todo: resolve current channel to resolve member
const user = resolveUser(userId);
Expand All @@ -19,9 +19,9 @@ function UserMention({ userId }: UserMentionProps) {
return (
<Styles.Mention
onClick={() => {
if (user !== null) userMentionOnClick?.(user);
if (user !== null) userOnClick?.(user);
}}
canBeClicked={userMentionOnClick !== undefined}
canBeClicked={userOnClick !== undefined}
>
<Styles.MentionIcon>@</Styles.MentionIcon>
{text}
Expand Down
9 changes: 6 additions & 3 deletions src/stories/Wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -380,9 +380,7 @@ const Wrapper: Decorator = (Story) => {
seeThreadOnClick={(messageId, thread) =>
alert(`See Thread "${thread.name}" clicked on message ${messageId}`)
}
userMentionOnClick={(user) =>
alert(`User "${getDisplayName(user)}" mention clicked!`)
}
userOnClick={(user) => alert(`User "${getDisplayName(user)}" clicked!`)}
roleMentionOnClick={(role) =>
alert(`Role "${role.name}" mention clicked!`)
}
Expand All @@ -403,6 +401,11 @@ const Wrapper: Decorator = (Story) => {

return null;
}}
attachmentImageOnClick={(image) => {
alert(`Image attachment ${image.filename} clicked!`);
}}
embedImageOnClick={(embed) => {
alert(`Embed image ${embed.url} clicked!`);
externalLinkOpenRequested={(url) => {
alert(`External link "${url}" requested!`);
}}
Expand Down

0 comments on commit c2b48c5

Please sign in to comment.