-
Notifications
You must be signed in to change notification settings - Fork 887
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[AI Chat] fix issues with conversation context menu #26990
base: master
Are you sure you want to change the base?
Changes from all commits
a3df109
0c5cf4b
993d18f
dd1e08f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -61,7 +61,8 @@ constexpr auto kAllowedSchemes = base::MakeFixedFlatSet<std::string_view>( | |||||||||||||||||||||||||||||||||||||||||||
{url::kHttpsScheme, url::kHttpScheme, url::kFileScheme, url::kDataScheme}); | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
std::vector<mojom::Conversation*> FilterVisibleConversations( | ||||||||||||||||||||||||||||||||||||||||||||
std::map<std::string, mojom::ConversationPtr>& conversations_map) { | ||||||||||||||||||||||||||||||||||||||||||||
std::map<std::string, mojom::ConversationPtr, std::less<>>& | ||||||||||||||||||||||||||||||||||||||||||||
conversations_map) { | ||||||||||||||||||||||||||||||||||||||||||||
std::vector<mojom::Conversation*> conversations; | ||||||||||||||||||||||||||||||||||||||||||||
for (const auto& kv : conversations_map) { | ||||||||||||||||||||||||||||||||||||||||||||
auto& conversation = kv.second; | ||||||||||||||||||||||||||||||||||||||||||||
|
@@ -598,14 +599,7 @@ void AIChatService::DeleteConversation(const std::string& id) { | |||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
void AIChatService::RenameConversation(const std::string& id, | ||||||||||||||||||||||||||||||||||||||||||||
const std::string& new_name) { | ||||||||||||||||||||||||||||||||||||||||||||
ConversationHandler* conversation_handler = | ||||||||||||||||||||||||||||||||||||||||||||
conversation_handlers_.at(id).get(); | ||||||||||||||||||||||||||||||||||||||||||||
if (!conversation_handler) { | ||||||||||||||||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
DVLOG(1) << "Renamed conversation " << id << " to '" << new_name << "'"; | ||||||||||||||||||||||||||||||||||||||||||||
OnConversationTitleChanged(conversation_handler, new_name); | ||||||||||||||||||||||||||||||||||||||||||||
OnConversationTitleChanged(id, new_name); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
void AIChatService::ConversationExists(const std::string& conversation_uuid, | ||||||||||||||||||||||||||||||||||||||||||||
|
@@ -814,20 +808,25 @@ void AIChatService::OnClientConnectionChanged(ConversationHandler* handler) { | |||||||||||||||||||||||||||||||||||||||||||
MaybeUnloadConversation(handler); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
void AIChatService::OnConversationTitleChanged(ConversationHandler* handler, | ||||||||||||||||||||||||||||||||||||||||||||
std::string title) { | ||||||||||||||||||||||||||||||||||||||||||||
auto conversation_it = conversations_.find(handler->get_conversation_uuid()); | ||||||||||||||||||||||||||||||||||||||||||||
CHECK(conversation_it != conversations_.end()); | ||||||||||||||||||||||||||||||||||||||||||||
auto& conversation = conversation_it->second; | ||||||||||||||||||||||||||||||||||||||||||||
conversation->title = title; | ||||||||||||||||||||||||||||||||||||||||||||
void AIChatService::OnConversationTitleChanged( | ||||||||||||||||||||||||||||||||||||||||||||
const std::string& conversation_uuid, | ||||||||||||||||||||||||||||||||||||||||||||
const std::string& new_title) { | ||||||||||||||||||||||||||||||||||||||||||||
auto conversation_it = conversations_.find(conversation_uuid); | ||||||||||||||||||||||||||||||||||||||||||||
if (conversation_it == conversations_.end()) { | ||||||||||||||||||||||||||||||||||||||||||||
DLOG(ERROR) << "Conversation not found for title change"; | ||||||||||||||||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
auto& conversation_metadata = conversation_it->second; | ||||||||||||||||||||||||||||||||||||||||||||
conversation_metadata->title = new_title; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
OnConversationListChanged(); | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
// Persist the change | ||||||||||||||||||||||||||||||||||||||||||||
if (ai_chat_db_) { | ||||||||||||||||||||||||||||||||||||||||||||
ai_chat_db_ | ||||||||||||||||||||||||||||||||||||||||||||
.AsyncCall(base::IgnoreResult(&AIChatDatabase::UpdateConversationTitle)) | ||||||||||||||||||||||||||||||||||||||||||||
.WithArgs(handler->get_conversation_uuid(), std::move(title)); | ||||||||||||||||||||||||||||||||||||||||||||
.WithArgs(conversation_uuid, new_title); | ||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+820
to
+829
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if we just take a
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -62,46 +62,48 @@ interface DisplayTitleProps { | |
} | ||
|
||
function DisplayTitle(props: DisplayTitleProps) { | ||
const [isButtonMenuVisible, setIsButtonMenuVisible] = React.useState(false) | ||
const [isOptionsMenuOpen, setIsOptionsMenuOpen] = React.useState(false) | ||
|
||
const handleButtonMenuChange = React.useCallback((e: {isOpen: boolean}) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. optional: with stuff like this where you're at a leaf node, I think its okay to not wrap things in a Up to you on this one There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just wasn't sure if our web component would cause a new listener to be added (and the old one removed) on every single render. But I suppose we're doing that everywhere anyway There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It will 😆 - but its the same with every other component that does this so 🤷 And with React compiler it will (probably) Just Work (tm) |
||
setIsOptionsMenuOpen(e.isOpen) | ||
}, []) | ||
|
||
return ( | ||
<div | ||
className={styles.displayTitle} | ||
onMouseEnter={() => setIsButtonMenuVisible(true)} | ||
onMouseLeave={() => setIsButtonMenuVisible(false)} | ||
className={classnames(styles.displayTitle, isOptionsMenuOpen && styles.isOptionsMenuOpen)} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a handy util taher added! |
||
> | ||
<div className={styles.displayTitleContent}> | ||
<div | ||
className={styles.text} | ||
onDoubleClick={props.onEditTitle} | ||
title={props.title} | ||
> | ||
{props.title} | ||
</div> | ||
<div className={styles.description}>{props.description}</div> | ||
</div> | ||
{isButtonMenuVisible && ( | ||
<ButtonMenu className={styles.optionsMenu}> | ||
<div | ||
slot='anchor-content' | ||
className={styles.optionsButton} | ||
> | ||
<Icon name='more-vertical' /> | ||
<ButtonMenu | ||
className={styles.optionsMenu} | ||
onChange={handleButtonMenuChange} | ||
> | ||
<div | ||
slot='anchor-content' | ||
className={styles.optionsButton} | ||
> | ||
<Icon name='more-vertical' /> | ||
</div> | ||
<leo-menu-item onClick={props.onEditTitle}> | ||
<div className={styles.optionsMenuItemWithIcon}> | ||
<Icon name='edit-pencil' /> | ||
<div>{getLocale('menuRenameConversation')}</div> | ||
</div> | ||
<leo-menu-item onClick={props.onEditTitle}> | ||
<div className={styles.optionsMenuItemWithIcon}> | ||
<Icon name='edit-pencil' /> | ||
<div>{getLocale('menuRenameConversation')}</div> | ||
</div> | ||
</leo-menu-item> | ||
<leo-menu-item onClick={props.onDelete}> | ||
<div className={styles.optionsMenuItemWithIcon}> | ||
<Icon name='trash' /> | ||
<div>{getLocale('menuDeleteConversation')}</div> | ||
</div> | ||
</leo-menu-item> | ||
</ButtonMenu> | ||
)} | ||
</leo-menu-item> | ||
<leo-menu-item onClick={props.onDelete}> | ||
<div className={styles.optionsMenuItemWithIcon}> | ||
<Icon name='trash' /> | ||
<div>{getLocale('menuDeleteConversation')}</div> | ||
</div> | ||
</leo-menu-item> | ||
</ButtonMenu> | ||
</div> | ||
) | ||
} | ||
|
@@ -140,19 +142,30 @@ export default function ConversationsList(props: ConversationsListProps) { | |
{aiChatContext.visibleConversations.length > 0 && | ||
<ol> | ||
{aiChatContext.visibleConversations.map(item => { | ||
const isEditing = aiChatContext.editingConversationId === item.uuid | ||
return ( | ||
<li key={item.uuid}> | ||
<a | ||
className={classnames({ | ||
[styles.navItem]: true, | ||
[styles.navItemActive]: item.uuid === conversationContext.conversationUuid | ||
})} | ||
onClick={() => { | ||
onClick={(e) => { | ||
if (isEditing) { | ||
e.preventDefault() | ||
} | ||
props.setIsConversationsListOpen?.(false) | ||
}} | ||
onDoubleClick={() => aiChatContext.setEditingConversationId(item.uuid)} | ||
href={`/${item.uuid}`} | ||
> | ||
{item.uuid === aiChatContext.editingConversationId ? ( | ||
<DisplayTitle | ||
title={item.title || getLocale('conversationListUntitled')} | ||
description='' | ||
onEditTitle={() => aiChatContext.setEditingConversationId(item.uuid)} | ||
onDelete={() => getAPI().service.deleteConversation(item.uuid)} | ||
/> | ||
{item.uuid === aiChatContext.editingConversationId && ( | ||
<div className={styles.editibleTitle}> | ||
<SimpleInput | ||
text={item.title} | ||
|
@@ -163,13 +176,6 @@ export default function ConversationsList(props: ConversationsListProps) { | |
}} | ||
/> | ||
</div> | ||
) : ( | ||
<DisplayTitle | ||
title={item.title || getLocale('conversationListUntitled')} | ||
description='' | ||
onEditTitle={() => aiChatContext.setEditingConversationId(item.uuid)} | ||
onDelete={() => getAPI().service.deleteConversation(item.uuid)} | ||
/> | ||
)} | ||
</a> | ||
</li> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this just take a
std::string
so we can move thenew_title
into this function?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as discussed in the slack thread, doesn't seem like it's worth the optimization