Clean frontend files
This commit is contained in:
@@ -1,12 +1,9 @@
|
||||
import Strings from 'features/global/utils/strings';
|
||||
import { Message, MessageFileType } from 'features/messages/types/message';
|
||||
import FileUploadService from 'features/files/services/file-upload-service';
|
||||
import routerService from 'app/features/router/services/router-service';
|
||||
import { ChannelType } from 'app/features/channels/types/channel';
|
||||
import ChannelAPIClient from 'app/features/channels/api/channel-api-client';
|
||||
import { DriveApiClient } from 'app/features/drive/api-client/api-client';
|
||||
import { DriveItem } from 'app/features/drive/types';
|
||||
import DriveService from 'app/deprecated/Apps/Drive/Drive.js';
|
||||
import routerService from '@features/router/services/router-service';
|
||||
import { DriveApiClient } from '@features/drive/api-client/api-client';
|
||||
import { DriveItem } from '@features/drive/types';
|
||||
import { AttachedFileType } from '@features/files/types/file';
|
||||
|
||||
export const highlightText = (text?: string, highlight?: string) => {
|
||||
if (!text) {
|
||||
@@ -19,7 +16,7 @@ export const highlightText = (text?: string, highlight?: string) => {
|
||||
return Strings.removeAccents(text).replace(reg, "<span class='highlight'>$1</span>");
|
||||
};
|
||||
|
||||
export const getFileMessageDownloadRoute = (file: MessageFileType): string => {
|
||||
export const getFileMessageDownloadRoute = (file: AttachedFileType): string => {
|
||||
if (file?.metadata?.source === 'internal')
|
||||
return FileUploadService.getDownloadRoute({
|
||||
companyId: file.metadata?.external_id?.company_id,
|
||||
@@ -28,61 +25,24 @@ export const getFileMessageDownloadRoute = (file: MessageFileType): string => {
|
||||
return '';
|
||||
};
|
||||
|
||||
export const onFileDownloadClick = (file: MessageFileType) => {
|
||||
export const onFileDownloadClick = (file: AttachedFileType) => {
|
||||
const url = getFileMessageDownloadRoute(file);
|
||||
|
||||
url && (window.location.href = url);
|
||||
};
|
||||
|
||||
export const openChannel = async (channel: ChannelType, currentWorkspaceId: string) => {
|
||||
if (channel.workspace_id === 'direct') {
|
||||
const direct = await ChannelAPIClient.getDirect(
|
||||
channel.company_id || '',
|
||||
channel.members || [],
|
||||
);
|
||||
if (direct) channel = { ...channel, id: direct.id };
|
||||
}
|
||||
|
||||
routerService.push(
|
||||
routerService.generateRouteFromState({
|
||||
companyId: channel.company_id,
|
||||
workspaceId:
|
||||
(channel.workspace_id === 'direct' ? undefined : channel.workspace_id) ||
|
||||
currentWorkspaceId,
|
||||
channelId: channel.id,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
export const openMessage = async (message: Message, currentWorkspaceId: string) => {
|
||||
routerService.push(
|
||||
routerService.generateRouteFromState({
|
||||
companyId: message?.cache?.company_id,
|
||||
workspaceId:
|
||||
message?.cache?.workspace_id === 'direct'
|
||||
? currentWorkspaceId
|
||||
: message?.cache?.workspace_id,
|
||||
channelId: message?.cache?.channel_id,
|
||||
threadId: message?.thread_id,
|
||||
...(message.id !== message?.thread_id ? { messageId: message.id } : {}),
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export const openDriveItem = (driveItem: DriveItem, workspace_id: string, drive_app_id: string) => {
|
||||
routerService.push(
|
||||
routerService.generateRouteFromState({
|
||||
companyId: driveItem.company_id,
|
||||
workspaceId: workspace_id ,
|
||||
workspaceId: workspace_id,
|
||||
channelId: drive_app_id,
|
||||
}),
|
||||
);
|
||||
DriveService.changeCurrentDirectory(drive_app_id, driveItem.parent_id);
|
||||
}
|
||||
};
|
||||
|
||||
export const onDriveItemDownloadClick = async (driveItem: DriveItem) => {
|
||||
const url = await DriveApiClient.getDownloadUrl(driveItem.company_id, driveItem.id);
|
||||
|
||||
url && (window.location.href = url);
|
||||
};
|
||||
};
|
||||
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
|
||||
|
||||
.component-channel-avatar {
|
||||
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
|
||||
.channel-icon-container{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
}
|
||||
|
||||
$iconWidth: 64px;
|
||||
|
||||
.channel-icon-wrapper {
|
||||
height: $iconWidth;
|
||||
width: $iconWidth;
|
||||
border-radius: 50%;
|
||||
margin-bottom:2px;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
|
||||
.channel-icon-text {
|
||||
line-height: 64px;
|
||||
width:100%;
|
||||
font-size: 24px;
|
||||
color:white;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.channel-icon-img {
|
||||
width:$iconWidth;
|
||||
height:$iconWidth;
|
||||
object-fit: cover;
|
||||
object-position: 50% 15%;
|
||||
border-radius: 50%;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.channel-icon-wrapper:not(:first-child){
|
||||
margin-left:calc($iconWidth*2/5*-1);
|
||||
}
|
||||
|
||||
|
||||
.channel-label {
|
||||
text-align: center;
|
||||
font-size:10px;
|
||||
line-height: 1.2em;
|
||||
padding-top:4px;
|
||||
padding-bottom:4px;
|
||||
width:$iconWidth;
|
||||
|
||||
-webkit-mask-image: linear-gradient(to right, black 85%, transparent 100%);
|
||||
mask-image: linear-gradient(to right, black 85%, transparent 100%);
|
||||
overflow: hidden;
|
||||
|
||||
-webkit-mask-image: linear-gradient(to bottom, black 50%, transparent 100%);
|
||||
mask-image: linear-gradient(to bottom, black 50%, transparent 100%);
|
||||
overflow-y: hidden;
|
||||
height: 40px;
|
||||
|
||||
}
|
||||
|
||||
.channel-icon-wrapper.style0{
|
||||
background: var(--messagebubblelight);
|
||||
}
|
||||
|
||||
.channel-icon-wrapper.style1 {background: linear-gradient(129.62deg, #9F2EF4 15.98%, #F97D64 50.17%, #F6C533 82.97%);}
|
||||
.channel-icon-wrapper.style2 {background: linear-gradient(124.19deg, #0099C0 12.87%, #0099C0 49.65%, #3DD5A8 84.94%);}
|
||||
.channel-icon-wrapper.style3 {background: linear-gradient(137.24deg, #D3A6FF 15.1%, #B966B5 50.52%, #7E72C6 85.13%);}
|
||||
.channel-icon-wrapper.style4 {background: linear-gradient(124.08deg, #E2EF54 14.56%, #FF5C66 47.57%, #6248D5 83.84%);}
|
||||
.channel-icon-wrapper.style5 {background: linear-gradient(120.32deg, #FF5CF1 14.48%, #B38ADE 51.66%, #56CDDF 84.61%);}
|
||||
.channel-icon-wrapper.style6 {background: linear-gradient(120.32deg, #75C192 14.48%, #70BDA0 51.66%, #21B59C 84.61%);}
|
||||
.channel-icon-wrapper.style7 {background: linear-gradient(136.93deg, #FFA6AF 14.87%, #E62E40 50.07%, #AA0909 84.36%);}
|
||||
.channel-icon-wrapper.style8 {background: linear-gradient(136.93deg, #335F50 14.87%, #47888C 50.07%, #08C992 84.36%);}
|
||||
.channel-icon-wrapper.style9 {background: linear-gradient(135.32deg, #6CD97E 15.23%, #12B312 56.97%, #117600 84.49%);}
|
||||
.channel-icon-wrapper.style10 {background: linear-gradient(135deg, #7DF1FA 14.84%, #2BB4D6 49.93%, #008AA2 82.63%);}
|
||||
.channel-icon-wrapper.style11 {background: linear-gradient(136.26deg, #FFBF80 13.66%, #E66B2E 51.13%, #A64300 84.79%);}
|
||||
}
|
||||
|
||||
-108
@@ -1,108 +0,0 @@
|
||||
import { ChannelType } from 'features/channels/types/channel';
|
||||
import React from 'react';
|
||||
import './channel-avatar.scss';
|
||||
import UsersService from 'features/users/services/current-user-service';
|
||||
import UserService from 'features/users/services/current-user-service';
|
||||
import { UserType } from 'features/users/types/user';
|
||||
import { addApiUrlIfNeeded } from 'features/global/utils/URLUtils';
|
||||
import emoji from 'emoji-name-map';
|
||||
import _ from 'lodash';
|
||||
|
||||
type PropsType = {
|
||||
channel: ChannelType;
|
||||
showLabel: boolean;
|
||||
collapseToOne?: boolean;
|
||||
};
|
||||
|
||||
export default ({ channel, showLabel, collapseToOne }: PropsType): JSX.Element => {
|
||||
if (channel.visibility === 'direct') {
|
||||
let channelUsers = _.uniqBy(channel.users || [], 'id')
|
||||
.filter(e => e.id !== UsersService.getCurrentUserId() || channel.users?.length === 1)
|
||||
.filter(a => a);
|
||||
|
||||
if (channelUsers.length === 0) {
|
||||
channelUsers = [UsersService.getCurrentUser()];
|
||||
}
|
||||
|
||||
const channelName: string[] = [];
|
||||
|
||||
const icons = [] as JSX.Element[];
|
||||
|
||||
channelUsers.forEach(member => {
|
||||
channelName.push(UserService.getFullName(member));
|
||||
if (!(collapseToOne && icons.length > 0)) {
|
||||
icons.push(getThumbnail(member));
|
||||
}
|
||||
});
|
||||
|
||||
let width = 64;
|
||||
|
||||
if (!collapseToOne && channelUsers.length > 1) {
|
||||
const shift = 64 - (64 * 2) / 5;
|
||||
width = 64 + (channelUsers.length - 1) * shift;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="component-channel-avatar">
|
||||
<div className="channel-icon-container">{icons.map(icon => icon)}</div>
|
||||
{showLabel && (
|
||||
<div className="channel-label" style={{ width: width }}>
|
||||
{channelName.join(', ')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let thumbnail;
|
||||
const label = channel?.name;
|
||||
|
||||
if (channel.icon) {
|
||||
thumbnail = emoji.get(channel.icon);
|
||||
}
|
||||
|
||||
if (!thumbnail && channel.name) {
|
||||
thumbnail = '';
|
||||
const channelName =
|
||||
channel.name.indexOf(' ') < 0 ? channel.name.replace('_', ' ') : channel.name;
|
||||
const split = channelName.toUpperCase().split(' ');
|
||||
thumbnail += (split?.[0]?.[0] || '') + (split?.[1]?.[0] || '');
|
||||
}
|
||||
|
||||
const style = getStyle(channel.id);
|
||||
|
||||
return (
|
||||
<div className="component-channel-avatar">
|
||||
<div className={'channel-icon-wrapper' + style}>
|
||||
<div className="channel-icon-text">{thumbnail}</div>
|
||||
</div>
|
||||
{showLabel && <div className="channel-label">{label}</div>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const getStyle = (id?: string) => {
|
||||
if (!id) return '';
|
||||
let output = 0;
|
||||
for (let i = 0; i < id.length; i++) {
|
||||
output += id[i].charCodeAt(0);
|
||||
}
|
||||
const i = (output % 10) + 1;
|
||||
return ` style${i}`;
|
||||
};
|
||||
|
||||
const getThumbnail = (user: UserType) => {
|
||||
let inner;
|
||||
let style = '';
|
||||
if (user && (user.thumbnail || user.picture)) {
|
||||
const thumbnail = addApiUrlIfNeeded(user.picture || user.thumbnail || '');
|
||||
inner = <img alt={user.first_name} className="channel-icon-img" src={thumbnail} />;
|
||||
} else {
|
||||
const fullName = UserService.getFullName(user).toUpperCase().split(' ');
|
||||
const letters = (fullName?.[0]?.[0] || '?') + (fullName?.[1]?.[0] || '');
|
||||
inner = <div className="channel-icon-text">{letters}</div>;
|
||||
style = getStyle(user.id);
|
||||
}
|
||||
|
||||
return <div className={`channel-icon-wrapper${style}`}>{inner}</div>;
|
||||
};
|
||||
@@ -1,90 +0,0 @@
|
||||
import * as Text from '@atoms/text';
|
||||
import { ChannelType } from 'app/features/channels/types/channel';
|
||||
import useRouterWorkspace from 'app/features/router/hooks/use-router-workspace';
|
||||
import { useSearchModal } from 'app/features/search/hooks/use-search';
|
||||
import { SearchInputState } from 'app/features/search/state/search-input';
|
||||
import ChannelAvatar from 'app/components/search-popup/parts/channel-avatar/channel-avatar';
|
||||
import Highlighter from 'react-highlight-words';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { openChannel } from '../common';
|
||||
import { Button } from 'app/atoms/button/button';
|
||||
import Languages from 'app/features/global/services/languages-service';
|
||||
import { useChannel } from 'app/features/channels/hooks/use-channel';
|
||||
import { useChannelNotifications } from 'app/features/users/hooks/use-notifications';
|
||||
import { Badge } from '@atoms/badge';
|
||||
import UserService from 'features/users/services/current-user-service';
|
||||
import ResultContext from './result-context';
|
||||
import _ from 'lodash';
|
||||
|
||||
type PropsType = {
|
||||
channel: ChannelType;
|
||||
};
|
||||
|
||||
export default ({ channel: _channel }: PropsType): JSX.Element => {
|
||||
const channel =
|
||||
(_channel.visibility !== 'direct'
|
||||
? useChannel(_channel.id || '', {
|
||||
companyId: _channel.company_id || '',
|
||||
workspaceId: _channel.workspace_id || '',
|
||||
})?.channel
|
||||
: undefined) || _channel;
|
||||
const currentWorkspaceId = useRouterWorkspace();
|
||||
const input = useRecoilValue(SearchInputState);
|
||||
const name =
|
||||
channel.name ||
|
||||
_.uniqBy(channel.users || [], 'id')
|
||||
.filter(u => u.id != UserService.getCurrentUserId() || channel.users?.length === 1)
|
||||
.map(u => UserService.getFullName(u).trim())
|
||||
.join(', ');
|
||||
const { setOpen } = useSearchModal();
|
||||
|
||||
const notifications = useChannelNotifications(channel.id || '');
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex items-center p-2 hover:bg-zinc-50 rounded-md cursor-pointer"
|
||||
onClick={() => {
|
||||
openChannel(channel, currentWorkspaceId);
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<div className="mr-3">
|
||||
<ChannelAvatar channel={channel} showLabel={false} collapseToOne={true} />
|
||||
</div>
|
||||
<div className="grow mr-3 overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
<Text.Base className="block overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
<Highlighter
|
||||
highlightClassName="text-blue-500 p-0 bg-blue-50 overflow-hidden text-ellipsis whitespace-nowrap"
|
||||
searchWords={input?.query?.split(' ')}
|
||||
autoEscape={true}
|
||||
textToHighlight={name}
|
||||
/>
|
||||
</Text.Base>
|
||||
<Text.Info className="block">
|
||||
{channel?.stats?.members || channel.users?.length || 0} members
|
||||
</Text.Info>
|
||||
<ResultContext context={{ workspaceId: channel.workspace_id || '' }} />
|
||||
</div>
|
||||
<div className="text-right">
|
||||
{!channel.user_member && (
|
||||
<>
|
||||
<Button theme="outline">Preview</Button>
|
||||
<br />
|
||||
<Text.Info>
|
||||
{Languages.t(
|
||||
'scenes.client.channelsbar.modals.workspace_channel_list.workspace_channel_row.tag',
|
||||
)}
|
||||
</Text.Info>
|
||||
</>
|
||||
)}
|
||||
{!!channel.user_member && (notifications?.badges?.length || 0) > 0 && (
|
||||
<>
|
||||
<Badge size="sm" className="rounded-full px-2.5" theme="primary">
|
||||
{notifications.badges.length}
|
||||
</Badge>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,45 +0,0 @@
|
||||
import { ChannelType } from 'app/features/channels/types/channel';
|
||||
import useRouterWorkspace from 'app/features/router/hooks/use-router-workspace';
|
||||
import { useSearchModal } from 'app/features/search/hooks/use-search';
|
||||
import ChannelAvatar from 'app/components/search-popup/parts/channel-avatar/channel-avatar';
|
||||
import { openChannel } from '../common';
|
||||
import { useChannelNotifications } from 'app/features/users/hooks/use-notifications';
|
||||
import { useChannel } from 'app/features/channels/hooks/use-channel';
|
||||
import { Badge } from 'app/atoms/badge';
|
||||
|
||||
type PropsType = {
|
||||
channel: ChannelType;
|
||||
};
|
||||
|
||||
export default ({ channel: _channel }: PropsType): JSX.Element => {
|
||||
const channel =
|
||||
(_channel.visibility !== 'direct'
|
||||
? useChannel(_channel.id || '', {
|
||||
companyId: _channel.company_id || '',
|
||||
workspaceId: _channel.workspace_id || '',
|
||||
})?.channel
|
||||
: undefined) || _channel;
|
||||
const currentWorkspaceId = useRouterWorkspace();
|
||||
const { setOpen } = useSearchModal();
|
||||
|
||||
const notifications = useChannelNotifications(channel.id || '');
|
||||
|
||||
return (
|
||||
<div
|
||||
className="relative"
|
||||
onClick={() => {
|
||||
openChannel(channel, currentWorkspaceId);
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<ChannelAvatar channel={channel} showLabel={true} collapseToOne={true} />
|
||||
{!!channel.user_member && !!notifications?.badges?.length && (
|
||||
<>
|
||||
<Badge size="sm" className="rounded-full px-2.5 absolute -right-2 top-0" theme="primary">
|
||||
{notifications.badges.length}
|
||||
</Badge>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,8 +1,11 @@
|
||||
import * as Text from '@atoms/text';
|
||||
import FileUploadAPIClient from '@features/files/api/file-upload-api-client';
|
||||
import { Button } from 'app/atoms/button/button';
|
||||
import { DownloadIcon } from 'app/atoms/icons-agnostic';
|
||||
import { FolderIcon } from '@heroicons/react/solid';
|
||||
import { ArrowRight } from 'react-feather';
|
||||
import Highlighter from 'react-highlight-words';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { onDriveItemDownloadClick, openDriveItem } from '../common';
|
||||
import ResultContext from './result-context';
|
||||
import { Button } from '@atoms/button/button';
|
||||
import { DownloadIcon } from '@atoms/icons-agnostic';
|
||||
import {
|
||||
FileTypeArchiveIcon,
|
||||
FileTypeDocumentIcon,
|
||||
@@ -10,23 +13,20 @@ import {
|
||||
FileTypeSlidesIcon,
|
||||
FileTypeSpreadsheetIcon,
|
||||
FileTypeUnknownIcon,
|
||||
} from 'app/atoms/icons-colored';
|
||||
import { formatDate } from 'app/features/global/utils/format-date';
|
||||
import { formatSize } from 'app/features/global/utils/format-file-size';
|
||||
import { DriveItem } from 'app/features/drive/types';
|
||||
import useRouterWorkspace from 'app/features/router/hooks/use-router-workspace';
|
||||
import { useSearchModal } from 'app/features/search/hooks/use-search';
|
||||
import { SearchInputState } from 'app/features/search/state/search-input';
|
||||
import { UserType } from 'app/features/users/types/user';
|
||||
import { useFileViewerModal } from 'app/features/viewer/hooks/use-viewer';
|
||||
import Media from 'app/molecules/media';
|
||||
import Highlighter from 'react-highlight-words';
|
||||
import { useRecoilValue, useRecoilState } from 'recoil';
|
||||
import { openDriveItem, onDriveItemDownloadClick } from '../common';
|
||||
import ResultContext from './result-context';
|
||||
import { useCompanyApplications } from 'app/features/applications/hooks/use-company-applications';
|
||||
import { DriveCurrentFolderAtom } from 'app/views/client/body/drive/browser';
|
||||
import { FolderIcon } from '@heroicons/react/solid';
|
||||
} from '@atoms/icons-colored';
|
||||
import * as Text from '@atoms/text';
|
||||
import { useCompanyApplications } from '@features/applications/hooks/use-company-applications';
|
||||
import { DriveItem } from '@features/drive/types';
|
||||
import FileUploadAPIClient from '@features/files/api/file-upload-api-client';
|
||||
import { formatDate } from '@features/global/utils/format-date';
|
||||
import { formatSize } from '@features/global/utils/format-file-size';
|
||||
import useRouterWorkspace from '@features/router/hooks/use-router-workspace';
|
||||
import { useSearchModal } from '@features/search/hooks/use-search';
|
||||
import { SearchInputState } from '@features/search/state/search-input';
|
||||
import { UserType } from '@features/users/types/user';
|
||||
import { useFileViewerModal } from '@features/viewer/hooks/use-viewer';
|
||||
import Media from '@molecules/media';
|
||||
import { DriveCurrentFolderAtom } from '@views/client/body/drive/browser';
|
||||
|
||||
export default (props: { driveItem: DriveItem & { user?: UserType } }) => {
|
||||
const input = useRecoilValue(SearchInputState);
|
||||
@@ -63,12 +63,7 @@ export default (props: { driveItem: DriveItem & { user?: UserType } }) => {
|
||||
{extension?.toLocaleUpperCase()} • {formatDate(parseInt(file?.last_modified))} •{' '}
|
||||
{formatSize(file?.size)}
|
||||
</Text.Info>
|
||||
<ResultContext
|
||||
user={file.user}
|
||||
context={{
|
||||
companyId: file?.company_id,
|
||||
}}
|
||||
/>
|
||||
<ResultContext user={file.user} />
|
||||
</div>
|
||||
<div
|
||||
className="whitespace-nowrap"
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
import * as Text from '@atoms/text';
|
||||
import FileUploadAPIClient from '@features/files/api/file-upload-api-client';
|
||||
import { Button } from 'app/atoms/button/button';
|
||||
import { DownloadIcon } from 'app/atoms/icons-agnostic';
|
||||
import {
|
||||
FileTypeArchiveIcon,
|
||||
FileTypeDocumentIcon,
|
||||
FileTypePdfIcon,
|
||||
FileTypeSlidesIcon,
|
||||
FileTypeSpreadsheetIcon,
|
||||
FileTypeUnknownIcon,
|
||||
} from 'app/atoms/icons-colored';
|
||||
import { formatDate } from 'app/features/global/utils/format-date';
|
||||
import { formatSize } from 'app/features/global/utils/format-file-size';
|
||||
import { Message, MessageFileType } from 'app/features/messages/types/message';
|
||||
import useRouterWorkspace from 'app/features/router/hooks/use-router-workspace';
|
||||
import { useSearchModal } from 'app/features/search/hooks/use-search';
|
||||
import { SearchInputState } from 'app/features/search/state/search-input';
|
||||
import { UserType } from 'app/features/users/types/user';
|
||||
import { useFileViewerModal } from 'app/features/viewer/hooks/use-viewer';
|
||||
import Media from 'app/molecules/media';
|
||||
import { ArrowRight } from 'react-feather';
|
||||
import Highlighter from 'react-highlight-words';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { onFileDownloadClick, openMessage } from '../common';
|
||||
import ResultContext from './result-context';
|
||||
|
||||
export default (props: { file: MessageFileType & { message?: Message } & { user?: UserType } }) => {
|
||||
const input = useRecoilValue(SearchInputState);
|
||||
const currentWorkspaceId = useRouterWorkspace();
|
||||
const file = props.file;
|
||||
const name = file?.metadata?.name;
|
||||
const extension = name?.split('.').pop();
|
||||
|
||||
const { setOpen } = useSearchModal();
|
||||
const { open: openViewer } = useFileViewerModal();
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex items-center p-2 hover:bg-zinc-50 rounded-md cursor-pointer"
|
||||
onClick={() => openViewer(file)}
|
||||
>
|
||||
<FileResultMedia file={file} className="w-16 h-16 mr-3" />
|
||||
<div className="grow mr-3 overflow-hidden">
|
||||
<Text.Base className="block whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
<Highlighter
|
||||
highlightClassName="text-blue-500 p-0 bg-blue-50"
|
||||
searchWords={input?.query?.split(' ')}
|
||||
autoEscape={true}
|
||||
textToHighlight={name}
|
||||
/>
|
||||
</Text.Base>
|
||||
<Text.Info className="block">
|
||||
{extension?.toLocaleUpperCase()} • {formatDate(file?.message?.created_at)} •{' '}
|
||||
{formatSize(file?.metadata?.size)}
|
||||
</Text.Info>
|
||||
<ResultContext
|
||||
user={file.user}
|
||||
context={{
|
||||
channelId: file.message?.cache?.channel_id,
|
||||
workspaceId: file.message?.cache?.workspace_id,
|
||||
companyId: file.message?.cache?.company_id,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="whitespace-nowrap"
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
theme="outline"
|
||||
className="w-9 px-1.5 ml-2 rounded-full border-none"
|
||||
onClick={() => onFileDownloadClick(file)}
|
||||
>
|
||||
<DownloadIcon className="text-blue-500 w-6 h-6" />
|
||||
</Button>
|
||||
|
||||
{!!file.message && (
|
||||
<Button
|
||||
theme="outline"
|
||||
className="w-9 px-1.5 ml-2 rounded-full border-none"
|
||||
onClick={() => {
|
||||
openMessage(file.message as Message, currentWorkspaceId);
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<ArrowRight className="text-blue-500 w-6 h-6" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const FileResultMedia = (props: {
|
||||
className?: string;
|
||||
size?: 'md' | 'lg' | 'sm';
|
||||
file: MessageFileType & { message?: Message } & { user?: UserType };
|
||||
}) => {
|
||||
const file = props.file;
|
||||
|
||||
const name = file?.metadata?.name;
|
||||
const type = FileUploadAPIClient.mimeToType(file?.metadata?.mime || '');
|
||||
const url = FileUploadAPIClient.getFileThumbnailUrlFromMessageFile(file);
|
||||
const extension = name?.split('.').pop();
|
||||
|
||||
let iconClassName = 'absolute left-0 top-0 bottom-0 right-0 m-auto w-8 h-8';
|
||||
if (url) iconClassName = 'absolute bottom-1 left-1 w-6 h-6';
|
||||
|
||||
return (
|
||||
<div className={'relative flex bg-zinc-200 rounded-md ' + (props.className || '')}>
|
||||
<Media
|
||||
size={props.size || 'md'}
|
||||
url={url}
|
||||
duration={type === 'video' ? extension : undefined}
|
||||
/>
|
||||
{(!['image', 'video'].includes(type) || !url) && (
|
||||
<>
|
||||
{type === 'archive' ? (
|
||||
<FileTypeArchiveIcon className={iconClassName} />
|
||||
) : type === 'pdf' ? (
|
||||
<FileTypePdfIcon className={iconClassName} />
|
||||
) : type === 'document' ? (
|
||||
<FileTypeDocumentIcon className={iconClassName} />
|
||||
) : type === 'spreadsheet' ? (
|
||||
<FileTypeSpreadsheetIcon className={iconClassName} />
|
||||
) : type === 'slides' ? (
|
||||
<FileTypeSlidesIcon className={iconClassName} />
|
||||
) : (
|
||||
<FileTypeUnknownIcon className={iconClassName} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
import FileUploadAPIClient from '@features/files/api/file-upload-api-client';
|
||||
import { MessageFileType } from '@features/messages/types/message';
|
||||
import Media from '@molecules/media';
|
||||
import { useFileViewerModal } from 'app/features/viewer/hooks/use-viewer';
|
||||
|
||||
export default (props: { file: MessageFileType }) => {
|
||||
const file = props.file;
|
||||
const url = FileUploadAPIClient.getFileThumbnailUrlFromMessageFile(file);
|
||||
const type = FileUploadAPIClient.mimeToType(file?.metadata?.mime || '');
|
||||
const { open: openViewer } = useFileViewerModal();
|
||||
|
||||
return (
|
||||
<div
|
||||
className="cursor-pointer hover:opacity-75 inline-block m-2"
|
||||
onClick={() => openViewer(file)}
|
||||
>
|
||||
<Media
|
||||
key={file.id}
|
||||
size="lg"
|
||||
url={url}
|
||||
duration={
|
||||
type === 'video'
|
||||
? file?.metadata?.name?.split('.').slice(-1)?.[0]?.toLocaleUpperCase()
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,79 +0,0 @@
|
||||
import * as Text from '@atoms/text';
|
||||
|
||||
import { Button } from 'app/atoms/button/button';
|
||||
import { DownloadIcon } from 'app/atoms/icons-agnostic';
|
||||
import { formatDate } from 'app/features/global/utils/format-date';
|
||||
import { formatSize } from 'app/features/global/utils/format-file-size';
|
||||
import { Message, MessageFileType } from 'app/features/messages/types/message';
|
||||
import useRouterWorkspace from 'app/features/router/hooks/use-router-workspace';
|
||||
import { useSearchModal } from 'app/features/search/hooks/use-search';
|
||||
import { SearchInputState } from 'app/features/search/state/search-input';
|
||||
import { UserType } from 'app/features/users/types/user';
|
||||
import { useFileViewerModal } from 'app/features/viewer/hooks/use-viewer';
|
||||
import { ArrowRight } from 'react-feather';
|
||||
import Highlighter from 'react-highlight-words';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { onFileDownloadClick, openMessage } from '../common';
|
||||
import { FileResultMedia } from './file-result';
|
||||
|
||||
export default (props: { file: MessageFileType & { message?: Message } & { user?: UserType } }) => {
|
||||
const input = useRecoilValue(SearchInputState);
|
||||
const currentWorkspaceId = useRouterWorkspace();
|
||||
const file = props.file;
|
||||
const name = file?.metadata?.name;
|
||||
const extension = name?.split('.').pop();
|
||||
|
||||
const { setOpen } = useSearchModal();
|
||||
const { open: openViewer } = useFileViewerModal();
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex items-center p-2 rounded-md cursor-pointer"
|
||||
onClick={() => openViewer(file)}
|
||||
>
|
||||
<FileResultMedia size="sm" file={file} className=" w-12 h-12 mr-3" />
|
||||
<div className="grow mr-3 overflow-hidden">
|
||||
<Text.Base className="block whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
<Highlighter
|
||||
highlightClassName="text-blue-500 p-0 bg-blue-50"
|
||||
searchWords={input?.query?.split(' ')}
|
||||
autoEscape={true}
|
||||
textToHighlight={name}
|
||||
/>
|
||||
</Text.Base>
|
||||
<Text.Info className="block">
|
||||
{extension?.toLocaleUpperCase()} • {formatDate(file?.message?.created_at)} •{' '}
|
||||
{formatSize(file?.metadata?.size)}
|
||||
</Text.Info>
|
||||
</div>
|
||||
<div
|
||||
className="whitespace-nowrap"
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
theme="outline"
|
||||
className="w-9 px-1.5 ml-2 rounded-full border-none"
|
||||
onClick={() => onFileDownloadClick(file)}
|
||||
>
|
||||
<DownloadIcon className="text-blue-500 w-6 h-6" />
|
||||
</Button>
|
||||
|
||||
{!!file.message && (
|
||||
<Button
|
||||
theme="outline"
|
||||
className="w-9 px-1.5 ml-2 rounded-full border-none"
|
||||
onClick={() => {
|
||||
openMessage(file.message as Message, currentWorkspaceId);
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<ArrowRight className="text-blue-500 w-6 h-6" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,90 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import A from '@atoms/link';
|
||||
import * as Text from '@atoms/text';
|
||||
import { formatDate } from '@features/global/utils/format-date';
|
||||
import { Message } from '@features/messages/types/message';
|
||||
import ChannelAvatar from 'app/components/search-popup/parts/channel-avatar/channel-avatar';
|
||||
import { useChannel } from 'app/features/channels/hooks/use-channel';
|
||||
import { ChannelType } from 'app/features/channels/types/channel';
|
||||
import useRouterWorkspace from 'app/features/router/hooks/use-router-workspace';
|
||||
import { useSearchModal } from 'app/features/search/hooks/use-search';
|
||||
import { SearchInputState } from 'app/features/search/state/search-input';
|
||||
import { useUser } from 'app/features/users/hooks/use-user';
|
||||
import { UserType } from 'app/features/users/types/user';
|
||||
import { useState } from 'react';
|
||||
import Highlighter from 'react-highlight-words';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { openMessage } from '../common';
|
||||
import MessageResultFile from './message-result-file';
|
||||
import ResultContext from './result-context';
|
||||
|
||||
export default ({
|
||||
message,
|
||||
}: {
|
||||
message: Message & { users?: UserType[]; channel?: ChannelType };
|
||||
}) => {
|
||||
const input = useRecoilValue(SearchInputState);
|
||||
const currentWorkspaceId = useRouterWorkspace();
|
||||
const user =
|
||||
(message.users || []).find(u => u.id === message.user_id) || useUser(message.user_id || '');
|
||||
const channel = message.channel || useChannel(message?.cache?.channel_id || '').channel;
|
||||
const [truncated, setTruncated] = useState(true);
|
||||
const { setOpen } = useSearchModal();
|
||||
|
||||
return (
|
||||
<div
|
||||
className="w-full flex items-start p-2 hover:bg-zinc-50 rounded-md cursor-pointer"
|
||||
onClick={() => {
|
||||
openMessage(message, currentWorkspaceId);
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<div className="mr-3">
|
||||
{channel && <ChannelAvatar channel={channel} showLabel={false} collapseToOne={true} />}
|
||||
</div>
|
||||
<div className="grow mr-3 overflow-hidden">
|
||||
<div className="messages-title overflow-hidden whitespace-nowrap text-ellipsis">
|
||||
<ResultContext
|
||||
user={user}
|
||||
context={{
|
||||
companyId: message?.cache?.company_id,
|
||||
workspaceId: message?.cache?.workspace_id,
|
||||
channelId: message?.cache?.channel_id,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="messages-text">
|
||||
<Text.BaseSmall className="block whitespace-pre-line">
|
||||
<Highlighter
|
||||
highlightClassName="text-blue-500 p-0 bg-blue-50"
|
||||
searchWords={input?.query?.split(' ')}
|
||||
autoEscape={true}
|
||||
textToHighlight={message.text?.substring(0, truncated ? 500 : message.text.length)}
|
||||
/>
|
||||
{truncated && (message.text?.length || 0) > 500 && (
|
||||
<A
|
||||
className="ml-3"
|
||||
onClick={(e: any) => {
|
||||
e.stopPropagation();
|
||||
setTruncated(false);
|
||||
}}
|
||||
>
|
||||
See more
|
||||
</A>
|
||||
)}
|
||||
</Text.BaseSmall>
|
||||
{(message.files || []).map(file => (
|
||||
<div
|
||||
key={file.id}
|
||||
onClick={(e: any) => e.stopPropagation()}
|
||||
className="rounded-md border bg-white border-gray-200 hover:border-gray-300 mb-1"
|
||||
>
|
||||
<MessageResultFile file={{ ...file, message, user }} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<Text.Info className="block">{formatDate(message.created_at)}</Text.Info>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NotFoundIcon } from 'app/atoms/icons-colored';
|
||||
import Languages from 'app/features/global/services/languages-service';
|
||||
import { SearchInputState } from 'app/features/search/state/search-input';
|
||||
import { NotFoundIcon } from '@atoms/icons-colored';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import { SearchInputState } from '@features/search/state/search-input';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import * as Text from '@atoms/text';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NotFoundIcon } from 'app/atoms/icons-colored';
|
||||
import Languages from 'app/features/global/services/languages-service';
|
||||
import { NotFoundIcon } from '@atoms/icons-colored';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import * as Text from '@atoms/text';
|
||||
|
||||
export default () => {
|
||||
|
||||
@@ -1,62 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import A from '@atoms/link';
|
||||
import { getChannel } from '@features/channels/hooks/use-channel';
|
||||
import { UserType } from '@features/users/types/user';
|
||||
import { useWorkspace } from '@features/workspaces/hooks/use-workspaces';
|
||||
import UsersService from '@features/users/services/current-user-service';
|
||||
import RouterServices from '@features/router/services/router-service';
|
||||
import useRouterCompany from 'app/features/router/hooks/use-router-company';
|
||||
import useRouterWorkspace from 'app/features/router/hooks/use-router-workspace';
|
||||
import { useSearchModal } from 'app/features/search/hooks/use-search';
|
||||
|
||||
export default ({
|
||||
user,
|
||||
context,
|
||||
}: {
|
||||
user?: UserType;
|
||||
context?: { channelId?: string; workspaceId?: string; companyId?: string };
|
||||
}) => {
|
||||
const channel = getChannel(context?.channelId || '');
|
||||
|
||||
const routerWorkspace = useRouterWorkspace();
|
||||
const routerCompany = useRouterCompany();
|
||||
const workspaceId =
|
||||
(context?.workspaceId !== 'direct' ? context?.workspaceId : '') || routerWorkspace;
|
||||
const { workspace } = useWorkspace(workspaceId || '');
|
||||
const companyId = context?.companyId || routerCompany;
|
||||
|
||||
const { setOpen } = useSearchModal();
|
||||
|
||||
const workspaceName =
|
||||
context?.workspaceId === 'direct' ? 'Direct' : workspace?.name ? workspace?.name : '';
|
||||
import { UserType } from '@features/users/types/user';
|
||||
|
||||
export default ({ user }: { user?: UserType }) => {
|
||||
return (
|
||||
<div className="flex overflow-hidden whitespace-nowrap text-ellipsis">
|
||||
{!!user && <>{UsersService.getFullName(user)}</>}
|
||||
{!!user && !!(channel || workspaceName) && <span className="mx-2"> {'->'} </span>}
|
||||
{(channel || workspaceName) && (
|
||||
<>
|
||||
{!!workspaceName && <span className="opacity-50">{workspaceName}</span>}
|
||||
{!!workspaceName && !!channel?.name && <span className="mx-2"> / </span>}
|
||||
{!!channel?.name && (
|
||||
<A
|
||||
onClick={(e: any) => {
|
||||
e.stopPropagation();
|
||||
setOpen(false);
|
||||
RouterServices.push(
|
||||
RouterServices.generateRouteFromState({
|
||||
companyId: companyId || '',
|
||||
workspaceId: workspaceId || '',
|
||||
channelId: context?.channelId || '',
|
||||
}),
|
||||
);
|
||||
}}
|
||||
>
|
||||
{channel?.name}
|
||||
</A>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
import { InputDecorationIcon } from '@atoms/input/input-decoration-icon';
|
||||
import { SearchIcon } from '@heroicons/react/solid';
|
||||
import { Input } from 'app/atoms/input/input-text';
|
||||
import { Loader } from 'app/atoms/loader';
|
||||
import Languages from 'app/features/global/services/languages-service';
|
||||
import { useSearchChannelsLoading } from 'app/features/search/hooks/use-search-channels';
|
||||
import {
|
||||
useSearchMessagesFilesLoading,
|
||||
useSearchMessagesMediasLoading,
|
||||
} from 'app/features/search/hooks/use-search-files-or-medias';
|
||||
import { useSearchDriveItemsLoading } from 'app/features/search/hooks/use-search-drive-items';
|
||||
import { useSearchMessagesLoading } from 'app/features/search/hooks/use-search-messages';
|
||||
import { SearchInputState } from 'app/features/search/state/search-input';
|
||||
import _ from 'lodash';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { InputDecorationIcon } from '@atoms/input/input-decoration-icon';
|
||||
import { Input } from '@atoms/input/input-text';
|
||||
import { Loader } from '@atoms/loader';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import { useSearchDriveItemsLoading } from '@features/search/hooks/use-search-drive-items';
|
||||
import { SearchInputState } from '@features/search/state/search-input';
|
||||
|
||||
export const SearchInput = () => {
|
||||
const [input, setInput] = useRecoilState(SearchInputState);
|
||||
@@ -23,14 +16,9 @@ export const SearchInput = () => {
|
||||
if (inputElement.current) inputElement.current.focus();
|
||||
}, []);
|
||||
|
||||
const channelsLoading = useSearchChannelsLoading();
|
||||
const messagesLoading = useSearchMessagesLoading();
|
||||
const filesLoading = useSearchMessagesFilesLoading();
|
||||
const mediasLoading = useSearchMessagesMediasLoading();
|
||||
const driveItemsLoading = useSearchDriveItemsLoading();
|
||||
|
||||
const loading =
|
||||
channelsLoading || messagesLoading || filesLoading || mediasLoading || driveItemsLoading;
|
||||
const loading = driveItemsLoading;
|
||||
|
||||
return (
|
||||
<div className="relative flex mt-2 w-full">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Modal, ModalContent } from '@atoms/modal';
|
||||
import Languages from 'app/features/global/services/languages-service';
|
||||
import { useSearchModal } from 'app/features/search/hooks/use-search';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import { useSearchModal } from '@features/search/hooks/use-search';
|
||||
import { SearchInput } from './search-input';
|
||||
import { SearchResultsIndex } from './search-tabs';
|
||||
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
import * as Text from '@atoms/text';
|
||||
import A from 'app/atoms/link';
|
||||
import Languages from 'app/features/global/services/languages-service';
|
||||
import { useSearchChannels } from 'app/features/search/hooks/use-search-channels';
|
||||
import {
|
||||
useSearchMessagesFiles,
|
||||
useSearchMessagesMedias,
|
||||
} from 'app/features/search/hooks/use-search-files-or-medias';
|
||||
import { useSearchMessages } from 'app/features/search/hooks/use-search-messages';
|
||||
import { SearchInputState, SearchTabsState } from 'app/features/search/state/search-input';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { ChannelsResults, ChannelsRowResults } from './channels';
|
||||
import { FilesResults } from './files';
|
||||
import { MediasResults } from './medias';
|
||||
import { MessagesResults } from './messages';
|
||||
|
||||
export default () => {
|
||||
const setTab = useSetRecoilState(SearchTabsState);
|
||||
|
||||
const input = useRecoilValue(SearchInputState);
|
||||
const isRecent = input?.query?.trim()?.length === 0;
|
||||
|
||||
const { channels } = useSearchChannels();
|
||||
const { messages } = useSearchMessages();
|
||||
const { files } = useSearchMessagesFiles();
|
||||
const { files: medias } = useSearchMessagesMedias();
|
||||
|
||||
return (
|
||||
<div className="-mt-4">
|
||||
{channels.length > 0 && (
|
||||
<>
|
||||
<div className="flex mt-4">
|
||||
<Text.Subtitle className="block grow">
|
||||
{isRecent
|
||||
? Languages.t('components.searchpopup.recent_channels_and_contacts')
|
||||
: Languages.t('components.searchpopup.channels')}
|
||||
</Text.Subtitle>
|
||||
{channels.length > 6 && (
|
||||
<div className="w-auto">
|
||||
<A onClick={() => setTab('channels')}>
|
||||
{Languages.t('components.searchpopup.see_more')}
|
||||
</A>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{isRecent && (
|
||||
<div className="-mx-2 mt-2">
|
||||
<ChannelsRowResults max={6} />
|
||||
</div>
|
||||
)}
|
||||
{!isRecent && (
|
||||
<div className="-mx-2">
|
||||
<ChannelsResults max={6} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{medias.length > 0 && (
|
||||
<>
|
||||
<div className="flex mt-4">
|
||||
<Text.Subtitle className="block grow">
|
||||
{isRecent
|
||||
? Languages.t('components.searchpopup.recent_media')
|
||||
: Languages.t('components.searchpopup.media')}
|
||||
</Text.Subtitle>
|
||||
{medias.length > 6 && (
|
||||
<div className="w-auto">
|
||||
<A onClick={() => setTab('medias')}>
|
||||
{Languages.t('components.searchpopup.see_more')}
|
||||
</A>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={'-mx-2'}>
|
||||
<MediasResults max={6} showAsFiles={!isRecent} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{files.length > 0 && (
|
||||
<>
|
||||
<div className="flex mt-4">
|
||||
<Text.Subtitle className="block grow">
|
||||
{isRecent
|
||||
? Languages.t('components.searchpopup.recent_files')
|
||||
: Languages.t('components.searchpopup.files')}
|
||||
</Text.Subtitle>
|
||||
{files.length > 6 && (
|
||||
<div className="w-auto">
|
||||
<A onClick={() => setTab('files')}>
|
||||
{Languages.t('components.searchpopup.see_more')}
|
||||
</A>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={'-mx-2'}>
|
||||
<FilesResults max={6} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{messages.length > 0 && (
|
||||
<>
|
||||
<div className="flex mt-4">
|
||||
<Text.Subtitle className="block grow">
|
||||
{Languages.t('components.searchpopup.messages')}
|
||||
</Text.Subtitle>
|
||||
</div>
|
||||
<div className="-mx-2">
|
||||
<MessagesResults />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,87 +0,0 @@
|
||||
import * as Text from '@atoms/text';
|
||||
import Languages from 'app/features/global/services/languages-service';
|
||||
import { useSearchChannels } from 'app/features/search/hooks/use-search-channels';
|
||||
import { SearchInputState } from 'app/features/search/state/search-input';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import ChannelLineResult from '../parts/channel-line-result';
|
||||
import ChannelResult from '../parts/channel-result';
|
||||
import NothingFound from '../parts/nothing-found';
|
||||
|
||||
export default () => {
|
||||
const input = useRecoilValue(SearchInputState);
|
||||
const isRecent = input?.query?.trim()?.length === 0;
|
||||
const { channels, loading } = useSearchChannels();
|
||||
|
||||
if (channels.length === 0 && !loading) return <NothingFound />;
|
||||
|
||||
const topChannels = channels
|
||||
.slice()
|
||||
.filter(a => Date.now() - (a.last_activity || 0) < 1000 * 60 * 60 * 24 * 31)
|
||||
.sort((a, b) => Math.min(50, b?.stats?.messages || 0) - Math.min(50, a?.stats?.messages || 0))
|
||||
.slice(0, 5);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!!isRecent && !!topChannels.length && (
|
||||
<div className="mb-4">
|
||||
<Text.Subtitle className="block">
|
||||
{Languages.t('components.searchpopup.recent_channels_and_contacts')}
|
||||
</Text.Subtitle>
|
||||
|
||||
<div className="-mx-2 mt-2 items-top">
|
||||
{topChannels.map(channel => (
|
||||
<div
|
||||
className="mx-2 inline-block align-top hover:opacity-75 cursor-pointer"
|
||||
key={channel.id}
|
||||
>
|
||||
<ChannelResult channel={channel} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!!isRecent && (
|
||||
<Text.Subtitle className="block">
|
||||
{Languages.t('components.searchpopup.channels')}
|
||||
</Text.Subtitle>
|
||||
)}
|
||||
<div className={'-mx-2'}>
|
||||
<ChannelsResults />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ChannelsRowResults = (props: { max?: number }) => {
|
||||
const { channels, loading } = useSearchChannels();
|
||||
|
||||
if (channels.length === 0 && !loading) return <NothingFound />;
|
||||
|
||||
return (
|
||||
<>
|
||||
{channels.slice(0, props?.max || channels.length).map(channel => (
|
||||
<div
|
||||
className="mx-2 inline-block align-top hover:opacity-75 cursor-pointer"
|
||||
key={channel.id}
|
||||
>
|
||||
<ChannelResult channel={channel} />
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const ChannelsResults = (props: { max?: number }) => {
|
||||
const { channels, loading } = useSearchChannels();
|
||||
|
||||
if (channels.length === 0 && !loading) return <NothingFound />;
|
||||
|
||||
return (
|
||||
<>
|
||||
{channels.slice(0, props?.max || channels.length).map(channel => (
|
||||
<ChannelLineResult key={channel.id} channel={channel} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as Text from '@atoms/text';
|
||||
import Languages from 'app/features/global/services/languages-service';
|
||||
import { useSearchDriveItems } from 'app/features/search/hooks/use-search-drive-items';
|
||||
import { SearchInputState } from 'app/features/search/state/search-input';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import { useSearchDriveItems } from '@features/search/hooks/use-search-drive-items';
|
||||
import { SearchInputState } from '@features/search/state/search-input';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import DriveItemResult from '../parts/drive-item-result';
|
||||
import NothingFound from '../parts/nothing-found';
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import * as Text from '@atoms/text';
|
||||
import Languages from 'app/features/global/services/languages-service';
|
||||
import { useSearchMessagesFiles } from 'app/features/search/hooks/use-search-files-or-medias';
|
||||
import { SearchInputState } from 'app/features/search/state/search-input';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import FileResult from '../parts/file-result';
|
||||
import NothingFound from '../parts/nothing-found';
|
||||
|
||||
export default () => {
|
||||
const input = useRecoilValue(SearchInputState);
|
||||
const isRecent = input?.query?.trim()?.length === 0;
|
||||
const { files, loading } = useSearchMessagesFiles();
|
||||
|
||||
if (files.length === 0 && !loading) return <NothingFound />;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!!isRecent && (
|
||||
<Text.Subtitle className="block">
|
||||
{Languages.t('components.searchpopup.recent_files')}
|
||||
</Text.Subtitle>
|
||||
)}
|
||||
|
||||
<div className={'-mx-2'}>
|
||||
<FilesResults />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const FilesResults = (props: { max?: number }) => {
|
||||
const { files, loading } = useSearchMessagesFiles();
|
||||
|
||||
if (files.length === 0 && !loading) return <NothingFound />;
|
||||
|
||||
return (
|
||||
<>
|
||||
{files.slice(0, props?.max || files.length).map(file => (
|
||||
<FileResult key={file.id} file={file} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,51 +0,0 @@
|
||||
import * as Text from '@atoms/text';
|
||||
import FileUploadAPIClient from '@features/files/api/file-upload-api-client';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import { useSearchMessagesMedias } from '@features/search/hooks/use-search-files-or-medias';
|
||||
import { SearchInputState } from '@features/search/state/search-input';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import FileResult from '../parts/file-result';
|
||||
import MediaResult from '../parts/media-result';
|
||||
import NothingFound from '../parts/nothing-found';
|
||||
|
||||
export default () => {
|
||||
const input = useRecoilValue(SearchInputState);
|
||||
const isRecent = input?.query?.trim()?.length === 0;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!!isRecent && (
|
||||
<Text.Subtitle className="block">
|
||||
{Languages.t('components.searchpopup.recent_media')}
|
||||
</Text.Subtitle>
|
||||
)}
|
||||
|
||||
<div className="-mx-2">
|
||||
<MediasResults showAsFiles={!isRecent} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const MediasResults = (props: { max?: number; showAsFiles?: boolean }) => {
|
||||
const { files, loading } = useSearchMessagesMedias();
|
||||
|
||||
if (files.length === 0 && !loading) return <NothingFound />;
|
||||
|
||||
return (
|
||||
<>
|
||||
{files
|
||||
.slice(0, props?.max || files.length)
|
||||
.map(file => {
|
||||
const url = FileUploadAPIClient.getFileThumbnailUrlFromMessageFile(file);
|
||||
if (url || props.showAsFiles)
|
||||
return props.showAsFiles ? (
|
||||
<FileResult key={file.id} file={file} />
|
||||
) : (
|
||||
<MediaResult key={file.id} file={file} />
|
||||
);
|
||||
})
|
||||
.filter(a => a)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,41 +0,0 @@
|
||||
import { SearchInputState } from '@features/search/state/search-input';
|
||||
import { useSearchMessages } from 'app/features/search/hooks/use-search-messages';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import MessageResult from '../parts/message-result';
|
||||
import NothingFound from '../parts/nothing-found';
|
||||
import NothingSearched from '../parts/nothing-searched';
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<div>
|
||||
<div className="-mx-2">
|
||||
<MessagesResults />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const MessagesResults = () => {
|
||||
const input = useRecoilValue(SearchInputState);
|
||||
const isRecent = input?.query?.trim()?.length === 0;
|
||||
const { messages, loading } = useSearchMessages();
|
||||
|
||||
if (isRecent) return <NothingSearched />;
|
||||
if (messages.length === 0 && !loading) return <NothingFound />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="">
|
||||
{messages
|
||||
.map(message => {
|
||||
return (
|
||||
<div key={message.id} className="cursor-pointer block w-full">
|
||||
<MessageResult message={message} />
|
||||
</div>
|
||||
);
|
||||
})
|
||||
.filter(a => a)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user