forked from akai/readest
Various fixes (#56)
* Add missing translation for progress synced toast * Graceful shutdown after all books closed, closes #50 * Throttle app update check to once every day * Avoid infinite loop to redirect to the login page, closes #54
This commit is contained in:
@@ -26,5 +26,6 @@ module.exports = {
|
||||
},
|
||||
metadata: {},
|
||||
allowDynamicKeys: true,
|
||||
removeUnusedKeys: true,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"Parallel Read": "Paralleles Lesen",
|
||||
"Published:": "Veröffentlicht:",
|
||||
"Publisher:": "Verlag:",
|
||||
"Reading progress synced": "Lesevorgang synchronisiert",
|
||||
"Reload Page": "Seite neu laden",
|
||||
"Reveal in File Explorer": "Im Datei-Explorer anzeigen",
|
||||
"Reveal in Finder": "Im Finder anzeigen",
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"Parallel Read": "Lectura paralela",
|
||||
"Published:": "Publicado:",
|
||||
"Publisher:": "Editorial:",
|
||||
"Reading progress synced": "Progreso de lectura sincronizado",
|
||||
"Reload Page": "Recargar página",
|
||||
"Reveal in File Explorer": "Mostrar en el Explorador de archivos",
|
||||
"Reveal in Finder": "Mostrar en Finder",
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"Parallel Read": "Lecture parallèle",
|
||||
"Published:": "Publié :",
|
||||
"Publisher:": "Éditeur :",
|
||||
"Reading progress synced": "Progression de lecture synchronisée",
|
||||
"Reload Page": "Recharger la page",
|
||||
"Reveal in File Explorer": "Afficher dans l'explorateur de fichiers",
|
||||
"Reveal in Finder": "Afficher dans le Finder",
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"Parallel Read": "Baca Paralel",
|
||||
"Published:": "Diterbitkan:",
|
||||
"Publisher:": "Penerbit:",
|
||||
"Reading progress synced": "Progres membaca disinkronkan",
|
||||
"Reload Page": "Muat Ulang Halaman",
|
||||
"Reveal in File Explorer": "Tampilkan di File Explorer",
|
||||
"Reveal in Finder": "Tampilkan di Finder",
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"Parallel Read": "Lettura parallela",
|
||||
"Published:": "Pubblicato:",
|
||||
"Publisher:": "Editore:",
|
||||
"Reading progress synced": "Progresso lettura sincronizzato",
|
||||
"Reload Page": "Ricarica pagina",
|
||||
"Reveal in File Explorer": "Mostra in Esplora file",
|
||||
"Reveal in Finder": "Mostra nel Finder",
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"Parallel Read": "並列読書",
|
||||
"Published:": "出版日:",
|
||||
"Publisher:": "出版社:",
|
||||
"Reading progress synced": "読書進捗が同期されました",
|
||||
"Reload Page": "ページを再読み込み",
|
||||
"Reveal in File Explorer": "エクスプローラーで表示",
|
||||
"Reveal in Finder": "Finderで表示",
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"Parallel Read": "병렬 읽기",
|
||||
"Published:": "출판일:",
|
||||
"Publisher:": "출판사:",
|
||||
"Reading progress synced": "읽기 진행 상황 동기화됨",
|
||||
"Reload Page": "페이지 새로고침",
|
||||
"Reveal in File Explorer": "파일 탐색기에서 표시",
|
||||
"Reveal in Finder": "Finder에서 표시",
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"Parallel Read": "Leitura Paralela",
|
||||
"Published:": "Publicado:",
|
||||
"Publisher:": "Editora:",
|
||||
"Reading progress synced": "Progresso de leitura sincronizado",
|
||||
"Reload Page": "Recarregar Página",
|
||||
"Reveal in File Explorer": "Mostrar no Explorador de Arquivos",
|
||||
"Reveal in Finder": "Mostrar no Finder",
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"Parallel Read": "Параллельное чтение",
|
||||
"Published:": "Опубликовано:",
|
||||
"Publisher:": "Издатель:",
|
||||
"Reading progress synced": "Синхронизирован прогресс чтения",
|
||||
"Reload Page": "Перезагрузить страницу",
|
||||
"Reveal in File Explorer": "Показать в проводнике",
|
||||
"Reveal in Finder": "Показать в Finder",
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"Parallel Read": "Paralel Okuma",
|
||||
"Published:": "Yayınlanma:",
|
||||
"Publisher:": "Yayıncı:",
|
||||
"Reading progress synced": "Okuma ilerlemesi senkronize edildi",
|
||||
"Reload Page": "Sayfayı Yenile",
|
||||
"Reveal in File Explorer": "Dosya Gezgininde Göster",
|
||||
"Reveal in Finder": "Finder'da Göster",
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"Parallel Read": "Đọc song song",
|
||||
"Published:": "Xuất bản:",
|
||||
"Publisher:": "Nhà xuất bản:",
|
||||
"Reading progress synced": "Tiến độ đọc đã được đồng bộ",
|
||||
"Reload Page": "Tải lại trang",
|
||||
"Reveal in File Explorer": "Hiển thị trong File Explorer",
|
||||
"Reveal in Finder": "Hiển thị trong Finder",
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"Parallel Read": "并排阅读",
|
||||
"Published:": "出版日期",
|
||||
"Publisher:": "出版商",
|
||||
"Reading progress synced": "阅读进度已同步",
|
||||
"Reload Page": "重新加载页面",
|
||||
"Reveal in File Explorer": "在文件管理器中显示",
|
||||
"Reveal in Finder": "在访达中显示",
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"Parallel Read": "並排閱讀",
|
||||
"Published:": "出版日期",
|
||||
"Publisher:": "出版商",
|
||||
"Reading progress synced": "閱讀進度已同步",
|
||||
"Reload Page": "重新載入頁面",
|
||||
"Reveal in File Explorer": "在檔案管理器中顯示",
|
||||
"Reveal in Finder": "在訪達中顯示",
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
"shell:default",
|
||||
"updater:default",
|
||||
"process:default",
|
||||
"process:allow-exit",
|
||||
"process:allow-restart",
|
||||
"cli:default",
|
||||
"oauth:allow-start",
|
||||
|
||||
@@ -73,6 +73,7 @@ async fn start_server(window: Window) -> Result<u16, String> {
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
let builder = tauri::Builder::default()
|
||||
.plugin(tauri_plugin_process::init())
|
||||
.plugin(tauri_plugin_updater::Builder::new().build())
|
||||
.plugin(tauri_plugin_oauth::init())
|
||||
.invoke_handler(tauri::generate_handler![start_server])
|
||||
@@ -137,10 +138,14 @@ pub fn run() {
|
||||
#[cfg(target_os = "macos")]
|
||||
let win_builder = win_builder
|
||||
.decorations(true)
|
||||
.title_bar_style(TitleBarStyle::Overlay).title("");
|
||||
.title_bar_style(TitleBarStyle::Overlay)
|
||||
.title("");
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
let win_builder = win_builder.decorations(false).transparent(true).title("Readest");
|
||||
let win_builder = win_builder
|
||||
.decorations(false)
|
||||
.transparent(true)
|
||||
.title("Readest");
|
||||
|
||||
win_builder.build().unwrap();
|
||||
// let win = win_builder.build().unwrap();
|
||||
|
||||
@@ -9,15 +9,17 @@ import { FcGoogle } from 'react-icons/fc';
|
||||
import { FaApple } from 'react-icons/fa';
|
||||
import { VscAzure } from 'react-icons/vsc';
|
||||
import { FaGithub } from 'react-icons/fa';
|
||||
import { IoArrowBack } from 'react-icons/io5';
|
||||
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { supabase } from '@/utils/supabase';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { isTauriAppPlatform } from '@/services/environment';
|
||||
import { start, cancel, onUrl, onInvalidUrl } from '@fabianlars/tauri-plugin-oauth';
|
||||
import { openUrl } from '@tauri-apps/plugin-opener';
|
||||
import { handleAuthCallback } from '@/helpers/auth';
|
||||
import { IoArrowBack } from 'react-icons/io5';
|
||||
|
||||
type OAuthProvider = 'google' | 'apple' | 'azure' | 'github';
|
||||
|
||||
@@ -46,7 +48,9 @@ const ProviderLogin: React.FC<ProviderLoginProp> = ({ provider, handleSignIn, Ic
|
||||
export default function AuthPage() {
|
||||
const router = useRouter();
|
||||
const { login } = useAuth();
|
||||
const { envConfig } = useEnv();
|
||||
const { isDarkMode } = useTheme();
|
||||
const { settings, setSettings, saveSettings } = useSettingsStore();
|
||||
const [port, setPort] = useState<number | null>(null);
|
||||
const isOAuthServerRunning = useRef(false);
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
@@ -109,6 +113,14 @@ export default function AuthPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleGoBack = () => {
|
||||
// Keep login false to avoid infinite loop to redirect to the login page
|
||||
settings.keepLogin = false;
|
||||
setSettings(settings);
|
||||
saveSettings(envConfig, settings);
|
||||
router.back();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTauriAppPlatform()) return;
|
||||
if (isOAuthServerRunning.current) return;
|
||||
@@ -147,7 +159,7 @@ export default function AuthPage() {
|
||||
return isTauriAppPlatform() ? (
|
||||
<div className='flex pt-11'>
|
||||
<button
|
||||
onClick={() => router.back()}
|
||||
onClick={handleGoBack}
|
||||
className='btn btn-ghost fixed left-3 top-11 h-8 min-h-8 w-8 p-0'
|
||||
>
|
||||
<IoArrowBack size={20} />
|
||||
@@ -191,7 +203,7 @@ export default function AuthPage() {
|
||||
) : (
|
||||
<div style={{ maxWidth: '420px', margin: 'auto', padding: '2rem', paddingTop: '4rem' }}>
|
||||
<button
|
||||
onClick={() => router.back()}
|
||||
onClick={handleGoBack}
|
||||
className='btn btn-ghost fixed left-10 top-6 h-8 min-h-8 w-8 p-0'
|
||||
>
|
||||
<IoArrowBack size={20} />
|
||||
|
||||
@@ -11,7 +11,8 @@ import { useReaderStore } from '@/store/readerStore';
|
||||
import { useSidebarStore } from '@/store/sidebarStore';
|
||||
import { SystemSettings } from '@/types/settings';
|
||||
import { parseOpenWithFiles } from '@/helpers/cli';
|
||||
import { tauriHandleClose } from '@/utils/window';
|
||||
import { tauriHandleClose, tauriHandleOnCloseWindow } from '@/utils/window';
|
||||
import { isTauriAppPlatform } from '@/services/environment';
|
||||
import { uniqueId } from '@/utils/misc';
|
||||
import { navigateToLibrary } from '@/utils/nav';
|
||||
import { BOOK_IDS_SEPARATOR } from '@/services/constants';
|
||||
@@ -61,6 +62,7 @@ const ReaderContent: React.FC<{ ids?: string; settings: SystemSettings }> = ({ i
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isTauriAppPlatform()) tauriHandleOnCloseWindow(handleCloseBooks);
|
||||
window.addEventListener('beforeunload', handleCloseBooks);
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', handleCloseBooks);
|
||||
@@ -68,12 +70,12 @@ const ReaderContent: React.FC<{ ids?: string; settings: SystemSettings }> = ({ i
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [bookKeys]);
|
||||
|
||||
const saveBookConfig = (bookKey: string) => {
|
||||
const saveBookConfig = async (bookKey: string) => {
|
||||
const config = getConfig(bookKey);
|
||||
const { book } = getBookData(bookKey) || {};
|
||||
const { isPrimary } = getViewState(bookKey) || {};
|
||||
if (isPrimary && book && config) {
|
||||
saveConfig(envConfig, bookKey, config, settings);
|
||||
await saveConfig(envConfig, bookKey, config, settings);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -81,7 +83,7 @@ const ReaderContent: React.FC<{ ids?: string; settings: SystemSettings }> = ({ i
|
||||
console.log('Closing book', bookKey);
|
||||
getView(bookKey)?.close();
|
||||
getView(bookKey)?.remove();
|
||||
saveBookConfig(bookKey);
|
||||
await saveBookConfig(bookKey);
|
||||
clearViewState(bookKey);
|
||||
};
|
||||
|
||||
@@ -90,11 +92,9 @@ const ReaderContent: React.FC<{ ids?: string; settings: SystemSettings }> = ({ i
|
||||
navigateToLibrary(router);
|
||||
};
|
||||
|
||||
const handleCloseBooks = () => {
|
||||
bookKeys.forEach((key) => {
|
||||
saveConfigAndCloseBook(key);
|
||||
});
|
||||
saveSettings(envConfig, settings);
|
||||
const handleCloseBooks = async () => {
|
||||
await Promise.all(bookKeys.map((key) => saveConfigAndCloseBook(key)));
|
||||
await saveSettings(envConfig, settings);
|
||||
};
|
||||
|
||||
const handleCloseBooksToLibrary = () => {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { BookConfig } from '@/types/book';
|
||||
import { useBookDataStore } from '@/store/bookDataStore';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { deserializeConfig, serializeConfig } from '@/utils/serializer';
|
||||
import { DEFAULT_BOOK_SEARCH_CONFIG, SYNC_PROGRESS_INTERVAL_SEC } from '@/services/constants';
|
||||
|
||||
@@ -12,6 +13,7 @@ export const useProgressSync = (
|
||||
bookKey: string,
|
||||
setToastMessage?: React.Dispatch<React.SetStateAction<string>>,
|
||||
) => {
|
||||
const _ = useTranslation();
|
||||
const { getConfig, setConfig } = useBookDataStore();
|
||||
const { getView } = useReaderStore();
|
||||
const { settings } = useSettingsStore();
|
||||
@@ -81,7 +83,7 @@ export const useProgressSync = (
|
||||
const configFraction = config!.progress![0] / config!.progress![1];
|
||||
if (syncedFraction > configFraction) {
|
||||
view?.goToFraction(syncedFraction);
|
||||
setToastMessage?.('Progress synced');
|
||||
setToastMessage?.(_('Reading progress synced'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import { check } from '@tauri-apps/plugin-updater';
|
||||
import { ask } from '@tauri-apps/plugin-dialog';
|
||||
import { relaunch } from '@tauri-apps/plugin-process';
|
||||
import { CHECK_UPDATE_INTERVAL_SEC } from '@/services/constants';
|
||||
|
||||
const LAST_CHECK_KEY = 'lastAppUpdateCheck';
|
||||
|
||||
export const checkForAppUpdates = async () => {
|
||||
const lastCheck = localStorage.getItem(LAST_CHECK_KEY);
|
||||
const now = Date.now();
|
||||
if (lastCheck && now - parseInt(lastCheck, 10) < CHECK_UPDATE_INTERVAL_SEC * 1000) return;
|
||||
localStorage.setItem(LAST_CHECK_KEY, now.toString());
|
||||
|
||||
const update = await check();
|
||||
console.log('update found', update);
|
||||
if (update) {
|
||||
const yes = await ask(
|
||||
`
|
||||
|
||||
@@ -89,3 +89,4 @@ export const READEST_WEB_BASE_URL = 'https://web.readest.com';
|
||||
|
||||
export const SYNC_PROGRESS_INTERVAL_SEC = 60;
|
||||
export const SYNC_NOTES_INTERVAL_SEC = 60;
|
||||
export const CHECK_UPDATE_INTERVAL_SEC = 24 * 60 * 60;
|
||||
|
||||
@@ -68,8 +68,8 @@ export const useBookDataStore = create<BookDataState>((set, get) => ({
|
||||
library.unshift(book);
|
||||
setLibrary(library);
|
||||
config.updatedAt = Date.now();
|
||||
appService.saveBookConfig(book, config, settings);
|
||||
appService.saveLibraryBooks(library);
|
||||
await appService.saveBookConfig(book, config, settings);
|
||||
await appService.saveLibraryBooks(library);
|
||||
},
|
||||
updateBooknotes: (key: string, booknotes: BookNote[]) => {
|
||||
let updatedConfig: BookConfig | undefined;
|
||||
|
||||
@@ -12,3 +12,15 @@ export const tauriHandleClose = async () => {
|
||||
const { getCurrentWindow } = await import('@tauri-apps/api/window');
|
||||
getCurrentWindow().close();
|
||||
};
|
||||
|
||||
export const tauriHandleOnCloseWindow = async (callback: () => void) => {
|
||||
const { TauriEvent } = await import('@tauri-apps/api/event');
|
||||
const { getCurrentWindow } = await import('@tauri-apps/api/window');
|
||||
const { exit } = await import('@tauri-apps/plugin-process');
|
||||
const currentWindow = getCurrentWindow();
|
||||
return currentWindow.listen(TauriEvent.WINDOW_CLOSE_REQUESTED, async () => {
|
||||
await callback();
|
||||
console.log('exit app');
|
||||
await exit(0);
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user