mirror of
https://github.com/0PandaDEV/Qopy.git
synced 2025-04-21 21:24:05 +02:00
fixed display of images
This commit is contained in:
parent
fb535a4521
commit
fd4ce395f5
5 changed files with 84 additions and 63 deletions
103
app.vue
103
app.vue
|
@ -33,10 +33,15 @@
|
|||
@click="selectItem(groupIndex, index)"
|
||||
:ref="el => { if (isSelected(groupIndex, index)) selectedElement = el as HTMLElement }">
|
||||
<template v-if="item.content_type === 'image'">
|
||||
<img :src="getComputedImageUrl(item)" alt="Image" class="image" @error="onImageError">
|
||||
<IconsImage v-show="imageLoadError" class="icon" />
|
||||
<img v-if="!imageLoading && !imageLoadError"
|
||||
:src="getComputedImageUrl(item)"
|
||||
alt="Image"
|
||||
class="image"
|
||||
@error="onImageError">
|
||||
<IconsImage v-if="imageLoading || imageLoadError" class="icon" />
|
||||
</template>
|
||||
<img v-else-if="hasFavicon(item.favicon ?? '')" :src="getFaviconFromDb(item.favicon ?? '')" alt="Favicon" class="favicon">
|
||||
<img v-else-if="hasFavicon(item.favicon ?? '')" :src="getFaviconFromDb(item.favicon ?? '')" alt="Favicon"
|
||||
class="favicon">
|
||||
<IconsFile class="icon" v-else-if="item.content_type === 'files'" />
|
||||
<IconsText class="icon" v-else-if="item.content_type === 'text'" />
|
||||
<IconsCode class="icon" v-else-if="item.content_type === 'code'" />
|
||||
|
@ -49,8 +54,8 @@
|
|||
<img :src="getComputedImageUrl(selectedItem)" alt="Image" class="image">
|
||||
</div>
|
||||
<OverlayScrollbarsComponent v-else class="content">
|
||||
<img v-if="selectedItem?.content && isYoutubeWatchUrl(selectedItem.content)" :src="getYoutubeThumbnail(selectedItem.content)"
|
||||
alt="YouTube Thumbnail" class="full-image">
|
||||
<img v-if="selectedItem?.content && isYoutubeWatchUrl(selectedItem.content)"
|
||||
:src="getYoutubeThumbnail(selectedItem.content)" alt="YouTube Thumbnail" class="full-image">
|
||||
<span v-else>{{ selectedItem?.content || '' }}</span>
|
||||
</OverlayScrollbarsComponent>
|
||||
<Noise />
|
||||
|
@ -95,6 +100,9 @@ const selectedItemIndex: Ref<number> = ref(0);
|
|||
const selectedElement: Ref<HTMLElement | null> = ref(null);
|
||||
const searchInput: Ref<HTMLInputElement | null> = ref(null);
|
||||
const os: Ref<string> = ref('');
|
||||
const imageLoadError = ref(false);
|
||||
const imageLoading = ref(true);
|
||||
const imageUrls: Ref<Record<number, string>> = shallowRef({});
|
||||
|
||||
const groupedHistory: ComputedRef<GroupedHistory[]> = computed(() => {
|
||||
const now = new Date();
|
||||
|
@ -255,52 +263,74 @@ const getFaviconFromDb = (favicon: string): string => {
|
|||
const getImageDimensions = (path: string): Promise<string> => {
|
||||
return new Promise(async (resolve) => {
|
||||
const img = new Image();
|
||||
img.onload = () => resolve(`${img.width}x${img.height}`);
|
||||
img.onerror = () => resolve('0x0');
|
||||
if (path.includes('AppData\\Roaming\\net.pandadev.qopy\\images\\')) {
|
||||
const filename = path.split('\\').pop();
|
||||
try {
|
||||
const imageData = await invoke<Uint8Array>("read_image", { filename: filename });
|
||||
const blob = new Blob([imageData], { type: 'image/png' });
|
||||
img.src = URL.createObjectURL(blob);
|
||||
} catch (error) {
|
||||
console.error('Error reading image file:', error);
|
||||
img.onload = () => {
|
||||
imageLoadError.value = false;
|
||||
imageLoading.value = false;
|
||||
resolve(`${img.width}x${img.height}`);
|
||||
};
|
||||
img.onerror = (e) => {
|
||||
console.error('Error loading image:', e);
|
||||
imageLoadError.value = true;
|
||||
imageLoading.value = false;
|
||||
resolve('0x0');
|
||||
};
|
||||
|
||||
try {
|
||||
imageLoading.value = true;
|
||||
const dataUrl = await getImageUrl(path);
|
||||
img.src = dataUrl;
|
||||
} catch (error) {
|
||||
console.error('Error getting image URL:', error);
|
||||
imageLoadError.value = true;
|
||||
imageLoading.value = false;
|
||||
resolve('0x0');
|
||||
}
|
||||
} else {
|
||||
img.src = `data:image/png;base64,${path}`;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const imageUrls: Ref<Record<number, string>> = shallowRef({});
|
||||
const getImageUrl = async (path: string): Promise<string> => {
|
||||
const isWindows = path.includes('\\');
|
||||
const separator = isWindows ? '\\' : '/';
|
||||
const filename = path.split(separator).pop();
|
||||
|
||||
try {
|
||||
imageLoading.value = true;
|
||||
const base64 = await invoke<string>("read_image", { filename });
|
||||
console.log('Image data received, length:', base64.length);
|
||||
if (!base64 || base64.length === 0) {
|
||||
throw new Error('Received empty image data');
|
||||
}
|
||||
|
||||
const dataUrl = `data:image/png;base64,${base64}`;
|
||||
|
||||
console.log('Data URL preview:', dataUrl.substring(0, 50) + '...');
|
||||
|
||||
imageLoadError.value = false;
|
||||
imageLoading.value = false;
|
||||
return dataUrl;
|
||||
} catch (error) {
|
||||
console.error('Error reading image file:', error);
|
||||
imageLoadError.value = true;
|
||||
imageLoading.value = false;
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
const getComputedImageUrl = (item: HistoryItem): string => {
|
||||
if (!imageUrls.value[item.id]) {
|
||||
imageUrls.value[item.id] = '';
|
||||
getImageUrl(item.content).then(url => {
|
||||
getImageUrl(item.content)
|
||||
.then(url => {
|
||||
imageUrls.value = { ...imageUrls.value, [item.id]: url };
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Failed to get image URL:', error);
|
||||
imageUrls.value = { ...imageUrls.value, [item.id]: '' };
|
||||
});
|
||||
}
|
||||
return imageUrls.value[item.id] || '';
|
||||
};
|
||||
|
||||
const getImageUrl = async (path: string): Promise<string> => {
|
||||
if (path.includes('AppData\\Roaming\\net.pandadev.qopy\\images\\')) {
|
||||
const filename = path.split('\\').pop();
|
||||
try {
|
||||
const imageData = await invoke<Uint8Array>("read_image", { filename: filename });
|
||||
const blob = new Blob([imageData], { type: 'image/png' });
|
||||
return URL.createObjectURL(blob);
|
||||
} catch (error) {
|
||||
console.error('Error reading image file:', error);
|
||||
return '';
|
||||
}
|
||||
} else {
|
||||
return `data:image/png;base64,${path}`;
|
||||
}
|
||||
};
|
||||
|
||||
const loadHistoryChunk = async (): Promise<void> => {
|
||||
if (!db.value || isLoading) return;
|
||||
|
||||
|
@ -328,6 +358,7 @@ const loadHistoryChunk = async (): Promise<void> => {
|
|||
const processedChunk = await Promise.all(results.map(async item => {
|
||||
if (item.content_type === 'image') {
|
||||
const dimensions = await getImageDimensions(item.content);
|
||||
getComputedImageUrl(item);
|
||||
return { ...item, dimensions };
|
||||
}
|
||||
return item;
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
"core:window:allow-show",
|
||||
"core:window:allow-set-focus",
|
||||
"core:window:allow-is-focused",
|
||||
"core:window:allow-is-visible"
|
||||
"core:window:allow-is-visible",
|
||||
"fs:allow-read"
|
||||
]
|
||||
}
|
|
@ -24,11 +24,12 @@ pub fn set_app_data_dir(path: std::path::PathBuf) {
|
|||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn read_image(filename: String) -> Result<Vec<u8>, String> {
|
||||
pub fn read_image(filename: String) -> Result<String, String> {
|
||||
let app_data_dir = APP_DATA_DIR.lock().unwrap();
|
||||
let app_data_dir = app_data_dir.as_ref().expect("App data directory not set");
|
||||
let image_path = app_data_dir.join("images").join(filename);
|
||||
fs::read(image_path).map_err(|e| e.to_string())
|
||||
let image_data = fs::read(image_path).map_err(|e| e.to_string())?;
|
||||
Ok(STANDARD.encode(image_data))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
@ -107,26 +108,22 @@ pub fn setup<R: Runtime>(app: &AppHandle<R>) {
|
|||
let app = app.clone();
|
||||
runtime.block_on(async move {
|
||||
if IS_PROGRAMMATIC_PASTE.load(Ordering::SeqCst) {
|
||||
println!("Ignoring programmatic paste");
|
||||
return;
|
||||
}
|
||||
|
||||
let clipboard = app.state::<Clipboard>();
|
||||
let available_types = clipboard.available_types().unwrap();
|
||||
|
||||
println!("Clipboard update detected");
|
||||
|
||||
match get_pool(&app).await {
|
||||
Ok(pool) => {
|
||||
if available_types.image {
|
||||
println!("Handling image change");
|
||||
if let Ok(image_data) = clipboard.read_image_base64() {
|
||||
let base64_image = STANDARD.encode(&image_data);
|
||||
insert_content_if_not_exists(
|
||||
app.clone(),
|
||||
pool.clone(),
|
||||
"image",
|
||||
base64_image,
|
||||
image_data,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use tauri::Manager;
|
|||
|
||||
use crate::utils::commands::center_window_on_current_monitor;
|
||||
|
||||
#[warn(dead_code)]
|
||||
pub fn setup(app_handle: tauri::AppHandle) {
|
||||
std::thread::spawn(move || {
|
||||
let mut meta_pressed = false;
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
use tauri::{
|
||||
menu::{MenuBuilder, MenuItemBuilder},
|
||||
tray::{MouseButton, TrayIconBuilder, TrayIconEvent},
|
||||
tray::TrayIconBuilder,
|
||||
Manager,
|
||||
};
|
||||
|
||||
pub fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let window = app.get_webview_window("main").unwrap();
|
||||
let window_clone_for_tray = window.clone();
|
||||
let window_clone_for_click = window.clone();
|
||||
|
||||
let icon_bytes = include_bytes!("../../icons/Square71x71Logo.png");
|
||||
let icon = tauri::image::Image::from_bytes(icon_bytes).unwrap();
|
||||
|
@ -37,14 +36,6 @@ pub fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
|
|||
}
|
||||
_ => (),
|
||||
})
|
||||
// .on_tray_icon_event(move |_tray, event| {
|
||||
// if let TrayIconEvent::Click { button, .. } = event {
|
||||
// if button == MouseButton::Left {
|
||||
// window_clone_for_click.show().unwrap();
|
||||
// window_clone_for_click.set_focus().unwrap();
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
.icon(icon)
|
||||
.build(app)?;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue