Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/components/Attachment/styling/LinkPreview.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
line-height: var(--typography-line-height-tight);

* {
color: var(--chat-text-incoming, #1a1b25);
color: var(--chat-text-incoming);
}

.str-chat__message-attachment-card--header {
Expand All @@ -32,8 +32,12 @@
flex-shrink: 0;
overflow: hidden;

img {
img,
.str-chat__image-placeholder {
border-radius: var(--message-bubble-radius-attachment-inline, 8px);
}

img {
object-fit: cover;
width: 40px;
height: 40px;
Expand Down Expand Up @@ -96,6 +100,14 @@
border-radius: 0;
}

.str-chat__message-attachment-card--header:has(.str-chat__image-placeholder) {
height: var(--str-chat__scraped-image-height);

.str-chat__image-placeholder {
border-radius: 0;
}
}

.str-chat__message-attachment-card--content {
padding: var(--spacing-sm);

Expand Down
46 changes: 29 additions & 17 deletions src/components/Gallery/BaseImage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { forwardRef, useEffect, useMemo, useState } from 'react';
import clsx from 'clsx';
import { useComponentContext } from '../../context/ComponentContext';
import { DownloadButton } from '../Attachment';
import { ImagePlaceholder as DefaultImagePlaceholder } from './ImagePlaceholder';
import { sanitizeUrl } from '@braintree/sanitize-url';

export type BaseImageProps = React.ComponentPropsWithRef<'img'> & {
Expand All @@ -12,12 +14,15 @@ export const BaseImage = forwardRef<HTMLImageElement, BaseImageProps>(function B
ref,
) {
const {
alt: propsAlt,
className: propsClassName,
onError: propsOnError,
showDownloadButtonOnError = true,
showDownloadButtonOnError = false,
...imgProps
} = props;
const [error, setError] = useState(false);
const { ImagePlaceholder: ImagePlaceholderComponent = DefaultImagePlaceholder } =
useComponentContext();

const sanitizedUrl = useMemo(() => sanitizeUrl(src), [src]);
useEffect(
Expand All @@ -27,22 +32,29 @@ export const BaseImage = forwardRef<HTMLImageElement, BaseImageProps>(function B
[sanitizedUrl],
);

if (error) {
return (
<>
<ImagePlaceholderComponent
className={clsx(propsClassName, 'str-chat__base-image--load-failed')}
/>
{showDownloadButtonOnError && <DownloadButton assetUrl={sanitizedUrl} />}
</>
);
}

return (
<>
<img
data-testid='str-chat__base-image'
{...imgProps}
className={clsx(propsClassName, 'str-chat__base-image', {
'str-chat__base-image--load-failed': error,
})}
onError={(e) => {
setError(true);
propsOnError?.(e);
}}
ref={ref}
src={sanitizedUrl}
/>
{error && showDownloadButtonOnError && <DownloadButton assetUrl={sanitizedUrl} />}
</>
<img
data-testid='str-chat__base-image'
{...imgProps}
alt={propsAlt}
className={clsx(propsClassName, 'str-chat__base-image')}
onError={(e) => {
setError(true);
propsOnError?.(e);
}}
ref={ref}
src={sanitizedUrl}
/>
);
});
22 changes: 22 additions & 0 deletions src/components/Gallery/ImagePlaceholder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import clsx from 'clsx';
import { useTranslationContext } from '../../context/TranslationContext';
import { IconImages1Alt } from '../Icons';

export type ImagePlaceholderProps = {
className?: string;
};

export const ImagePlaceholder = ({ className }: ImagePlaceholderProps) => {
const { t } = useTranslationContext('ImagePlaceholder');
return (
<div
aria-label={t('aria/Image failed to load')}
className={clsx('str-chat__image-placeholder', className)}
data-testid='str-chat__base-image-placeholder'
role='img'
>
<IconImages1Alt />
</div>
);
};
1 change: 1 addition & 0 deletions src/components/Gallery/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './BaseImage';
export * from './Gallery';
export * from './GalleryContext';
export * from './GalleryUI';
export * from './ImagePlaceholder';
8 changes: 8 additions & 0 deletions src/components/Gallery/styling/BaseImage.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.str-chat {
.str-chat__base-image {
display: block;
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
}
18 changes: 18 additions & 0 deletions src/components/Gallery/styling/ImagePlaceholder.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@use '../../../styling/utils';

.str-chat {
.str-chat__image-placeholder {
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
@include utils.flex-col-center;
background-color: var(--background-core-overlay-light);

svg {
fill: var(--accent-neutral);
width: min(var(--icon-size-lg, 32px), 50%);
height: min(var(--icon-size-lg, 32px), 50%);
}
}
}
2 changes: 2 additions & 0 deletions src/components/Gallery/styling/index.scss
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
@use 'BaseImage';
@use 'Gallery';
@use 'ImagePlaceholder';
5 changes: 4 additions & 1 deletion src/components/Message/styling/QuotedMessage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
.str-chat__quoted-message-preview {
background-color: var(--chat-bg-attachment-incoming);
}
.str-chat__quoted-message-preview--own {
}

.str-chat__message--me {
.str-chat__quoted-message-preview {
background-color: var(--chat-bg-attachment-outgoing);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// todo: should we have img dimensions determined by semantic variables?
.str-chat__link-preview-card .str-chat__image-placeholder,
.str-chat__attachment-preview__thumbnail {
border-radius: var(--radius-md);
overflow: hidden;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@
.str-chat__quoted-message-preview__image {
display: flex;
position: relative;
flex-shrink: 0;
width: 40px;
height: 40px;
min-width: 40px;
overflow: hidden;

.str-chat__attachment-preview__thumbnail__play-indicator {
display: flex;
Expand Down
3 changes: 3 additions & 0 deletions src/context/ComponentContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
type FileDragAndDropContentProps,
type GalleryProps,
type GiphyPreviewMessageProps,
type ImagePlaceholderProps,
type LoadingErrorIndicatorProps,
type LoadingIndicatorProps,
type MessageBouncePromptProps,
Expand Down Expand Up @@ -96,6 +97,8 @@ export type ComponentContextValue = {
BaseImage?: React.ComponentType<BaseImageProps>;
/** Custom UI component to display the contents of callout dialog, accepts same props as: [DefaultCalloutDialog](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Dialog/base/Callout.tsx) */
CalloutDialog?: React.ComponentType<CalloutDialogProps>;
/** Custom UI component shown instead of the image when it fails to load, defaults to and accepts same props as: [ImagePlaceholder](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Gallery/ImagePlaceholder.tsx) */
ImagePlaceholder?: React.ComponentType<ImagePlaceholderProps>;
/** Custom UI component to display set of action buttons within `ChannelPreviewMessenger` component, accepts same props as: [ChannelPreviewActionButtons](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelList/ChannelPreviewActionButtons.tsx) */
ChannelPreviewActionButtons?: React.ComponentType<ChannelPreviewActionButtonsProps>;
/** Custom UI component to display command chip, defaults to and accepts same props as: [CommandChip](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageInput/CommandChip.tsx) */
Expand Down
1 change: 1 addition & 0 deletions src/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"aria/File input": "Dateieingabe",
"aria/File upload": "Datei hochladen",
"aria/Flag Message": "Nachricht melden",
"aria/Image failed to load": "Bild konnte nicht geladen werden",
"aria/Image input": "Bildeingabe",
"aria/Load More Channels": "Mehr Kanäle laden",
"aria/Mark Message Unread": "Als ungelesen markieren",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"aria/File input": "File input",
"aria/File upload": "File upload",
"aria/Flag Message": "Flag Message",
"aria/Image failed to load": "Image failed to load",
"aria/Image input": "Image input",
"aria/Load More Channels": "Load More Channels",
"aria/Mark Message Unread": "Mark Message Unread",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"aria/File input": "Entrada de archivo",
"aria/File upload": "Carga de archivo",
"aria/Flag Message": "Marcar mensaje",
"aria/Image failed to load": "Error al cargar la imagen",
"aria/Image input": "Entrada de imagen",
"aria/Load More Channels": "Cargar más canales",
"aria/Mark Message Unread": "Marcar como no leído",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"aria/File input": "Entrée de fichier",
"aria/File upload": "Téléchargement de fichier",
"aria/Flag Message": "Signaler le message",
"aria/Image failed to load": "Échec du chargement de l'image",
"aria/Image input": "Entrée d'image",
"aria/Load More Channels": "Charger plus de canaux",
"aria/Mark Message Unread": "Marquer comme non lu",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/hi.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"aria/File input": "फ़ाइल इनपुट",
"aria/File upload": "फ़ाइल अपलोड",
"aria/Flag Message": "संदेश फ्लैग करें",
"aria/Image failed to load": "छवि लोड होने में विफल",
"aria/Image input": "छवि इनपुट",
"aria/Load More Channels": "और चैनल लोड करें",
"aria/Mark Message Unread": "अपठित चिह्नित करें",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"aria/File input": "Input di file",
"aria/File upload": "Caricamento di file",
"aria/Flag Message": "Segnala messaggio",
"aria/Image failed to load": "Caricamento immagine non riuscito",
"aria/Image input": "Input di immagine",
"aria/Load More Channels": "Carica altri canali",
"aria/Mark Message Unread": "Contrassegna come non letto",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"aria/File input": "ファイル入力",
"aria/File upload": "ファイルアップロード",
"aria/Flag Message": "メッセージをフラグ",
"aria/Image failed to load": "画像の読み込みに失敗しました",
"aria/Image input": "画像入力",
"aria/Load More Channels": "さらにチャンネルを読み込む",
"aria/Mark Message Unread": "未読としてマーク",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"aria/File input": "파일 입력",
"aria/File upload": "파일 업로드",
"aria/Flag Message": "메시지 신고",
"aria/Image failed to load": "이미지를 불러오지 못했습니다",
"aria/Image input": "이미지 입력",
"aria/Load More Channels": "더 많은 채널 불러오기",
"aria/Mark Message Unread": "읽지 않음으로 표시",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"aria/File input": "Bestandsinvoer",
"aria/File upload": "Bestand uploaden",
"aria/Flag Message": "Bericht markeren",
"aria/Image failed to load": "Afbeelding laden mislukt",
"aria/Image input": "Afbeelding invoeren",
"aria/Load More Channels": "Meer kanalen laden",
"aria/Mark Message Unread": "Markeren als ongelezen",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"aria/File input": "Entrada de arquivo",
"aria/File upload": "Carregar arquivo",
"aria/Flag Message": "Reportar mensagem",
"aria/Image failed to load": "Falha ao carregar a imagem",
"aria/Image input": "Entrada de imagem",
"aria/Load More Channels": "Carregar mais canais",
"aria/Mark Message Unread": "Marcar como não lida",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"aria/File input": "Ввод файла",
"aria/File upload": "Загрузка файла",
"aria/Flag Message": "Пожаловаться на сообщение",
"aria/Image failed to load": "Не удалось загрузить изображение",
"aria/Image input": "Ввод изображения",
"aria/Load More Channels": "Загрузить больше каналов",
"aria/Mark Message Unread": "Отметить как непрочитанное",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"aria/File input": "Dosya girişi",
"aria/File upload": "Dosya yükleme",
"aria/Flag Message": "Mesajı bayrakla",
"aria/Image failed to load": "Görsel yüklenemedi",
"aria/Image input": "Resim girişi",
"aria/Load More Channels": "Daha Fazla Kanal Yükle",
"aria/Mark Message Unread": "Okunmamış olarak işaretle",
Expand Down
Loading