mirror of https://github.com/buster-so/buster.git
started moving mutation to a stable handler
This commit is contained in:
parent
d542862e7b
commit
acac1ed700
|
@ -24,4 +24,5 @@ export interface BusterChatListItem {
|
|||
created_by_id: string;
|
||||
created_by_name: string;
|
||||
created_by_avatar: string | null;
|
||||
last_edited: string;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
} from '@tanstack/react-query';
|
||||
import type { InferBusterSocketResponseData } from './types';
|
||||
import { useBusterWebSocket } from '@/context/BusterWebSocket';
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useSocketQueryOn } from './useSocketQueryOn';
|
||||
import { timeout } from '@/utils';
|
||||
|
||||
|
@ -36,9 +36,15 @@ export const useSocketQueryEmitOn = <
|
|||
const busterSocket = useBusterWebSocket();
|
||||
const enabledTrigger = enabledTriggerProp ?? true;
|
||||
|
||||
const mutationKey = useMemo(() => {
|
||||
return ['socket-emit', ...options.queryKey];
|
||||
}, [options.queryKey]);
|
||||
|
||||
console.log(mutationKey);
|
||||
|
||||
// Use mutation for deduped socket emissions
|
||||
const { mutate: emitQueryFn } = useMutation({
|
||||
mutationKey: ['socket-emit', ...options.queryKey],
|
||||
const { mutateAsync: emitQueryFn } = useMutation({
|
||||
mutationKey,
|
||||
mutationFn: async () => {
|
||||
busterSocket.emit(emitEvent);
|
||||
await timeout(250);
|
||||
|
@ -54,6 +60,8 @@ export const useSocketQueryEmitOn = <
|
|||
const isStale =
|
||||
!queryState?.dataUpdatedAt || Date.now() - queryState.dataUpdatedAt >= staleTime;
|
||||
|
||||
console.log(isStale, queryState);
|
||||
|
||||
if (enabledTrigger && (isStale || !queryState)) {
|
||||
emitQueryFn();
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
type BusterSearchResult,
|
||||
ShareAssetType
|
||||
} from '@/api/asset_interfaces';
|
||||
import { asset_typeToIcon } from '@/app/_helpers';
|
||||
import { asset_typeToIcon } from '@/app/app/_helpers';
|
||||
import { CircleSpinnerLoaderContainer } from '@/components/loaders';
|
||||
import { BusterCollection } from '@/api/asset_interfaces';
|
||||
import { useBusterSearchContextSelector } from '@/context/Search';
|
||||
|
|
|
@ -33,17 +33,6 @@ export const NewCollectionModal: React.FC<{
|
|||
};
|
||||
}, []);
|
||||
|
||||
const memoizedFooter = useMemo(() => {
|
||||
return {
|
||||
primaryButton: {
|
||||
text: 'Create a collection',
|
||||
onClick: onCreateNewCollection,
|
||||
loading: isCreatingCollection,
|
||||
disabled: disableSubmit
|
||||
}
|
||||
};
|
||||
}, [isCreatingCollection, disableSubmit]);
|
||||
|
||||
const onCreateNewCollection = useMemoizedFn(async () => {
|
||||
if (isCreatingCollection || disableSubmit) return;
|
||||
const res = await createNewCollection({ name: title, onCollectionCreated });
|
||||
|
@ -58,6 +47,17 @@ export const NewCollectionModal: React.FC<{
|
|||
}, 200);
|
||||
});
|
||||
|
||||
const memoizedFooter = useMemo(() => {
|
||||
return {
|
||||
primaryButton: {
|
||||
text: 'Create a collection',
|
||||
onClick: onCreateNewCollection,
|
||||
loading: isCreatingCollection,
|
||||
disabled: disableSubmit
|
||||
}
|
||||
};
|
||||
}, [isCreatingCollection, disableSubmit]);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setTimeout(() => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import { ShareAssetType } from '@/api/asset_interfaces';
|
||||
import { asset_typeToTranslation } from '@/app/_helpers';
|
||||
import { asset_typeToTranslation } from '@/app/app/_helpers';
|
||||
import { BusterLogo } from '@/assets/svg/BusterLogo';
|
||||
import { Title } from '@/components/text';
|
||||
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||
|
|
|
@ -8,7 +8,7 @@ import Link from 'next/link';
|
|||
import { useParams } from 'next/navigation';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import { asset_typeToIcon } from '@/app/_helpers';
|
||||
import { asset_typeToIcon } from '@/app/app/_helpers';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
|
||||
export const FavoritesDropdown: React.FC<{}> = React.memo(() => {
|
||||
|
|
|
@ -68,7 +68,7 @@ export const CollectionListHeader: React.FC<{
|
|||
[collectionId, collectionTitle]
|
||||
);
|
||||
|
||||
useHotkeys('c', () => {
|
||||
useHotkeys('n', () => {
|
||||
setOpenNewCollectionModal(true);
|
||||
});
|
||||
|
||||
|
@ -86,7 +86,7 @@ export const CollectionListHeader: React.FC<{
|
|||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
<AppTooltip title={'Create new collection'} shortcuts={['C']}>
|
||||
<AppTooltip title={'Create new collection'} shortcuts={['N']}>
|
||||
<Button
|
||||
type="default"
|
||||
icon={<AppMaterialIcons icon="add" />}
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
import { ShareAssetType, VerificationStatus, BusterMetricListItem } from '@/api/asset_interfaces';
|
||||
import {
|
||||
getNow,
|
||||
isDateSame,
|
||||
isDateBefore,
|
||||
isDateAfter,
|
||||
makeHumanReadble,
|
||||
formatDate
|
||||
} from '@/utils';
|
||||
import { ShareAssetType, VerificationStatus, BusterChatListItem } from '@/api/asset_interfaces';
|
||||
import { makeHumanReadble, formatDate } from '@/utils';
|
||||
import React, { memo, useMemo, useRef, useState } from 'react';
|
||||
import { StatusBadgeIndicator, getShareStatus } from '../../_components/Lists';
|
||||
import { BusterUserAvatar, Text } from '@/components';
|
||||
|
@ -16,62 +9,14 @@ import { BusterListColumn, BusterListRow } from '@/components/list';
|
|||
import { ChatSelectedOptionPopup } from './ChatItemsSelectedPopup';
|
||||
import { BusterList, ListEmptyStateWithButton } from '@/components/list';
|
||||
import { FavoriteStar } from '../../_components/Lists';
|
||||
|
||||
const createLogRecord = (
|
||||
data: BusterMetricListItem[]
|
||||
): {
|
||||
TODAY: BusterMetricListItem[];
|
||||
YESTERDAY: BusterMetricListItem[];
|
||||
LAST_WEEK: BusterMetricListItem[];
|
||||
ALL_OTHERS: BusterMetricListItem[];
|
||||
} => {
|
||||
const today = getNow();
|
||||
const TODAY = data.filter((d) =>
|
||||
isDateSame({
|
||||
date: d.last_edited,
|
||||
compareDate: today,
|
||||
interval: 'day'
|
||||
})
|
||||
);
|
||||
const YESTERDAY = data.filter((d) =>
|
||||
isDateSame({
|
||||
date: d.last_edited,
|
||||
compareDate: today.subtract(1, 'day'),
|
||||
interval: 'day'
|
||||
})
|
||||
);
|
||||
const LAST_WEEK = data.filter(
|
||||
(d) =>
|
||||
isDateBefore({
|
||||
date: d.last_edited,
|
||||
compareDate: today.subtract(2, 'day').startOf('day'),
|
||||
interval: 'day'
|
||||
}) &&
|
||||
isDateAfter({
|
||||
date: d.last_edited,
|
||||
compareDate: today.subtract(8, 'day').startOf('day'),
|
||||
interval: 'day'
|
||||
})
|
||||
);
|
||||
const ALL_OTHERS = data.filter(
|
||||
(d) => !TODAY.includes(d) && !YESTERDAY.includes(d) && !LAST_WEEK.includes(d)
|
||||
);
|
||||
|
||||
return {
|
||||
TODAY,
|
||||
YESTERDAY,
|
||||
LAST_WEEK,
|
||||
ALL_OTHERS
|
||||
};
|
||||
};
|
||||
import { useCreateListByDate } from '@/components/list/useCreateListByDate';
|
||||
|
||||
export const ChatItemsContainer: React.FC<{
|
||||
metrics: BusterMetricListItem[];
|
||||
chats: BusterChatListItem[];
|
||||
className?: string;
|
||||
openNewMetricModal: () => void;
|
||||
type: 'logs' | 'metrics';
|
||||
loading: boolean;
|
||||
}> = ({ type, metrics = [], className = '', loading, openNewMetricModal }) => {
|
||||
}> = ({ chats = [], className = '', loading, openNewMetricModal }) => {
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
|
||||
const renderedDates = useRef<Record<string, string>>({});
|
||||
const renderedOwners = useRef<Record<string, React.ReactNode>>({});
|
||||
|
@ -82,9 +27,9 @@ export const ChatItemsContainer: React.FC<{
|
|||
});
|
||||
const hasSelected = selectedRowKeys.length > 0;
|
||||
|
||||
const logsRecord = useMemo(() => createLogRecord(metrics), [metrics]);
|
||||
const logsRecord = useCreateListByDate({ data: chats });
|
||||
|
||||
const metricsByDate: BusterListRow[] = useMemo(() => {
|
||||
const chatsByDate: BusterListRow[] = useMemo(() => {
|
||||
return Object.entries(logsRecord).flatMap(([key, metrics]) => {
|
||||
const records = metrics.map((metric) => ({
|
||||
id: metric.id,
|
||||
|
@ -95,9 +40,9 @@ export const ChatItemsContainer: React.FC<{
|
|||
})
|
||||
}));
|
||||
const hasRecords = records.length > 0;
|
||||
if (!hasRecords) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!hasRecords) return [];
|
||||
|
||||
return [
|
||||
{
|
||||
id: key,
|
||||
|
@ -160,18 +105,18 @@ export const ChatItemsContainer: React.FC<{
|
|||
[]
|
||||
);
|
||||
|
||||
console.log(chatsByDate, loading);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={tableContainerRef}
|
||||
className={`${className} relative flex h-full flex-col items-center`}>
|
||||
<BusterList
|
||||
rows={metricsByDate}
|
||||
rows={chatsByDate}
|
||||
columns={columns}
|
||||
onSelectChange={onSelectChange}
|
||||
selectedRowKeys={selectedRowKeys}
|
||||
emptyState={
|
||||
<EmptyState loading={loading} type={type} openNewMetricModal={openNewMetricModal} />
|
||||
}
|
||||
emptyState={<EmptyState loading={loading} openNewMetricModal={openNewMetricModal} />}
|
||||
/>
|
||||
|
||||
<ChatSelectedOptionPopup
|
||||
|
@ -185,36 +130,23 @@ export const ChatItemsContainer: React.FC<{
|
|||
|
||||
const EmptyState: React.FC<{
|
||||
loading: boolean;
|
||||
type: 'logs' | 'metrics';
|
||||
openNewMetricModal: () => void;
|
||||
}> = React.memo(({ loading, type, openNewMetricModal }) => {
|
||||
}> = React.memo(({ loading, openNewMetricModal }) => {
|
||||
if (loading) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return <ChatsEmptyState openNewMetricModal={openNewMetricModal} type={type} />;
|
||||
return <ChatsEmptyState openNewMetricModal={openNewMetricModal} />;
|
||||
});
|
||||
EmptyState.displayName = 'EmptyState';
|
||||
|
||||
const ChatsEmptyState: React.FC<{
|
||||
openNewMetricModal: () => void;
|
||||
type: 'logs' | 'metrics';
|
||||
}> = ({ openNewMetricModal, type }) => {
|
||||
if (type === 'logs') {
|
||||
return (
|
||||
<ListEmptyStateWithButton
|
||||
title="You don't have any logs yet."
|
||||
description="You don't have any logs. As soon as you do, they will start to appear here."
|
||||
buttonText="New chat"
|
||||
onClick={openNewMetricModal}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
}> = ({ openNewMetricModal }) => {
|
||||
return (
|
||||
<ListEmptyStateWithButton
|
||||
title="You don't have any metrics yet."
|
||||
description="You don't have any metrics. As soon as you do, they will start to appear here."
|
||||
title="You don't have any chats yet."
|
||||
description="You don't have any chats. As soon as you do, they will start to appear here."
|
||||
buttonText="New chat"
|
||||
onClick={openNewMetricModal}
|
||||
/>
|
||||
|
|
|
@ -4,8 +4,7 @@ import React, { useState } from 'react';
|
|||
import { AppContent } from '../../../../components/layout/AppContent';
|
||||
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
|
||||
import { useMemoizedFn, useMount } from 'ahooks';
|
||||
import { VerificationStatus } from '@/api/asset_interfaces';
|
||||
import { useBusterMetricListByFilter } from '@/context/Metrics';
|
||||
import { useBusterChatListByFilter } from '@/context/Chats';
|
||||
import { ChatListHeader } from './ChatListHeader';
|
||||
import { ChatItemsContainer } from './ChatItemsContainer';
|
||||
|
||||
|
@ -13,21 +12,20 @@ export const ChatListContainer: React.FC<{
|
|||
className?: string;
|
||||
}> = ({ className = '' }) => {
|
||||
const onToggleChatsModal = useAppLayoutContextSelector((s) => s.onToggleChatsModal);
|
||||
const [filters, setFilters] = useState<VerificationStatus[]>([]);
|
||||
const type = 'logs';
|
||||
|
||||
const adminView = type === 'logs';
|
||||
// const { list, fetched } = useBusterMetricListByFilter({
|
||||
// filters,
|
||||
// admin_view: adminView
|
||||
// });
|
||||
|
||||
const onSetFilters = useMemoizedFn((newFilters: VerificationStatus[]) => {
|
||||
setFilters(newFilters);
|
||||
const [filters, setFilters] = useState<Parameters<typeof useBusterChatListByFilter>[0]>({
|
||||
admin_view: false
|
||||
});
|
||||
|
||||
const { list, isFetched } = useBusterChatListByFilter(filters);
|
||||
|
||||
const onSetFilters = useMemoizedFn(
|
||||
(newFilters: Parameters<typeof useBusterChatListByFilter>[0]) => {
|
||||
setFilters(newFilters);
|
||||
}
|
||||
);
|
||||
|
||||
useMount(async () => {
|
||||
onSetFilters([]);
|
||||
onSetFilters({ admin_view: false });
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -35,9 +33,8 @@ export const ChatListContainer: React.FC<{
|
|||
<ChatListHeader />
|
||||
<AppContent>
|
||||
<ChatItemsContainer
|
||||
type={type}
|
||||
metrics={[]}
|
||||
loading={false}
|
||||
chats={list}
|
||||
loading={!isFetched}
|
||||
openNewMetricModal={onToggleChatsModal}
|
||||
className="flex-col overflow-hidden"
|
||||
/>
|
||||
|
|
|
@ -5,8 +5,6 @@ import { AppContentHeader } from '../../../../components/layout/AppContentHeader
|
|||
import { Text } from '@/components/text';
|
||||
|
||||
export const ChatListHeader: React.FC<{}> = ({}) => {
|
||||
const showFilters: boolean = true;
|
||||
|
||||
return (
|
||||
<AppContentHeader>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
import { ShareAssetType, VerificationStatus, BusterMetricListItem } from '@/api/asset_interfaces';
|
||||
import {
|
||||
getNow,
|
||||
isDateSame,
|
||||
isDateBefore,
|
||||
isDateAfter,
|
||||
makeHumanReadble,
|
||||
formatDate
|
||||
} from '@/utils';
|
||||
import { makeHumanReadble, formatDate } from '@/utils';
|
||||
import React, { memo, useMemo, useRef, useState } from 'react';
|
||||
import { StatusBadgeIndicator, getShareStatus } from '../../_components/Lists';
|
||||
import { BusterUserAvatar, Text } from '@/components';
|
||||
|
@ -16,54 +9,7 @@ import { BusterListColumn, BusterListRow } from '@/components/list';
|
|||
import { MetricSelectedOptionPopup } from './MetricItemsSelectedPopup';
|
||||
import { BusterList, ListEmptyStateWithButton } from '@/components/list';
|
||||
import { FavoriteStar } from '../../_components/Lists';
|
||||
|
||||
const createLogRecord = (
|
||||
data: BusterMetricListItem[]
|
||||
): {
|
||||
TODAY: BusterMetricListItem[];
|
||||
YESTERDAY: BusterMetricListItem[];
|
||||
LAST_WEEK: BusterMetricListItem[];
|
||||
ALL_OTHERS: BusterMetricListItem[];
|
||||
} => {
|
||||
const today = getNow();
|
||||
const TODAY = data.filter((d) =>
|
||||
isDateSame({
|
||||
date: d.last_edited,
|
||||
compareDate: today,
|
||||
interval: 'day'
|
||||
})
|
||||
);
|
||||
const YESTERDAY = data.filter((d) =>
|
||||
isDateSame({
|
||||
date: d.last_edited,
|
||||
compareDate: today.subtract(1, 'day'),
|
||||
interval: 'day'
|
||||
})
|
||||
);
|
||||
const LAST_WEEK = data.filter(
|
||||
(d) =>
|
||||
isDateBefore({
|
||||
date: d.last_edited,
|
||||
compareDate: today.subtract(2, 'day').startOf('day'),
|
||||
interval: 'day'
|
||||
}) &&
|
||||
isDateAfter({
|
||||
date: d.last_edited,
|
||||
compareDate: today.subtract(8, 'day').startOf('day'),
|
||||
interval: 'day'
|
||||
})
|
||||
);
|
||||
const ALL_OTHERS = data.filter(
|
||||
(d) => !TODAY.includes(d) && !YESTERDAY.includes(d) && !LAST_WEEK.includes(d)
|
||||
);
|
||||
|
||||
return {
|
||||
TODAY,
|
||||
YESTERDAY,
|
||||
LAST_WEEK,
|
||||
ALL_OTHERS
|
||||
};
|
||||
};
|
||||
import { useCreateListByDate } from '@/components/list/useCreateListByDate';
|
||||
|
||||
export const MetricItemsContainer: React.FC<{
|
||||
metrics: BusterMetricListItem[];
|
||||
|
@ -82,7 +28,7 @@ export const MetricItemsContainer: React.FC<{
|
|||
});
|
||||
const hasSelected = selectedRowKeys.length > 0;
|
||||
|
||||
const logsRecord = useMemo(() => createLogRecord(metrics), [metrics]);
|
||||
const logsRecord = useCreateListByDate({ data: metrics });
|
||||
|
||||
const metricsByDate: BusterListRow[] = useMemo(() => {
|
||||
return Object.entries(logsRecord).flatMap(([key, metrics]) => {
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
import { getNow, isDateAfter, isDateBefore, isDateSame } from '@/utils/date';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
type ListItem = {
|
||||
id: string;
|
||||
last_edited: string;
|
||||
};
|
||||
|
||||
const createChatRecord = <T extends ListItem>(
|
||||
data: T[]
|
||||
): {
|
||||
TODAY: T[];
|
||||
YESTERDAY: T[];
|
||||
LAST_WEEK: T[];
|
||||
ALL_OTHERS: T[];
|
||||
} => {
|
||||
const today = getNow();
|
||||
const TODAY = data.filter((d) =>
|
||||
isDateSame({
|
||||
date: d.last_edited,
|
||||
compareDate: today,
|
||||
interval: 'day'
|
||||
})
|
||||
);
|
||||
const YESTERDAY = data.filter((d) =>
|
||||
isDateSame({
|
||||
date: d.last_edited,
|
||||
compareDate: today.subtract(1, 'day'),
|
||||
interval: 'day'
|
||||
})
|
||||
);
|
||||
const LAST_WEEK = data.filter(
|
||||
(d) =>
|
||||
isDateBefore({
|
||||
date: d.last_edited,
|
||||
compareDate: today.subtract(2, 'day').startOf('day'),
|
||||
interval: 'day'
|
||||
}) &&
|
||||
isDateAfter({
|
||||
date: d.last_edited,
|
||||
compareDate: today.subtract(8, 'day').startOf('day'),
|
||||
interval: 'day'
|
||||
})
|
||||
);
|
||||
const ALL_OTHERS = data.filter(
|
||||
(d) => !TODAY.includes(d) && !YESTERDAY.includes(d) && !LAST_WEEK.includes(d)
|
||||
);
|
||||
|
||||
return {
|
||||
TODAY,
|
||||
YESTERDAY,
|
||||
LAST_WEEK,
|
||||
ALL_OTHERS
|
||||
};
|
||||
};
|
||||
|
||||
export const useCreateListByDate = <T extends ListItem>({ data }: { data: T[] }) => {
|
||||
const listRecord = useMemo(() => createChatRecord(data), [data]);
|
||||
|
||||
return listRecord;
|
||||
};
|
|
@ -1,12 +1,20 @@
|
|||
import { useSocketQueryEmitOn } from '@/api/buster_socket_query';
|
||||
import { queryKeys } from '@/api/query_keys';
|
||||
import { GetChatListParams } from '@/api/request_interfaces/chats';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useBusterChatListByFilter = (
|
||||
filtersProp: Omit<GetChatListParams, 'page_token' | 'page_size'>
|
||||
) => {
|
||||
const filters = useMemo(
|
||||
() => ({ ...filtersProp, page_token: 0, page_size: 3000 }),
|
||||
[filtersProp]
|
||||
);
|
||||
|
||||
export const useBusterChatListByFilter = (filters: GetChatListParams) => {
|
||||
const { data: chatsList, isFetched: isFetchedChatsList } = useSocketQueryEmitOn({
|
||||
emitEvent: {
|
||||
route: '/chats/list',
|
||||
payload: { page_token: 0, page_size: 3000, admin_view: false }
|
||||
payload: filters
|
||||
},
|
||||
responseEvent: '/chats/list:getChatsList',
|
||||
options: queryKeys['chatsGetList'](filters)
|
||||
|
@ -15,7 +23,7 @@ export const useBusterChatListByFilter = (filters: GetChatListParams) => {
|
|||
//ACTIONS
|
||||
|
||||
return {
|
||||
list: chatsList,
|
||||
list: chatsList || [],
|
||||
isFetched: isFetchedChatsList
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue