mirror of
https://github.com/0PandaDEV/Qopy.git
synced 2025-04-20 12:54:05 +02:00
refactor: serach function now async which is way faster
This commit is contained in:
parent
ae878b7203
commit
f435a7b20a
14 changed files with 203 additions and 106 deletions
9
app.vue
9
app.vue
|
@ -38,25 +38,25 @@ onMounted(async () => {
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: SFRoundedRegular;
|
font-family: SFRoundedRegular;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("~/assets/fonts/SFRoundedRegular.otf") format("opentype");
|
src: url("/fonts/SFRoundedRegular.otf") format("opentype");
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: SFRoundedMedium;
|
font-family: SFRoundedMedium;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("~/assets/fonts/SFRoundedMedium.otf") format("opentype");
|
src: url("/fonts/SFRoundedMedium.otf") format("opentype");
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: SFRoundedSemiBold;
|
font-family: SFRoundedSemiBold;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("~/assets/fonts/SFRoundedSemiBold.otf") format("opentype");
|
src: url("/fonts/SFRoundedSemiBold.otf") format("opentype");
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: CommitMono;
|
font-family: CommitMono;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("~/assets/fonts/CommitMono.woff2") format("woff2");
|
src: url("/fonts/CommitMono.woff2") format("woff2");
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
@ -85,6 +85,7 @@ onMounted(async () => {
|
||||||
font-family: SFRoundedRegular;
|
font-family: SFRoundedRegular;
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
--os-handle-bg: #ada9a1;
|
--os-handle-bg: #ada9a1;
|
||||||
--os-handle-bg-hover: #78756f;
|
--os-handle-bg-hover: #78756f;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<input
|
<input
|
||||||
ref="searchInput"
|
ref="searchInput"
|
||||||
v-model="searchQuery"
|
v-model="searchQuery"
|
||||||
@input="onSearch"
|
@input="onInputChange"
|
||||||
class="search"
|
class="search"
|
||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
autocapitalize="off"
|
autocapitalize="off"
|
||||||
|
@ -21,10 +21,12 @@ const searchInput = ref<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: "search", query: string): void;
|
(e: "search", query: string): void;
|
||||||
|
(e: "searchStarted"): void;
|
||||||
(e: "focus"): void;
|
(e: "focus"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const onSearch = () => {
|
const onInputChange = () => {
|
||||||
|
emit("searchStarted");
|
||||||
emit("search", searchQuery.value);
|
emit("search", searchQuery.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
192
pages/index.vue
192
pages/index.vue
|
@ -1,64 +1,40 @@
|
||||||
<template>
|
<template>
|
||||||
<main>
|
<main>
|
||||||
<TopBar ref="topBar" @search="searchHistory" />
|
<TopBar ref="topBar" @search="searchHistory" @searchStarted="searchStarted" />
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<OverlayScrollbarsComponent
|
<OverlayScrollbarsComponent class="results" ref="resultsContainer"
|
||||||
class="results"
|
|
||||||
ref="resultsContainer"
|
|
||||||
:options="{ scrollbars: { autoHide: 'scroll' } }">
|
:options="{ scrollbars: { autoHide: 'scroll' } }">
|
||||||
<div
|
<div v-for="(group, groupIndex) in groupedHistory" :key="groupIndex" class="group">
|
||||||
v-for="(group, groupIndex) in groupedHistory"
|
|
||||||
:key="groupIndex"
|
|
||||||
class="group">
|
|
||||||
<div class="time-separator">{{ group.label }}</div>
|
<div class="time-separator">{{ group.label }}</div>
|
||||||
<div class="results-group">
|
<div class="results-group">
|
||||||
<Result
|
<Result v-for="(item, index) in group.items" :key="item.id" :item="item"
|
||||||
v-for="(item, index) in group.items"
|
:selected="isSelected(groupIndex, index)" :image-url="imageUrls[item.id]"
|
||||||
:key="item.id"
|
:dimensions="imageDimensions[item.id]" @select="selectItem(groupIndex, index)" @image-error="onImageError"
|
||||||
:item="item"
|
|
||||||
:selected="isSelected(groupIndex, index)"
|
|
||||||
:image-url="imageUrls[item.id]"
|
|
||||||
:dimensions="imageDimensions[item.id]"
|
|
||||||
@select="selectItem(groupIndex, index)"
|
|
||||||
@image-error="onImageError"
|
|
||||||
@setRef="(el) => (selectedElement = el)" />
|
@setRef="(el) => (selectedElement = el)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</OverlayScrollbarsComponent>
|
</OverlayScrollbarsComponent>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<div
|
<div class="content" v-if="selectedItem?.content_type === ContentType.Image">
|
||||||
class="content"
|
|
||||||
v-if="selectedItem?.content_type === ContentType.Image">
|
|
||||||
<img :src="imageUrls[selectedItem.id]" alt="Image" class="image" />
|
<img :src="imageUrls[selectedItem.id]" alt="Image" class="image" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div v-else-if="selectedItem && isYoutubeWatchUrl(selectedItem.content)" class="content">
|
||||||
v-else-if="selectedItem && isYoutubeWatchUrl(selectedItem.content)"
|
<img class="image" :src="getYoutubeThumbnail(selectedItem.content)" alt="YouTube Thumbnail" />
|
||||||
class="content">
|
|
||||||
<img
|
|
||||||
class="image"
|
|
||||||
:src="getYoutubeThumbnail(selectedItem.content)"
|
|
||||||
alt="YouTube Thumbnail" />
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="content" v-else-if="
|
||||||
class="content"
|
selectedItem?.content_type === ContentType.Link && pageOgImage
|
||||||
v-else-if="
|
">
|
||||||
selectedItem?.content_type === ContentType.Link && pageOgImage
|
|
||||||
">
|
|
||||||
<img :src="pageOgImage" alt="Image" class="image" />
|
<img :src="pageOgImage" alt="Image" class="image" />
|
||||||
</div>
|
</div>
|
||||||
<OverlayScrollbarsComponent v-else class="content">
|
<OverlayScrollbarsComponent v-else class="content">
|
||||||
<span class="content-text">{{ selectedItem?.content || "" }}</span>
|
<span class="content-text">{{ selectedItem?.content || "" }}</span>
|
||||||
</OverlayScrollbarsComponent>
|
</OverlayScrollbarsComponent>
|
||||||
<OverlayScrollbarsComponent
|
<OverlayScrollbarsComponent class="information" :options="{ scrollbars: { autoHide: 'scroll' } }">
|
||||||
class="information"
|
|
||||||
:options="{ scrollbars: { autoHide: 'scroll' } }">
|
|
||||||
<div class="title">Information</div>
|
<div class="title">Information</div>
|
||||||
<div class="info-content" v-if="selectedItem && getInfo">
|
<div class="info-content" v-if="selectedItem && getInfo">
|
||||||
<div class="info-row" v-for="(row, index) in infoRows" :key="index">
|
<div class="info-row" v-for="(row, index) in infoRows" :key="index">
|
||||||
<p class="label">{{ row.label }}</p>
|
<p class="label">{{ row.label }}</p>
|
||||||
<span
|
<span :class="{ 'url-truncate': row.isUrl }" :data-text="row.value">
|
||||||
:class="{ 'url-truncate': row.isUrl }"
|
|
||||||
:data-text="row.value">
|
|
||||||
<img v-if="row.icon" :src="row.icon" :alt="String(row.value)">
|
<img v-if="row.icon" :src="row.icon" :alt="String(row.value)">
|
||||||
{{ row.value }}
|
{{ row.value }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -67,17 +43,15 @@
|
||||||
</OverlayScrollbarsComponent>
|
</OverlayScrollbarsComponent>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<BottomBar
|
<BottomBar :primary-action="{
|
||||||
:primary-action="{
|
text: 'Paste',
|
||||||
text: 'Paste',
|
icon: IconsEnter,
|
||||||
icon: IconsEnter,
|
onClick: pasteSelectedItem,
|
||||||
onClick: pasteSelectedItem,
|
}" :secondary-action="{
|
||||||
}"
|
text: 'Actions',
|
||||||
:secondary-action="{
|
icon: IconsK,
|
||||||
text: 'Actions',
|
showModifier: true,
|
||||||
icon: IconsK,
|
}" />
|
||||||
showModifier: true,
|
|
||||||
}" />
|
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -156,7 +130,7 @@ const getWeekNumber = (date: Date): number => {
|
||||||
((date.getTime() - firstDayOfYear.getTime()) / 86400000 +
|
((date.getTime() - firstDayOfYear.getTime()) / 86400000 +
|
||||||
firstDayOfYear.getDay() +
|
firstDayOfYear.getDay() +
|
||||||
1) /
|
1) /
|
||||||
7
|
7
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -177,8 +151,8 @@ const groupedHistory = computed<GroupedHistory[]>(() => {
|
||||||
|
|
||||||
const filteredItems = searchQuery.value
|
const filteredItems = searchQuery.value
|
||||||
? history.value.filter((item) =>
|
? history.value.filter((item) =>
|
||||||
item.content.toLowerCase().includes(searchQuery.value.toLowerCase())
|
item.content.toLowerCase().includes(searchQuery.value.toLowerCase())
|
||||||
)
|
)
|
||||||
: history.value;
|
: history.value;
|
||||||
|
|
||||||
const yesterday = new Date(today.getTime() - 86400000);
|
const yesterday = new Date(today.getTime() - 86400000);
|
||||||
|
@ -321,32 +295,78 @@ const scrollToSelectedItem = (): void => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let searchController: AbortController | null = null;
|
||||||
|
let searchQueue: Array<string> = [];
|
||||||
|
let isProcessingSearch = false;
|
||||||
|
|
||||||
|
const searchStarted = () => {
|
||||||
|
if (searchController) {
|
||||||
|
searchController.abort();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const processSearchQueue = async () => {
|
||||||
|
if (isProcessingSearch || searchQueue.length === 0) return;
|
||||||
|
|
||||||
|
isProcessingSearch = true;
|
||||||
|
const query = searchQueue.pop();
|
||||||
|
searchQueue = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!query || !query.trim()) {
|
||||||
|
history.value = [];
|
||||||
|
offset = 0;
|
||||||
|
await loadHistoryChunk();
|
||||||
|
isProcessingSearch = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = await $history.searchHistory(query);
|
||||||
|
|
||||||
|
if (searchController?.signal.aborted) {
|
||||||
|
isProcessingSearch = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
history.value = results.map((item) =>
|
||||||
|
Object.assign(
|
||||||
|
new HistoryItem(
|
||||||
|
item.source,
|
||||||
|
item.content_type,
|
||||||
|
item.content,
|
||||||
|
item.favicon,
|
||||||
|
item.source_icon,
|
||||||
|
item.language
|
||||||
|
),
|
||||||
|
{ id: item.id, timestamp: new Date(item.timestamp) }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (groupedHistory.value.length > 0) {
|
||||||
|
handleSelection(0, 0, false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Search error:", error);
|
||||||
|
} finally {
|
||||||
|
isProcessingSearch = false;
|
||||||
|
if (searchQueue.length > 0) {
|
||||||
|
requestAnimationFrame(() => processSearchQueue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const searchHistory = async (query: string): Promise<void> => {
|
const searchHistory = async (query: string): Promise<void> => {
|
||||||
searchQuery.value = query;
|
searchQuery.value = query;
|
||||||
if (!query.trim()) {
|
|
||||||
history.value = [];
|
if (searchController) {
|
||||||
offset = 0;
|
searchController.abort();
|
||||||
await loadHistoryChunk();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = await $history.searchHistory(query);
|
searchController = new AbortController();
|
||||||
history.value = results.map((item) =>
|
|
||||||
Object.assign(
|
|
||||||
new HistoryItem(
|
|
||||||
item.source,
|
|
||||||
item.content_type,
|
|
||||||
item.content,
|
|
||||||
item.favicon,
|
|
||||||
item.source_icon,
|
|
||||||
item.language
|
|
||||||
),
|
|
||||||
{ id: item.id, timestamp: new Date(item.timestamp) }
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (groupedHistory.value.length > 0) {
|
searchQueue.push(query);
|
||||||
handleSelection(0, 0, false);
|
if (!isProcessingSearch) {
|
||||||
|
processSearchQueue();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -517,14 +537,14 @@ const setupEventListeners = async (): Promise<void> => {
|
||||||
|
|
||||||
switch (os.value) {
|
switch (os.value) {
|
||||||
case "macos":
|
case "macos":
|
||||||
keyboard.prevent.down([Key.LeftMeta, Key.K], () => {});
|
keyboard.prevent.down([Key.LeftMeta, Key.K], () => { });
|
||||||
keyboard.prevent.down([Key.RightMeta, Key.K], () => {});
|
keyboard.prevent.down([Key.RightMeta, Key.K], () => { });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "linux":
|
case "linux":
|
||||||
case "windows":
|
case "windows":
|
||||||
keyboard.prevent.down([Key.LeftControl, Key.K], () => {});
|
keyboard.prevent.down([Key.LeftControl, Key.K], () => { });
|
||||||
keyboard.prevent.down([Key.RightControl, Key.K], () => {});
|
keyboard.prevent.down([Key.RightControl, Key.K], () => { });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -552,14 +572,14 @@ const setupEventListeners = async (): Promise<void> => {
|
||||||
|
|
||||||
switch (os.value) {
|
switch (os.value) {
|
||||||
case "macos":
|
case "macos":
|
||||||
keyboard.prevent.down([Key.LeftMeta, Key.K], () => {});
|
keyboard.prevent.down([Key.LeftMeta, Key.K], () => { });
|
||||||
keyboard.prevent.down([Key.RightMeta, Key.K], () => {});
|
keyboard.prevent.down([Key.RightMeta, Key.K], () => { });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "linux":
|
case "linux":
|
||||||
case "windows":
|
case "windows":
|
||||||
keyboard.prevent.down([Key.LeftControl, Key.K], () => {});
|
keyboard.prevent.down([Key.LeftControl, Key.K], () => { });
|
||||||
keyboard.prevent.down([Key.RightControl, Key.K], () => {});
|
keyboard.prevent.down([Key.RightControl, Key.K], () => { });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -750,9 +770,9 @@ const infoRows = computed(() => {
|
||||||
if (!getInfo.value) return [];
|
if (!getInfo.value) return [];
|
||||||
|
|
||||||
const commonRows = [
|
const commonRows = [
|
||||||
{
|
{
|
||||||
label: "Source",
|
label: "Source",
|
||||||
value: getInfo.value.source,
|
value: getInfo.value.source,
|
||||||
isUrl: false,
|
isUrl: false,
|
||||||
icon: selectedItem.value?.source_icon ? `data:image/png;base64,${selectedItem.value.source_icon}` : undefined
|
icon: selectedItem.value?.source_icon ? `data:image/png;base64,${selectedItem.value.source_icon}` : undefined
|
||||||
},
|
},
|
||||||
|
@ -785,7 +805,7 @@ const infoRows = computed(() => {
|
||||||
],
|
],
|
||||||
[ContentType.Link]: [
|
[ContentType.Link]: [
|
||||||
...((getInfo.value as InfoLink).title &&
|
...((getInfo.value as InfoLink).title &&
|
||||||
(getInfo.value as InfoLink).title !== "Loading..."
|
(getInfo.value as InfoLink).title !== "Loading..."
|
||||||
? [{ label: "Title", value: (getInfo.value as InfoLink).title || "" }]
|
? [{ label: "Title", value: (getInfo.value as InfoLink).title || "" }]
|
||||||
: []),
|
: []),
|
||||||
{ label: "URL", value: (getInfo.value as InfoLink).url, isUrl: true },
|
{ label: "URL", value: (getInfo.value as InfoLink).url, isUrl: true },
|
||||||
|
@ -815,5 +835,5 @@ const infoRows = computed(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@use "~/assets/css/index.scss";
|
@use "/styles/index.scss";
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -228,5 +228,5 @@ onUnmounted(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@use "~/assets/css/settings.scss";
|
@use "/styles/settings.scss";
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -14,17 +14,27 @@ export default defineNuxtPlugin(() => {
|
||||||
},
|
},
|
||||||
|
|
||||||
async searchHistory(query: string): Promise<HistoryItem[]> {
|
async searchHistory(query: string): Promise<HistoryItem[]> {
|
||||||
return await invoke<HistoryItem[]>("search_history", { query });
|
try {
|
||||||
|
return await invoke<HistoryItem[]>("search_history", { query });
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error searching history:", error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async loadHistoryChunk(
|
async loadHistoryChunk(
|
||||||
offset: number,
|
offset: number,
|
||||||
limit: number
|
limit: number
|
||||||
): Promise<HistoryItem[]> {
|
): Promise<HistoryItem[]> {
|
||||||
return await invoke<HistoryItem[]>("load_history_chunk", {
|
try {
|
||||||
offset,
|
return await invoke<HistoryItem[]>("load_history_chunk", {
|
||||||
limit,
|
offset,
|
||||||
});
|
limit,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading history chunk:", error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async deleteHistoryItem(id: string): Promise<void> {
|
async deleteHistoryItem(id: string): Promise<void> {
|
||||||
|
|
49
src-tauri/Cargo.lock
generated
49
src-tauri/Cargo.lock
generated
|
@ -77,15 +77,17 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "applications"
|
name = "applications"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/HuakunShen/applications-rs?branch=load_icon#bcfbebb93a57918aca4ba51257b2788be90892da"
|
source = "git+https://github.com/HuakunShen/applications-rs?branch=fix/win-app-detection#ddcec7cdc3cc4f6678f1b1b525b3e509987b21ae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cocoa 0.25.0",
|
"cocoa 0.25.0",
|
||||||
"core-foundation 0.9.4",
|
"core-foundation 0.9.4",
|
||||||
|
"env_logger",
|
||||||
"glob",
|
"glob",
|
||||||
"image",
|
"image",
|
||||||
"ini",
|
"ini",
|
||||||
"lnk",
|
"lnk",
|
||||||
|
"log",
|
||||||
"objc",
|
"objc",
|
||||||
"parselnk",
|
"parselnk",
|
||||||
"plist",
|
"plist",
|
||||||
|
@ -1359,6 +1361,19 @@ dependencies = [
|
||||||
"syn 2.0.87",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
|
||||||
|
dependencies = [
|
||||||
|
"humantime",
|
||||||
|
"is-terminal",
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
"termcolor",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -2121,6 +2136,12 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
|
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hex"
|
name = "hex"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
|
@ -2208,6 +2229,12 @@ version = "1.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
|
checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "1.4.1"
|
version = "1.4.1"
|
||||||
|
@ -2584,6 +2611,17 @@ version = "2.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-terminal"
|
||||||
|
version = "0.4.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi 0.5.0",
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
|
@ -5957,6 +5995,15 @@ dependencies = [
|
||||||
"utf-8",
|
"utf-8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "texting_robots"
|
name = "texting_robots"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
|
|
@ -49,7 +49,7 @@ log = { version = "0.4.26", features = ["std"] }
|
||||||
uuid = "1.16.0"
|
uuid = "1.16.0"
|
||||||
include_dir = "0.7.4"
|
include_dir = "0.7.4"
|
||||||
# hyperpolyglot = { git = "https://github.com/0pandadev/hyperpolyglot" }
|
# hyperpolyglot = { git = "https://github.com/0pandadev/hyperpolyglot" }
|
||||||
applications = { git = "https://github.com/HuakunShen/applications-rs", branch = "load_icon" }
|
applications = { git = "https://github.com/HuakunShen/applications-rs", branch = "fix/win-app-detection" }
|
||||||
glob = "0.3.2"
|
glob = "0.3.2"
|
||||||
meta_fetcher = "0.1.1"
|
meta_fetcher = "0.1.1"
|
||||||
parking_lot = "0.12.3"
|
parking_lot = "0.12.3"
|
||||||
|
|
|
@ -111,18 +111,27 @@ pub async fn search_history(
|
||||||
pool: tauri::State<'_, SqlitePool>,
|
pool: tauri::State<'_, SqlitePool>,
|
||||||
query: String
|
query: String
|
||||||
) -> Result<Vec<HistoryItem>, String> {
|
) -> Result<Vec<HistoryItem>, String> {
|
||||||
|
if query.trim().is_empty() {
|
||||||
|
return Ok(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
let query = format!("%{}%", query);
|
let query = format!("%{}%", query);
|
||||||
|
|
||||||
let rows = sqlx
|
let rows = sqlx
|
||||||
::query(
|
::query(
|
||||||
"SELECT id, source, source_icon, content_type, content, favicon, timestamp, language FROM history WHERE content LIKE ? ORDER BY timestamp DESC"
|
"SELECT id, source, source_icon, content_type, content, favicon, timestamp, language
|
||||||
|
FROM history
|
||||||
|
WHERE content LIKE ?
|
||||||
|
ORDER BY timestamp DESC
|
||||||
|
LIMIT 100"
|
||||||
)
|
)
|
||||||
.bind(query)
|
.bind(query)
|
||||||
.fetch_all(&*pool).await
|
.fetch_all(&*pool).await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
let items = rows
|
let mut items = Vec::with_capacity(rows.len());
|
||||||
.iter()
|
for row in rows.iter() {
|
||||||
.map(|row| HistoryItem {
|
items.push(HistoryItem {
|
||||||
id: row.get("id"),
|
id: row.get("id"),
|
||||||
source: row.get("source"),
|
source: row.get("source"),
|
||||||
source_icon: row.get("source_icon"),
|
source_icon: row.get("source_icon"),
|
||||||
|
@ -131,8 +140,8 @@ pub async fn search_history(
|
||||||
favicon: row.get("favicon"),
|
favicon: row.get("favicon"),
|
||||||
timestamp: row.get("timestamp"),
|
timestamp: row.get("timestamp"),
|
||||||
language: row.get("language"),
|
language: row.get("language"),
|
||||||
})
|
});
|
||||||
.collect();
|
}
|
||||||
|
|
||||||
Ok(items)
|
Ok(items)
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,3 +176,11 @@ main {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-loading {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue