mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-15 23:15:30 +01:00
🔗 feat: Enhance Share Functionality, Optimize DataTable & Fix Critical Bugs (#5220)
* 🔄 refactor: frontend and backend share link logic; feat: qrcode for share link; feat: refresh link * 🐛 fix: Conditionally render shared link and refactor share link creation logic * 🐛 fix: Correct conditional check for shareId in ShareButton component * 🔄 refactor: Update shared links API and data handling; improve query parameters and response structure * 🔄 refactor: Update shared links pagination and response structure; replace pageNumber with cursor for improved data fetching * 🔄 refactor: DataTable performance optimization * fix: delete shared link cache update * 🔄 refactor: Enhance shared links functionality; add conversationId to shared link model and update related components * 🔄 refactor: Add delete functionality to SharedLinkButton; integrate delete mutation and confirmation dialog * 🔄 feat: Add AnimatedSearchInput component with gradient animations and search functionality; update search handling in API and localization * 🔄 refactor: Improve SharedLinks component; enhance delete functionality and loading states, optimize AnimatedSearchInput, and refine DataTable scrolling behavior * fix: mutation type issues with deleted shared link mutation * fix: MutationOptions types * fix: Ensure only public shared links are retrieved in getSharedLink function * fix: `qrcode.react` install location * fix: ensure non-public shared links are not fetched when checking for existing shared links, and remove deprecated .exec() method for queries * fix: types and import order * refactor: cleanup share button UI logic, make more intuitive --------- Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
parent
460cde0c0b
commit
fa9e778399
55 changed files with 1779 additions and 1975 deletions
|
|
@ -14,10 +14,20 @@ export const messages = (conversationId: string, messageId?: string) =>
|
|||
|
||||
const shareRoot = '/api/share';
|
||||
export const shareMessages = (shareId: string) => `${shareRoot}/${shareId}`;
|
||||
export const getSharedLinks = (pageNumber: string, isPublic: boolean) =>
|
||||
`${shareRoot}?pageNumber=${pageNumber}&isPublic=${isPublic}`;
|
||||
export const createSharedLink = shareRoot;
|
||||
export const updateSharedLink = shareRoot;
|
||||
export const getSharedLink = (conversationId: string) => `${shareRoot}/link/${conversationId}`;
|
||||
export const getSharedLinks = (
|
||||
pageSize: number,
|
||||
isPublic: boolean,
|
||||
sortBy: 'title' | 'createdAt',
|
||||
sortDirection: 'asc' | 'desc',
|
||||
search?: string,
|
||||
cursor?: string,
|
||||
) =>
|
||||
`${shareRoot}?pageSize=${pageSize}&isPublic=${isPublic}&sortBy=${sortBy}&sortDirection=${sortDirection}${
|
||||
search ? `&search=${search}` : ''
|
||||
}${cursor ? `&cursor=${cursor}` : ''}`;
|
||||
export const createSharedLink = (conversationId: string) => `${shareRoot}/${conversationId}`;
|
||||
export const updateSharedLink = (shareId: string) => `${shareRoot}/${shareId}`;
|
||||
|
||||
const keysEndpoint = '/api/keys';
|
||||
|
||||
|
|
|
|||
|
|
@ -41,27 +41,29 @@ export function getSharedMessages(shareId: string): Promise<t.TSharedMessagesRes
|
|||
return request.get(endpoints.shareMessages(shareId));
|
||||
}
|
||||
|
||||
export const listSharedLinks = (
|
||||
params?: q.SharedLinkListParams,
|
||||
export const listSharedLinks = async (
|
||||
params: q.SharedLinksListParams,
|
||||
): Promise<q.SharedLinksResponse> => {
|
||||
const pageNumber = (params?.pageNumber ?? '1') || '1'; // Default to page 1 if not provided
|
||||
const isPublic = params?.isPublic ?? true; // Default to true if not provided
|
||||
return request.get(endpoints.getSharedLinks(pageNumber, isPublic));
|
||||
const { pageSize, isPublic, sortBy, sortDirection, search, cursor } = params;
|
||||
|
||||
return request.get(
|
||||
endpoints.getSharedLinks(pageSize, isPublic, sortBy, sortDirection, search, cursor),
|
||||
);
|
||||
};
|
||||
|
||||
export function getSharedLink(shareId: string): Promise<t.TSharedLinkResponse> {
|
||||
return request.get(endpoints.shareMessages(shareId));
|
||||
export function getSharedLink(conversationId: string): Promise<t.TSharedLinkGetResponse> {
|
||||
return request.get(endpoints.getSharedLink(conversationId));
|
||||
}
|
||||
|
||||
export function createSharedLink(payload: t.TSharedLinkRequest): Promise<t.TSharedLinkResponse> {
|
||||
return request.post(endpoints.createSharedLink, payload);
|
||||
export function createSharedLink(conversationId: string): Promise<t.TSharedLinkResponse> {
|
||||
return request.post(endpoints.createSharedLink(conversationId));
|
||||
}
|
||||
|
||||
export function updateSharedLink(payload: t.TSharedLinkRequest): Promise<t.TSharedLinkResponse> {
|
||||
return request.patch(endpoints.updateSharedLink, payload);
|
||||
export function updateSharedLink(shareId: string): Promise<t.TSharedLinkResponse> {
|
||||
return request.patch(endpoints.updateSharedLink(shareId));
|
||||
}
|
||||
|
||||
export function deleteSharedLink(shareId: string): Promise<t.TDeleteSharedLinkResponse> {
|
||||
export function deleteSharedLink(shareId: string): Promise<m.TDeleteSharedLinkResponse> {
|
||||
return request.delete(endpoints.shareMessages(shareId));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,29 @@ export const useGetSharedMessages = (
|
|||
);
|
||||
};
|
||||
|
||||
export const useGetSharedLinkQuery = (
|
||||
conversationId: string,
|
||||
config?: UseQueryOptions<t.TSharedLinkGetResponse>,
|
||||
): QueryObserverResult<t.TSharedLinkGetResponse> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useQuery<t.TSharedLinkGetResponse>(
|
||||
[QueryKeys.sharedLinks, conversationId],
|
||||
() => dataService.getSharedLink(conversationId),
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnMount: false,
|
||||
onSuccess: (data) => {
|
||||
queryClient.setQueryData([QueryKeys.sharedLinks, conversationId], {
|
||||
conversationId: data.conversationId,
|
||||
shareId: data.shareId,
|
||||
});
|
||||
},
|
||||
...config,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
export const useGetUserBalance = (
|
||||
config?: UseQueryOptions<string>,
|
||||
): QueryObserverResult<string> => {
|
||||
|
|
@ -306,15 +329,18 @@ export const useRegisterUserMutation = (
|
|||
options?: m.RegistrationOptions,
|
||||
): UseMutationResult<t.TError, unknown, t.TRegisterUser, unknown> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation((payload: t.TRegisterUser) => dataService.register(payload), {
|
||||
...options,
|
||||
onSuccess: (...args) => {
|
||||
queryClient.invalidateQueries([QueryKeys.user]);
|
||||
if (options?.onSuccess) {
|
||||
options.onSuccess(...args);
|
||||
}
|
||||
return useMutation<t.TRegisterUserResponse, t.TError, t.TRegisterUser>(
|
||||
(payload: t.TRegisterUser) => dataService.register(payload),
|
||||
{
|
||||
...options,
|
||||
onSuccess: (...args) => {
|
||||
queryClient.invalidateQueries([QueryKeys.user]);
|
||||
if (options?.onSuccess) {
|
||||
options.onSuccess(...args);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
};
|
||||
|
||||
export const useRefreshTokenMutation = (): UseMutationResult<
|
||||
|
|
|
|||
|
|
@ -719,13 +719,12 @@ export const tSharedLinkSchema = z.object({
|
|||
conversationId: z.string(),
|
||||
shareId: z.string(),
|
||||
messages: z.array(z.string()),
|
||||
isAnonymous: z.boolean(),
|
||||
isPublic: z.boolean(),
|
||||
isVisible: z.boolean(),
|
||||
title: z.string(),
|
||||
createdAt: z.string(),
|
||||
updatedAt: z.string(),
|
||||
});
|
||||
|
||||
export type TSharedLink = z.infer<typeof tSharedLinkSchema>;
|
||||
|
||||
export const tConversationTagSchema = z.object({
|
||||
|
|
|
|||
|
|
@ -170,15 +170,17 @@ export type TArchiveConversationResponse = TConversation;
|
|||
export type TSharedMessagesResponse = Omit<TSharedLink, 'messages'> & {
|
||||
messages: TMessage[];
|
||||
};
|
||||
export type TSharedLinkRequest = Partial<
|
||||
Omit<TSharedLink, 'messages' | 'createdAt' | 'updatedAt'>
|
||||
> & {
|
||||
conversationId: string;
|
||||
};
|
||||
|
||||
export type TSharedLinkResponse = TSharedLink;
|
||||
export type TSharedLinksResponse = TSharedLink[];
|
||||
export type TDeleteSharedLinkResponse = TSharedLink;
|
||||
export type TCreateShareLinkRequest = Pick<TConversation, 'conversationId'>;
|
||||
|
||||
export type TUpdateShareLinkRequest = Pick<TSharedLink, 'shareId'>;
|
||||
|
||||
export type TSharedLinkResponse = Pick<TSharedLink, 'shareId'> &
|
||||
Pick<TConversation, 'conversationId'>;
|
||||
|
||||
export type TSharedLinkGetResponse = TSharedLinkResponse & {
|
||||
success: boolean;
|
||||
};
|
||||
|
||||
// type for getting conversation tags
|
||||
export type TConversationTagsResponse = TConversationTag[];
|
||||
|
|
@ -203,12 +205,10 @@ export type TDuplicateConvoRequest = {
|
|||
conversationId?: string;
|
||||
};
|
||||
|
||||
export type TDuplicateConvoResponse =
|
||||
| {
|
||||
conversation: TConversation;
|
||||
messages: TMessage[];
|
||||
}
|
||||
| undefined;
|
||||
export type TDuplicateConvoResponse = {
|
||||
conversation: TConversation;
|
||||
messages: TMessage[];
|
||||
};
|
||||
|
||||
export type TForkConvoRequest = {
|
||||
messageId: string;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@ export type MutationOptions<
|
|||
onSuccess?: (data: Response, variables: Request, context?: Context) => void;
|
||||
onMutate?: (variables: Request) => Snapshot | Promise<Snapshot>;
|
||||
onError?: (error: Error, variables: Request, context?: Context, snapshot?: Snapshot) => void;
|
||||
onSettled?: (
|
||||
data: Response | undefined,
|
||||
error: Error | null,
|
||||
variables: Request,
|
||||
context?: Context,
|
||||
) => void;
|
||||
};
|
||||
|
||||
export type TGenTitleRequest = {
|
||||
|
|
@ -186,7 +192,12 @@ export type ArchiveConvoOptions = MutationOptions<
|
|||
types.TArchiveConversationRequest
|
||||
>;
|
||||
|
||||
export type DeleteSharedLinkOptions = MutationOptions<types.TSharedLink, { shareId: string }>;
|
||||
export type DeleteSharedLinkContext = { previousQueries?: Map<string, TDeleteSharedLinkResponse> };
|
||||
export type DeleteSharedLinkOptions = MutationOptions<
|
||||
TDeleteSharedLinkResponse,
|
||||
{ shareId: string },
|
||||
DeleteSharedLinkContext
|
||||
>;
|
||||
|
||||
export type TUpdatePromptContext =
|
||||
| {
|
||||
|
|
@ -298,3 +309,9 @@ export type ToolCallMutationOptions<T extends ToolId> = MutationOptions<
|
|||
ToolCallResponse,
|
||||
ToolParams<T>
|
||||
>;
|
||||
|
||||
export type TDeleteSharedLinkResponse = {
|
||||
success: boolean;
|
||||
shareId: string;
|
||||
message: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -41,23 +41,34 @@ export type ConversationUpdater = (
|
|||
export type SharedMessagesResponse = Omit<s.TSharedLink, 'messages'> & {
|
||||
messages: s.TMessage[];
|
||||
};
|
||||
export type SharedLinkListParams = Omit<ConversationListParams, 'isArchived' | 'conversationId'> & {
|
||||
isPublic?: boolean;
|
||||
|
||||
export interface SharedLinksListParams {
|
||||
pageSize: number;
|
||||
isPublic: boolean;
|
||||
sortBy: 'title' | 'createdAt';
|
||||
sortDirection: 'asc' | 'desc';
|
||||
search?: string;
|
||||
cursor?: string;
|
||||
}
|
||||
|
||||
export type SharedLinkItem = {
|
||||
shareId: string;
|
||||
title: string;
|
||||
isPublic: boolean;
|
||||
createdAt: Date;
|
||||
conversationId: string;
|
||||
};
|
||||
|
||||
export type SharedLinksResponse = Omit<ConversationListResponse, 'conversations' | 'messages'> & {
|
||||
sharedLinks: s.TSharedLink[];
|
||||
};
|
||||
export interface SharedLinksResponse {
|
||||
links: SharedLinkItem[];
|
||||
nextCursor: string | null;
|
||||
hasNextPage: boolean;
|
||||
}
|
||||
|
||||
// Type for the response from the conversation list API
|
||||
export type SharedLinkListResponse = {
|
||||
sharedLinks: s.TSharedLink[];
|
||||
pageNumber: string;
|
||||
pageSize: string | number;
|
||||
pages: string | number;
|
||||
};
|
||||
|
||||
export type SharedLinkListData = InfiniteData<SharedLinkListResponse>;
|
||||
export interface SharedLinkQueryData {
|
||||
pages: SharedLinksResponse[];
|
||||
pageParams: (string | null)[];
|
||||
}
|
||||
|
||||
export type AllPromptGroupsFilterRequest = {
|
||||
category: string;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue