feat: add option to keep window on top, closes #820 (#825)

This commit is contained in:
Huang Xin
2025-04-07 14:23:46 +08:00
committed by GitHub
parent aa66ba54c5
commit 2599f40dfb
24 changed files with 74 additions and 23 deletions
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "فشل في تحليل ملف EPUB.",
"This book format is not supported.": "هذا تنسيق الكتاب غير مدعوم.",
"Unable to fetch the translation. Please log in first and try again.": "تعذر جلب الترجمة. يرجى تسجيل الدخول أولاً ثم المحاولة مرة أخرى.",
"Group": "تجميع"
"Group": "تجميع",
"Always on Top": "دائمًا في المقدمة"
}
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "Fehler beim Parsen der EPUB-Datei.",
"This book format is not supported.": "Dieses Buchformat wird nicht unterstützt.",
"Unable to fetch the translation. Please log in first and try again.": "Übersetzung konnte nicht abgerufen werden. Bitte zuerst anmelden und erneut versuchen.",
"Group": "Gruppieren"
"Group": "Gruppieren",
"Always on Top": "Immer im Vordergrund"
}
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "Αποτυχία ανάλυσης του αρχείου EPUB.",
"This book format is not supported.": "Αυτή η μορφή βιβλίου δεν υποστηρίζεται.",
"Unable to fetch the translation. Please log in first and try again.": "Αδυναμία λήψης της μετάφρασης. Παρακαλώ συνδεθείτε πρώτα και δοκιμάστε ξανά.",
"Group": "Ομάδα"
"Group": "Ομάδα",
"Always on Top": "Πάντα στην κορυφή"
}
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "Error al analizar el archivo EPUB.",
"This book format is not supported.": "Este formato de libro no es compatible.",
"Unable to fetch the translation. Please log in first and try again.": "No se puede obtener la traducción. Por favor, inicia sesión primero e inténtalo de nuevo.",
"Group": "Grupo"
"Group": "Grupo",
"Always on Top": "Siempre en la parte superior"
}
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "Échec de l'analyse du fichier EPUB.",
"This book format is not supported.": "Ce format de livre n'est pas pris en charge.",
"Unable to fetch the translation. Please log in first and try again.": "Impossible de récupérer la traduction. Veuillez d'abord vous connecter et réessayer.",
"Group": "Groupe"
"Group": "Groupe",
"Always on Top": "Toujours au-dessus"
}
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "EPUB फ़ाइल को पार्स करने में विफल।",
"This book format is not supported.": "यह पुस्तक प्रारूप समर्थित नहीं है।",
"Unable to fetch the translation. Please log in first and try again.": "अनुवाद प्राप्त करने में असमर्थ। कृपया पहले लॉग इन करें और पुनः प्रयास करें।",
"Group": "समूह"
"Group": "समूह",
"Always on Top": "सर्वोच्च पर हमेशा"
}
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "Gagal mengurai file EPUB.",
"This book format is not supported.": "Format buku ini tidak didukung.",
"Unable to fetch the translation. Please log in first and try again.": "Tidak dapat mengambil terjemahan. Silakan masuk terlebih dahulu dan coba lagi.",
"Group": "Grup"
"Group": "Grup",
"Always on Top": "Selalu di Atas"
}
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "Impossibile analizzare il file EPUB.",
"This book format is not supported.": "Questo formato di libro non è supportato.",
"Unable to fetch the translation. Please log in first and try again.": "Impossibile recuperare la traduzione. Accedi prima e riprova.",
"Group": "Gruppo"
"Group": "Gruppo",
"Always on Top": "Sempre in primo piano"
}
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "EPUBファイルの解析に失敗しました。",
"This book format is not supported.": "この書籍形式はサポートされていません。",
"Unable to fetch the translation. Please log in first and try again.": "翻訳を取得できません。まずログインして再試行してください。",
"Group": "グループ"
"Group": "グループ",
"Always on Top": "常に最前面"
}
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "EPUB 파일 구문 분석 실패.",
"This book format is not supported.": "이 책 형식은 지원되지 않습니다.",
"Unable to fetch the translation. Please log in first and try again.": "번역을 가져올 수 없습니다. 먼저 로그인하고 다시 시도하세요.",
"Group": "그룹"
"Group": "그룹",
"Always on Top": "항상 위에"
}
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "Nie udało się przeanalizować pliku EPUB.",
"This book format is not supported.": "Ten format książki nie jest obsługiwany.",
"Unable to fetch the translation. Please log in first and try again.": "Nie można pobrać tłumaczenia. Proszę najpierw się zalogować i spróbować ponownie.",
"Group": "Grupa"
"Group": "Grupa",
"Always on Top": "Zawsze na wierzchu"
}
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "Falha ao analisar o arquivo EPUB.",
"This book format is not supported.": "Este formato de livro não é suportado.",
"Unable to fetch the translation. Please log in first and try again.": "Não foi possível buscar a tradução. Faça login primeiro e tente novamente.",
"Group": "Grupo"
"Group": "Grupo",
"Always on Top": "Sempre no Topo"
}
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "Не удалось разобрать файл EPUB.",
"This book format is not supported.": "Этот формат книги не поддерживается.",
"Unable to fetch the translation. Please log in first and try again.": "Не удалось получить перевод. Пожалуйста, войдите в систему и попробуйте снова.",
"Group": "Группа"
"Group": "Группа",
"Always on Top": "Всегда сверху"
}
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "EPUB dosyası ayrıştırılamadı.",
"This book format is not supported.": "Bu kitap formatı desteklenmiyor.",
"Unable to fetch the translation. Please log in first and try again.": "Çeviri alınamıyor. Lütfen önce giriş yapın ve tekrar deneyin.",
"Group": "Grup"
"Group": "Grup",
"Always on Top": "Her Zaman Üstte"
}
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "Не вдалося розібрати файл EPUB.",
"This book format is not supported.": "Цей формат книги не підтримується.",
"Unable to fetch the translation. Please log in first and try again.": "Не вдалося отримати переклад. Будь ласка, спочатку увійдіть у систему та спробуйте ще раз.",
"Group": "Група"
"Group": "Група",
"Always on Top": "Завжди зверху"
}
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "Không thể phân tích tệp EPUB.",
"This book format is not supported.": "Định dạng sách này không được hỗ trợ.",
"Unable to fetch the translation. Please log in first and try again.": "Không thể tải dịch vụ. Vui lòng đăng nhập trước và thử lại.",
"Group": "Nhóm"
"Group": "Nhóm",
"Always on Top": "Luôn ở trên cùng"
}
@@ -230,7 +230,7 @@
"Maximum Column Height": "最大列高",
"Maximum Column Width": "最大列宽",
"Continuous Scroll": "连续滚动",
"Fullscreen": "全屏",
"Fullscreen": "全屏显示",
"No supported files found. Supported formats: {{formats}}": "未找到支持的文件。支持的格式:{{formats}}",
"Drop to Import Books": "拖放导入书籍",
"Custom": "自定义",
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "无法解析 EPUB 文件。",
"This book format is not supported.": "不支持此书籍格式。",
"Unable to fetch the translation. Please log in first and try again.": "无法获取翻译。请登录后重试。",
"Group": "分组"
"Group": "分组",
"Always on Top": "窗口置顶"
}
@@ -265,5 +265,6 @@
"Failed to parse the EPUB file.": "無法解析 EPUB 文件。",
"This book format is not supported.": "不支援此書籍格式。",
"Unable to fetch the translation. Please log in first and try again.": "無法獲取翻譯。請先登入然後重試。",
"Group": "分組"
"Group": "分組",
"Always on Top": "視窗置頂"
}
@@ -63,6 +63,7 @@
"core:window:allow-start-dragging",
"core:window:allow-toggle-maximize",
"core:window:allow-set-fullscreen",
"core:window:allow-set-always-on-top",
"log:default",
"shell:default",
"process:default",
@@ -14,7 +14,7 @@ import { useSettingsStore } from '@/store/settingsStore';
import { useTranslation } from '@/hooks/useTranslation';
import { getStoragePlanData } from '@/utils/access';
import { navigateToLogin, navigateToProfile } from '@/utils/nav';
import { tauriHandleToggleFullScreen } from '@/utils/window';
import { tauriHandleSetAlwaysOnTop, tauriHandleToggleFullScreen } from '@/utils/window';
import { QuotaType } from '@/types/user';
import MenuItem from '@/components/MenuItem';
import Quota from '@/components/Quota';
@@ -32,6 +32,7 @@ const SettingsMenu: React.FC<BookMenuProps> = ({ setIsDropdownOpen }) => {
const [quotas, setQuotas] = React.useState<QuotaType[]>([]);
const [isAutoUpload, setIsAutoUpload] = useState(settings.autoUpload);
const [isAutoCheckUpdates, setIsAutoCheckUpdates] = useState(settings.autoCheckUpdates);
const [isAlwaysOnTop, setIsAlwaysOnTop] = useState(settings.alwaysOnTop);
const [isScreenWakeLock, setIsScreenWakeLock] = useState(settings.screenWakeLock);
const [isAutoImportBooksOnOpen, setIsAutoImportBooksOnOpen] = useState(
settings.autoImportBooksOnOpen,
@@ -66,6 +67,15 @@ const SettingsMenu: React.FC<BookMenuProps> = ({ setIsDropdownOpen }) => {
setIsDropdownOpen?.(false);
};
const toggleAlwaysOnTop = () => {
settings.alwaysOnTop = !settings.alwaysOnTop;
setSettings(settings);
saveSettings(envConfig, settings);
setIsAlwaysOnTop(settings.alwaysOnTop);
tauriHandleSetAlwaysOnTop(settings.alwaysOnTop);
setIsDropdownOpen?.(false);
};
const toggleAutoUploadBooks = () => {
settings.autoUpload = !settings.autoUpload;
setSettings(settings);
@@ -174,13 +184,20 @@ const SettingsMenu: React.FC<BookMenuProps> = ({ setIsDropdownOpen }) => {
onClick={toggleAutoCheckUpdates}
/>
)}
<hr className='border-base-200 my-1' />
{appService?.hasWindow && <MenuItem label={_('Fullscreen')} onClick={handleFullScreen} />}
{appService?.hasWindow && (
<MenuItem
label={_('Always on Top')}
icon={isAlwaysOnTop ? <MdCheck className='text-base-content' /> : undefined}
onClick={toggleAlwaysOnTop}
/>
)}
<MenuItem
label={_('Keep Screen Awake')}
icon={isScreenWakeLock ? <MdCheck className='text-base-content' /> : undefined}
onClick={toggleScreenWakeLock}
/>
<hr className='border-base-200 my-1' />
{appService?.hasWindow && <MenuItem label={_('Fullscreen')} onClick={handleFullScreen} />}
<MenuItem label={_('Reload Page')} onClick={handleReloadPage} />
<hr className='border-base-200 my-1' />
{isWebApp && <MenuItem label={_('Download Readest')} onClick={downloadReadest} />}
+4 -1
View File
@@ -31,7 +31,7 @@ import { useDemoBooks } from './hooks/useDemoBooks';
import { useBooksSync } from './hooks/useBooksSync';
import { useScreenWakeLock } from '@/hooks/useScreenWakeLock';
import { useOpenWithBooks } from '@/hooks/useOpenWithBooks';
import { tauriQuitApp } from '@/utils/window';
import { tauriHandleSetAlwaysOnTop, tauriQuitApp } from '@/utils/window';
import { AboutWindow } from '@/components/AboutWindow';
import { Toast } from '@/components/Toast';
@@ -104,6 +104,9 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP
await checkForAppUpdates(_);
}
};
if (settings.alwaysOnTop) {
tauriHandleSetAlwaysOnTop(settings.alwaysOnTop);
}
doCheckAppUpdates();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [settings]);
@@ -34,6 +34,7 @@ export const BOOK_UNGROUPED_ID = '';
export const DEFAULT_SYSTEM_SETTINGS: Partial<SystemSettings> = {
keepLogin: false,
autoUpload: true,
alwaysOnTop: false,
autoCheckUpdates: true,
screenWakeLock: true,
autoImportBooksOnOpen: false,
+1
View File
@@ -22,6 +22,7 @@ export interface SystemSettings {
keepLogin: boolean;
autoUpload: boolean;
alwaysOnTop: boolean;
autoCheckUpdates: boolean;
screenWakeLock: boolean;
autoImportBooksOnOpen: boolean;
+10
View File
@@ -38,6 +38,16 @@ export const tauriHandleToggleFullScreen = async () => {
await currentWindow.setFullscreen(newIsFullscreen);
};
export const tauriHandleSetAlwaysOnTop = async (isAlwaysOnTop: boolean) => {
const currentWindow = getCurrentWindow();
await currentWindow.setAlwaysOnTop(isAlwaysOnTop);
};
export const tauriGetAlwaysOnTop = async () => {
const currentWindow = getCurrentWindow();
return await currentWindow.isAlwaysOnTop();
};
export const tauriHandleOnWindowFocus = async (callback: () => void) => {
const currentWindow = getCurrentWindow();
return currentWindow.listen(TauriEvent.WINDOW_FOCUS, async () => {