feat: add pinned, shared and archived tags functionality for chat search moda
Co-Authored-By: G30 <50341825+silentoplayz@users.noreply.github.com>
This commit is contained in:
parent
d1a654019b
commit
041da26756
|
@ -617,8 +617,34 @@ class ChatTable:
|
||||||
if word.startswith("tag:")
|
if word.startswith("tag:")
|
||||||
]
|
]
|
||||||
|
|
||||||
|
is_pinned = None
|
||||||
|
if "pinned:true" in search_text_words:
|
||||||
|
is_pinned = True
|
||||||
|
elif "pinned:false" in search_text_words:
|
||||||
|
is_pinned = False
|
||||||
|
|
||||||
|
is_archived = None
|
||||||
|
if "archived:true" in search_text_words:
|
||||||
|
is_archived = True
|
||||||
|
elif "archived:false" in search_text_words:
|
||||||
|
is_archived = False
|
||||||
|
|
||||||
|
is_shared = None
|
||||||
|
if "shared:true" in search_text_words:
|
||||||
|
is_shared = True
|
||||||
|
elif "shared:false" in search_text_words:
|
||||||
|
is_shared = False
|
||||||
|
|
||||||
search_text_words = [
|
search_text_words = [
|
||||||
word for word in search_text_words if not word.startswith("tag:")
|
word
|
||||||
|
for word in search_text_words
|
||||||
|
if (
|
||||||
|
not word.startswith("tag:")
|
||||||
|
and not word.startswith("folder:")
|
||||||
|
and not word.startswith("pinned:")
|
||||||
|
and not word.startswith("archived:")
|
||||||
|
and not word.startswith("shared:")
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
search_text = " ".join(search_text_words)
|
search_text = " ".join(search_text_words)
|
||||||
|
@ -626,9 +652,20 @@ class ChatTable:
|
||||||
with get_db() as db:
|
with get_db() as db:
|
||||||
query = db.query(Chat).filter(Chat.user_id == user_id)
|
query = db.query(Chat).filter(Chat.user_id == user_id)
|
||||||
|
|
||||||
if not include_archived:
|
if is_archived is not None:
|
||||||
|
query = query.filter(Chat.archived == is_archived)
|
||||||
|
elif not include_archived:
|
||||||
query = query.filter(Chat.archived == False)
|
query = query.filter(Chat.archived == False)
|
||||||
|
|
||||||
|
if is_pinned is not None:
|
||||||
|
query = query.filter(Chat.pinned == is_pinned)
|
||||||
|
|
||||||
|
if is_shared is not None:
|
||||||
|
if is_shared:
|
||||||
|
query = query.filter(Chat.share_id.isnot(None))
|
||||||
|
else:
|
||||||
|
query = query.filter(Chat.share_id.is_(None))
|
||||||
|
|
||||||
query = query.order_by(Chat.updated_at.desc())
|
query = query.order_by(Chat.updated_at.desc())
|
||||||
|
|
||||||
# Check if the database dialect is either 'sqlite' or 'postgresql'
|
# Check if the database dialect is either 'sqlite' or 'postgresql'
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadChatPreview = async (selectedIdx) => {
|
const loadChatPreview = async (selectedIdx) => {
|
||||||
if (!chatList || chatList.length === 0) {
|
if (!chatList || chatList.length === 0 || chatList[selectedIdx] === undefined) {
|
||||||
selectedChat = null;
|
selectedChat = null;
|
||||||
messages = null;
|
messages = null;
|
||||||
history = null;
|
history = null;
|
||||||
|
@ -139,6 +139,11 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKeyDown = (e) => {
|
const onKeyDown = (e) => {
|
||||||
|
const searchOptions = document.getElementById('search-options-container');
|
||||||
|
if (searchOptions) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (e.code === 'Escape') {
|
if (e.code === 'Escape') {
|
||||||
show = false;
|
show = false;
|
||||||
onClose();
|
onClose();
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
export let onKeydown = (e) => {};
|
export let onKeydown = (e) => {};
|
||||||
|
|
||||||
let selectedIdx = 0;
|
let selectedIdx = 0;
|
||||||
|
let selectedOption = null;
|
||||||
|
|
||||||
let lastWord = '';
|
let lastWord = '';
|
||||||
$: lastWord = value ? value.split(' ').at(-1) : value;
|
$: lastWord = value ? value.split(' ').at(-1) : value;
|
||||||
|
@ -23,39 +24,115 @@
|
||||||
{
|
{
|
||||||
name: 'tag:',
|
name: 'tag:',
|
||||||
description: $i18n.t('search for tags')
|
description: $i18n.t('search for tags')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pinned:',
|
||||||
|
description: $i18n.t('search for pinned chats')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'shared:',
|
||||||
|
description: $i18n.t('search for shared chats')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'archived:',
|
||||||
|
description: $i18n.t('search for archived chats')
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
let focused = false;
|
let focused = false;
|
||||||
let loading = false;
|
let loading = false;
|
||||||
|
|
||||||
|
let hovering = false;
|
||||||
|
|
||||||
let filteredOptions = options;
|
let filteredOptions = options;
|
||||||
$: filteredOptions = options.filter((option) => {
|
$: filteredOptions = options.filter((option) => {
|
||||||
return option.name.startsWith(lastWord);
|
return option.name.startsWith(lastWord);
|
||||||
});
|
});
|
||||||
|
|
||||||
let filteredTags = [];
|
let filteredItems = [];
|
||||||
$: filteredTags = lastWord.startsWith('tag:')
|
|
||||||
? [
|
$: if (lastWord && lastWord !== null) {
|
||||||
|
initItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
const initItems = async () => {
|
||||||
|
console.log('initItems', lastWord);
|
||||||
|
loading = true;
|
||||||
|
await tick();
|
||||||
|
|
||||||
|
if (lastWord.startsWith('tag:')) {
|
||||||
|
filteredItems = [
|
||||||
...$tags,
|
...$tags,
|
||||||
{
|
{
|
||||||
id: 'none',
|
id: 'none',
|
||||||
name: $i18n.t('Untagged')
|
name: $i18n.t('Untagged')
|
||||||
}
|
}
|
||||||
].filter((tag) => {
|
]
|
||||||
const tagName = lastWord.slice(4);
|
.filter((tag) => {
|
||||||
if (tagName) {
|
const tagName = lastWord.slice(4);
|
||||||
const tagId = tagName.replace(' ', '_').toLowerCase();
|
if (tagName) {
|
||||||
|
const tagId = tagName.replace(' ', '_').toLowerCase();
|
||||||
|
|
||||||
if (tag.id !== tagId) {
|
if (tag.id !== tagId) {
|
||||||
return tag.id.startsWith(tagId);
|
return tag.id.startsWith(tagId);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
})
|
||||||
return true;
|
.map((tag) => {
|
||||||
|
return {
|
||||||
|
id: tag.id,
|
||||||
|
name: tag.name,
|
||||||
|
type: 'tag'
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} else if (lastWord.startsWith('pinned:')) {
|
||||||
|
filteredItems = [
|
||||||
|
{
|
||||||
|
id: 'true',
|
||||||
|
name: 'true',
|
||||||
|
type: 'pinned'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'false',
|
||||||
|
name: 'false',
|
||||||
|
type: 'pinned'
|
||||||
}
|
}
|
||||||
})
|
];
|
||||||
: [];
|
} else if (lastWord.startsWith('shared:')) {
|
||||||
|
filteredItems = [
|
||||||
|
{
|
||||||
|
id: 'true',
|
||||||
|
name: 'true',
|
||||||
|
type: 'shared'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'false',
|
||||||
|
name: 'false',
|
||||||
|
type: 'shared'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
} else if (lastWord.startsWith('archived:')) {
|
||||||
|
filteredItems = [
|
||||||
|
{
|
||||||
|
id: 'true',
|
||||||
|
name: 'true',
|
||||||
|
type: 'archived'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'false',
|
||||||
|
name: 'false',
|
||||||
|
type: 'archived'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
filteredItems = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
loading = false;
|
||||||
|
};
|
||||||
|
|
||||||
const initTags = async () => {
|
const initTags = async () => {
|
||||||
loading = true;
|
loading = true;
|
||||||
|
@ -99,6 +176,7 @@
|
||||||
id="search-input"
|
id="search-input"
|
||||||
class="w-full rounded-r-xl py-1.5 pl-2.5 text-sm bg-transparent dark:text-gray-300 outline-hidden"
|
class="w-full rounded-r-xl py-1.5 pl-2.5 text-sm bg-transparent dark:text-gray-300 outline-hidden"
|
||||||
placeholder={placeholder ? placeholder : $i18n.t('Search')}
|
placeholder={placeholder ? placeholder : $i18n.t('Search')}
|
||||||
|
autocomplete="off"
|
||||||
bind:value
|
bind:value
|
||||||
on:input={() => {
|
on:input={() => {
|
||||||
dispatch('input');
|
dispatch('input');
|
||||||
|
@ -108,13 +186,15 @@
|
||||||
initTags();
|
initTags();
|
||||||
}}
|
}}
|
||||||
on:blur={() => {
|
on:blur={() => {
|
||||||
focused = false;
|
if (!hovering) {
|
||||||
|
focused = false;
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
on:keydown={(e) => {
|
on:keydown={(e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
if (filteredTags.length > 0) {
|
if (filteredItems.length > 0) {
|
||||||
const tagElement = document.getElementById(`search-tag-${selectedIdx}`);
|
const itemElement = document.getElementById(`search-item-${selectedIdx}`);
|
||||||
tagElement.click();
|
itemElement.click();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,10 +211,18 @@
|
||||||
} else if (e.key === 'ArrowDown') {
|
} else if (e.key === 'ArrowDown') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (filteredTags.length > 0) {
|
if (filteredItems.length > 0) {
|
||||||
selectedIdx = Math.min(selectedIdx + 1, filteredTags.length - 1);
|
if (selectedIdx === filteredItems.length - 1) {
|
||||||
|
focused = false;
|
||||||
|
} else {
|
||||||
|
selectedIdx = Math.min(selectedIdx + 1, filteredItems.length - 1);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
selectedIdx = Math.min(selectedIdx + 1, filteredOptions.length - 1);
|
if (selectedIdx === filteredOptions.length - 1) {
|
||||||
|
focused = false;
|
||||||
|
} else {
|
||||||
|
selectedIdx = Math.min(selectedIdx + 1, filteredOptions.length - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if the user types something, reset to the top selection.
|
// if the user types something, reset to the top selection.
|
||||||
|
@ -159,48 +247,53 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if focused && (filteredOptions.length > 0 || filteredTags.length > 0)}
|
{#if focused && (filteredOptions.length > 0 || filteredItems.length > 0)}
|
||||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div
|
<div
|
||||||
class="absolute top-0 mt-8 left-0 right-1 border border-gray-100 dark:border-gray-900 bg-gray-50 dark:bg-gray-950 rounded-lg z-10 shadow-lg"
|
class="absolute top-0 mt-8 left-0 right-1 border border-gray-100 dark:border-gray-900 bg-gray-50 dark:bg-gray-950 rounded-lg z-10 shadow-lg"
|
||||||
id="search-options-container"
|
id="search-options-container"
|
||||||
in:fade={{ duration: 50 }}
|
in:fade={{ duration: 50 }}
|
||||||
on:mouseenter={() => {
|
on:mouseenter={() => {
|
||||||
|
hovering = true;
|
||||||
selectedIdx = null;
|
selectedIdx = null;
|
||||||
}}
|
}}
|
||||||
on:mouseleave={() => {
|
on:mouseleave={() => {
|
||||||
selectedIdx = 0;
|
selectedIdx = 0;
|
||||||
|
hovering = false;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="px-2 py-2 text-xs group">
|
<div class="px-2 py-2 text-xs group">
|
||||||
{#if filteredTags.length > 0}
|
{#if filteredItems.length > 0}
|
||||||
<div class="px-1 font-medium dark:text-gray-300 text-gray-700 mb-1">Tags</div>
|
<div class="px-1 font-medium dark:text-gray-300 text-gray-700 mb-1 capitalize">
|
||||||
|
{selectedOption}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="max-h-60 overflow-auto">
|
<div class="max-h-60 overflow-auto">
|
||||||
{#each filteredTags as tag, tagIdx}
|
{#each filteredItems as item, itemIdx}
|
||||||
<button
|
<button
|
||||||
class=" px-1.5 py-0.5 flex gap-1 hover:bg-gray-100 dark:hover:bg-gray-900 w-full rounded {selectedIdx ===
|
class=" px-1.5 py-0.5 flex gap-1 hover:bg-gray-100 dark:hover:bg-gray-900 w-full rounded {selectedIdx ===
|
||||||
tagIdx
|
itemIdx
|
||||||
? 'bg-gray-100 dark:bg-gray-900'
|
? 'bg-gray-100 dark:bg-gray-900'
|
||||||
: ''}"
|
: ''}"
|
||||||
id="search-tag-{tagIdx}"
|
id="search-item-{itemIdx}"
|
||||||
on:click|stopPropagation={async () => {
|
on:click|stopPropagation={async () => {
|
||||||
const words = value.split(' ');
|
const words = value.split(' ');
|
||||||
|
|
||||||
words.pop();
|
words.pop();
|
||||||
words.push(`tag:${tag.id} `);
|
words.push(`${item.type}:${item.id} `);
|
||||||
|
|
||||||
value = words.join(' ');
|
value = words.join(' ');
|
||||||
|
|
||||||
|
filteredItems = [];
|
||||||
dispatch('input');
|
dispatch('input');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="dark:text-gray-300 text-gray-700 font-medium line-clamp-1 shrink-0">
|
<div class="dark:text-gray-300 text-gray-700 font-medium line-clamp-1 shrink-0">
|
||||||
{tag.name}
|
{item.name}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class=" text-gray-500 line-clamp-1">
|
<div class=" text-gray-500 line-clamp-1">
|
||||||
{tag.id}
|
{item.id}
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -222,7 +315,9 @@
|
||||||
const words = value.split(' ');
|
const words = value.split(' ');
|
||||||
|
|
||||||
words.pop();
|
words.pop();
|
||||||
words.push('tag:');
|
words.push(`${option.name}`);
|
||||||
|
|
||||||
|
selectedOption = option.name.replace(':', '');
|
||||||
|
|
||||||
value = words.join(' ');
|
value = words.join(' ');
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue