mirror of https://github.com/buster-so/buster.git
Refactor chat permissions and enhance edit capabilities
- Introduced `useChatPermission` hook to manage chat permissions. - Updated components to utilize the new permission logic, allowing conditional rendering of edit options. - Adjusted `ChatInput` to display a view-only message when the user lacks edit permissions. - Cleaned up `package.json` formatting and added missing exports in context files.
This commit is contained in:
parent
eff58a11f1
commit
b1b21e30a4
|
@ -4,7 +4,7 @@ import type { IBusterChat } from '@/api/asset_interfaces';
|
|||
import { useGetChat } from '@/api/buster_rest/chats';
|
||||
import { Dropdown, type IDropdownItems } from '@/components/ui/dropdown';
|
||||
import { useGetChatId } from '@/context/Chats/useGetChatId';
|
||||
import { getIsEffectiveOwner } from '@/lib/share';
|
||||
import { canEdit, getIsEffectiveOwner } from '@/lib/share';
|
||||
import {
|
||||
useDeleteChatSelectMenu,
|
||||
useDuplicateChatSelectMenu,
|
||||
|
@ -32,6 +32,7 @@ export const ChatContainerHeaderDropdown: React.FC<{
|
|||
const deleteChat = useDeleteChatSelectMenu({ chatId });
|
||||
|
||||
const isOwnerEffective = getIsEffectiveOwner(permission);
|
||||
const canEditChat = canEdit(permission);
|
||||
|
||||
const menuItem: IDropdownItems = useMemo(() => {
|
||||
return [
|
||||
|
@ -40,10 +41,20 @@ export const ChatContainerHeaderDropdown: React.FC<{
|
|||
favoriteChat,
|
||||
openInNewTab,
|
||||
{ type: 'divider' },
|
||||
canEditChat && duplicateChat,
|
||||
canEditChat && deleteChat,
|
||||
].filter(Boolean) as IDropdownItems;
|
||||
}, [
|
||||
chatId,
|
||||
isOwnerEffective,
|
||||
canEditChat,
|
||||
shareMenu,
|
||||
renameChatTitle,
|
||||
favoriteChat,
|
||||
openInNewTab,
|
||||
duplicateChat,
|
||||
deleteChat,
|
||||
].filter(Boolean) as IDropdownItems;
|
||||
}, [chatId, duplicateChat, deleteChat, duplicateChat]);
|
||||
]);
|
||||
|
||||
return (
|
||||
<Dropdown align="end" items={menuItem}>
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export * from './useChatPermission';
|
||||
export * from './useGetActiveChat';
|
||||
export * from './useIsStreamingMessage';
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import type { ShareRole } from '@buster/server-shared/share';
|
||||
import type { IBusterChat } from '../../api/asset_interfaces';
|
||||
import { useGetChat } from '../../api/buster_rest/chats';
|
||||
|
||||
const stablePermissionSelector = (chat: IBusterChat): ShareRole => chat.permission;
|
||||
|
||||
export const useChatPermission = (chatId: string | undefined): ShareRole | undefined => {
|
||||
const { data: permission } = useGetChat(
|
||||
{ id: chatId || '' },
|
||||
{
|
||||
select: stablePermissionSelector,
|
||||
enabled: !!chatId,
|
||||
}
|
||||
);
|
||||
|
||||
return permission;
|
||||
};
|
|
@ -1,8 +1,11 @@
|
|||
import React, { type ChangeEvent, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { InputTextAreaButton } from '@/components/ui/inputs/InputTextAreaButton';
|
||||
import { useIsStreamingMessage } from '@/context/Chats/useIsStreamingMessage';
|
||||
import { Text } from '@/components/ui/typography';
|
||||
import { useChatPermission, useIsStreamingMessage } from '@/context/Chats';
|
||||
import { useGetChatId } from '@/context/Chats/useGetChatId';
|
||||
import { useIsChatMode } from '@/context/Chats/useMode';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
import { canEdit } from '@/lib/share';
|
||||
import { inputHasText } from '@/lib/text';
|
||||
import { useChatInputFlow } from '../../../../context/Chats/useChatInputFlow';
|
||||
import { AIWarning } from './AIWarning';
|
||||
|
@ -11,12 +14,15 @@ export const ChatInput: React.FC = React.memo(() => {
|
|||
const textAreaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const isStreamingMessage = useIsStreamingMessage();
|
||||
const hasChat = useIsChatMode();
|
||||
const chatId = useGetChatId();
|
||||
const permission = useChatPermission(chatId);
|
||||
const canEditChat = canEdit(permission);
|
||||
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
|
||||
const disableSubmit = useMemo(() => {
|
||||
return !inputHasText(inputValue) && !isStreamingMessage;
|
||||
}, [inputValue, isStreamingMessage]);
|
||||
return (!inputHasText(inputValue) && !isStreamingMessage) || !canEditChat;
|
||||
}, [inputValue, isStreamingMessage, canEditChat]);
|
||||
|
||||
const { onSubmitPreflight, onStopChat } = useChatInputFlow({
|
||||
disableSubmit,
|
||||
|
@ -44,6 +50,14 @@ export const ChatInput: React.FC = React.memo(() => {
|
|||
'z-10 mx-3 mt-0.5 mb-2 flex min-h-fit flex-col items-center space-y-1.5 overflow-visible'
|
||||
)}
|
||||
>
|
||||
{!canEditChat ? (
|
||||
<div className="w-full p-4 bg-muted/50 rounded-lg border">
|
||||
<Text variant="secondary" size="sm" className="text-center">
|
||||
This chat is view-only. You don't have permission to send messages.
|
||||
</Text>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<InputTextAreaButton
|
||||
placeholder="Ask Buster a question..."
|
||||
minRows={2}
|
||||
|
@ -53,13 +67,14 @@ export const ChatInput: React.FC = React.memo(() => {
|
|||
onStop={onStopChat}
|
||||
loading={isStreamingMessage}
|
||||
value={inputValue}
|
||||
disabled={!hasChat}
|
||||
disabled={!hasChat || !canEditChat}
|
||||
disabledSubmit={disableSubmit}
|
||||
autoFocus
|
||||
ref={textAreaRef}
|
||||
/>
|
||||
|
||||
<AIWarning />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -12,8 +12,10 @@ import { ThumbsDown as ThumbsDownFilled } from '@/components/ui/icons/NucleoIcon
|
|||
import { AppTooltip } from '@/components/ui/tooltip';
|
||||
import { Text } from '@/components/ui/typography';
|
||||
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||
import { useChatPermission } from '@/context/Chats';
|
||||
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
|
||||
import { formatDate } from '@/lib/date';
|
||||
import { canEdit } from '@/lib/share';
|
||||
import { timeout } from '@/lib/timeout';
|
||||
|
||||
export const ChatMessageOptions: React.FC<{
|
||||
|
@ -24,6 +26,8 @@ export const ChatMessageOptions: React.FC<{
|
|||
const { openConfirmModal } = useBusterNotifications();
|
||||
const { mutateAsync: duplicateChat, isPending: isCopying } = useDuplicateChat();
|
||||
const { mutateAsync: updateChatMessageFeedback } = useUpdateChatMessageFeedback();
|
||||
const permission = useChatPermission(chatId);
|
||||
const canEditChat = canEdit(permission);
|
||||
const { data: feedback } = useGetChatMessage(messageId, {
|
||||
select: ({ feedback }) => feedback,
|
||||
});
|
||||
|
@ -65,6 +69,7 @@ export const ChatMessageOptions: React.FC<{
|
|||
|
||||
return (
|
||||
<div className="flex items-center gap-1">
|
||||
{canEditChat && (
|
||||
<AppTooltip title="Duplicate chat from this message">
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
@ -73,6 +78,7 @@ export const ChatMessageOptions: React.FC<{
|
|||
onClick={warnBeforeDuplicate}
|
||||
/>
|
||||
</AppTooltip>
|
||||
)}
|
||||
<AppTooltip title="Report message">
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
|
|
@ -7,9 +7,11 @@ import { InputTextArea } from '@/components/ui/inputs/InputTextArea';
|
|||
import { Tooltip } from '@/components/ui/tooltip';
|
||||
import { Paragraph } from '@/components/ui/typography';
|
||||
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||
import { useChatPermission } from '@/context/Chats';
|
||||
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
|
||||
import { useMount } from '@/hooks/useMount';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
import { canEdit } from '@/lib/share';
|
||||
import { useChat } from '../../../context/Chats/useChat';
|
||||
import { MessageContainer } from './MessageContainer';
|
||||
|
||||
|
@ -22,6 +24,8 @@ export const ChatUserMessage: React.FC<{
|
|||
const { openSuccessMessage } = useBusterNotifications();
|
||||
const [isTooltipOpen, setIsTooltipOpen] = useState(false);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const permission = useChatPermission(chatId);
|
||||
const canEditChat = canEdit(permission);
|
||||
|
||||
const { sender_avatar, sender_id, sender_name, request } = requestMessage;
|
||||
|
||||
|
@ -67,7 +71,7 @@ export const ChatUserMessage: React.FC<{
|
|||
{request}
|
||||
</Paragraph>
|
||||
</div>
|
||||
{isStreamFinished && (
|
||||
{isStreamFinished && canEditChat && (
|
||||
<RequestMessageTooltip
|
||||
isTooltipOpen={isTooltipOpen}
|
||||
setIsEditing={setIsEditing}
|
||||
|
|
|
@ -66,9 +66,7 @@
|
|||
"packageManager": "pnpm@10.15.1",
|
||||
"pnpm": {
|
||||
"peerDependencyRules": {
|
||||
"ignoreMissing": [
|
||||
"shiki"
|
||||
],
|
||||
"ignoreMissing": ["shiki"],
|
||||
"allowedVersions": {
|
||||
"shiki": "3"
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { and, eq, isNull } from 'drizzle-orm';
|
||||
import { type InferSelectModel } from 'drizzle-orm';
|
||||
import type { InferSelectModel } from 'drizzle-orm';
|
||||
import { z } from 'zod';
|
||||
import { db } from '../../connection';
|
||||
import { reportFiles } from '../../schema';
|
||||
|
|
Loading…
Reference in New Issue