snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-16 01:21:02 +05:00
commit 30810eecd7
10161 changed files with 987110 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
import { Contextualbar } from '@rocket.chat/ui-client';
import type { StoryObj, Meta } from '@storybook/react';
import FileItem from './FileItem';
export default {
component: FileItem,
parameters: {
layout: 'fullscreen',
},
decorators: [(fn) => <Contextualbar height='100vh'>{fn()}</Contextualbar>],
} satisfies Meta<typeof FileItem>;
export const Default: StoryObj<typeof FileItem> = {
name: 'FileItem',
args: {
fileData: {
_id: '1',
name: 'Lorem Ipsum Indolor Dolor',
url: '#',
uploadedAt: new Date(),
user: {
_id: 'rocket.cat',
username: 'rocket.cat',
},
_updatedAt: new Date(),
},
},
};

View File

@@ -0,0 +1,70 @@
import type { IRoom, IUpload, IUploadWithUser } from '@rocket.chat/core-typings';
import { Box } from '@rocket.chat/fuselage';
import { FilePreviewIcon } from '@rocket.chat/ui-client';
import FileItemMenu from './FileItemMenu';
import ImageItem from './ImageItem';
import { getFileExtension } from '../../../../../../lib/utils/getFileExtension';
import { normalizeUsername } from '../../../../../../lib/utils/normalizeUsername';
import { useDownloadFromServiceWorker } from '../../../../../hooks/useDownloadFromServiceWorker';
import { useFormatDateAndTime } from '../../../../../hooks/useFormatDateAndTime';
import { isPreviewableImage } from '../../../../../lib/utils/isPreviewableImage';
export type FileItemProps = {
rid: IRoom['_id'];
fileData: IUploadWithUser;
onClickDelete: (id: IUpload['_id']) => void;
};
const FileItem = ({ rid, fileData, onClickDelete }: FileItemProps) => {
const format = useFormatDateAndTime();
const { _id, path, name, uploadedAt, type, typeGroup, user, description } = fileData;
const encryptedAnchorProps = useDownloadFromServiceWorker(path || '', name);
const normalizedUsername = user?.username ? normalizeUsername(user.username) : undefined;
const shouldDisplayPreview = typeGroup === 'image' && !!type && isPreviewableImage(type);
return (
<>
{shouldDisplayPreview ? (
<ImageItem id={_id} url={path} name={name} username={normalizedUsername} timestamp={format(uploadedAt)} alt={description} />
) : (
<Box
is='a'
minWidth={0}
aria-label={name}
download
rel='noopener noreferrer'
target='_blank'
title={name}
display='flex'
alignItems='center'
flexGrow={1}
flexShrink={1}
href={path}
tabIndex={-1}
textDecorationLine='none'
{...(path?.includes('/file-decrypt/') ? encryptedAnchorProps : {})}
>
<FilePreviewIcon format={getFileExtension(name)} />
<Box marginInlineStart={8} flexShrink={1} overflow='hidden'>
<Box withTruncatedText color='default' fontScale='p2m'>
{name}
</Box>
{user?.username && (
<Box withTruncatedText color='hint' fontScale='p2'>
@{normalizedUsername}
</Box>
)}
<Box color='hint' fontScale='micro'>
{format(uploadedAt)}
</Box>
</Box>
</Box>
)}
<FileItemMenu rid={rid} fileData={fileData} onClickDelete={onClickDelete} />
</>
);
};
export default FileItem;

View File

@@ -0,0 +1,93 @@
import type { IRoom, IUpload } from '@rocket.chat/core-typings';
import { Emitter } from '@rocket.chat/emitter';
import { Box } from '@rocket.chat/fuselage';
import type { GenericMenuItemProps } from '@rocket.chat/ui-client';
import { GenericMenu } from '@rocket.chat/ui-client';
import { useTranslation, useUserId } from '@rocket.chat/ui-contexts';
import { memo, useEffect, useId } from 'react';
import { download, downloadAs } from '../../../../../lib/download';
import { getURL } from '../../../../../lib/getURL';
import { useMessageDeletionIsAllowed } from '../hooks/useMessageDeletionIsAllowed';
export type FileItemMenuProps = {
rid: IRoom['_id'];
fileData: IUpload;
onClickDelete: (id: IUpload['_id']) => void;
};
const ee = new Emitter<Record<string, { result: ArrayBuffer; id: string }>>();
if ('serviceWorker' in navigator) {
navigator.serviceWorker.addEventListener('message', (event) => {
if (event.data.type === 'attachment-download-result') {
const { result } = event.data as { result: ArrayBuffer; id: string };
ee.emit(event.data.id, { result, id: event.data.id });
}
});
}
const FileItemMenu = ({ rid, fileData, onClickDelete }: FileItemMenuProps) => {
const t = useTranslation();
const userId = useUserId();
const isDeletionAllowed = useMessageDeletionIsAllowed(rid, fileData, userId);
const canDownloadFile = !fileData.encryption || 'serviceWorker' in navigator;
const { controller } = navigator?.serviceWorker || {};
const uid = useId();
useEffect(
() =>
ee.once(uid, ({ result }) => {
downloadAs({ data: [new Blob([result])] }, fileData.name ?? t('Download'));
}),
[fileData, t, uid],
);
const menuOptions = [
{
id: 'download',
content: t('Download'),
icon: 'download',
onClick: () => {
if (fileData.path?.includes('/file-decrypt/')) {
if (!controller) {
return;
}
controller?.postMessage({
type: 'attachment-download',
url: fileData.path,
id: uid,
});
return;
}
if (fileData.url && fileData.name) {
const URL = window.webkitURL ?? window.URL;
const href = getURL(fileData.url);
download(href, fileData.name);
URL.revokeObjectURL(fileData.url);
}
},
disabled: !canDownloadFile,
},
...(isDeletionAllowed && onClickDelete
? [
{
id: 'delete',
content: <Box color='status-font-on-danger'>{t('Delete')}</Box>,
onClick: () => onClickDelete(fileData._id),
icon: 'trash',
iconColor: 'status-font-on-danger',
},
]
: []),
] as GenericMenuItemProps[];
return <GenericMenu title={t('More')} aria-label={t('More')} items={menuOptions} placement='bottom-end' />;
};
export default memo(FileItemMenu);

View File

@@ -0,0 +1,48 @@
import { Avatar, Box } from '@rocket.chat/fuselage';
export type ImageItemProps = {
id: string;
url: string | undefined;
name: string | undefined;
timestamp: string;
username?: string;
alt?: string;
};
const ImageItem = ({ id, url, name, timestamp, username, alt = '' }: ImageItemProps) => {
return (
<Box
minWidth={0}
data-id={id}
className='gallery-item-container'
title={name}
display='flex'
alignItems='center'
flexGrow={1}
flexShrink={1}
>
{url && (
<Box minWidth='x48'>
<Avatar objectFit='cover' size='x48' url={url} className='gallery-item' alt={alt} />
</Box>
)}
<Box marginInlineStart={8} flexShrink={1} overflow='hidden' className='gallery-item' cursor='default'>
{name && (
<Box withTruncatedText color='default' fontScale='p2m' className='gallery-item'>
{name}
</Box>
)}
{username && (
<Box withTruncatedText color='hint' fontScale='p2' className='gallery-item'>
@{username}
</Box>
)}
<Box color='hint' fontScale='micro' className='gallery-item'>
{timestamp}
</Box>
</Box>
</Box>
);
};
export default ImageItem;