diff --git a/app/UI.hs b/app/UI.hs index 8c51fa1..358e8f3 100644 --- a/app/UI.hs +++ b/app/UI.hs @@ -1,11 +1,13 @@ {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} -- | The GTK4 / WebKit front end. The book's structure loads in the background; -- chapters are rendered lazily (and cached) the first time they're visited, -- with neighbours preloaded so arrow-key paging feels instant. module UI (runApp) where +import Data.Bifunctor (first) import Control.Concurrent.Async (async) import Control.Concurrent.STM (TVar, atomically, modifyTVar', newTVarIO, readTVarIO, writeTVar, readTVar, modifyTVar') import Data.List (minimumBy) @@ -18,6 +20,8 @@ import qualified Data.Text as T import Data.Word (Word32) import Data.Bits ((.|.)) import Control.Exception (SomeException, try) +import Control.Monad.Except +import Control.Monad.IO.Class (liftIO) import EpubParser (EpubEnv, openEpub) -- brings the `BookInfo EpubEnv` instance into scope import qualified GI.GLib as GLib import qualified GI.GLib.Constants as GLibConst @@ -272,18 +276,30 @@ epPicker :: Maybe Gtk.ApplicationWindow -> Ctx -> IO () epPicker window ctx = do dg <- Gtk.fileDialogNew Gtk.fileDialogOpen dg window (Nothing :: Maybe Gio.Cancellable) $ Just $ \_ result -> do - outcome <- try (Gtk.fileDialogOpenFinish dg result) :: IO (Either SomeException Gio.File) - case outcome of - Left _err -> putStrLn "picker cancelled or failed" - Right file -> do - mp <- Gio.fileGetPath file - case mp of - Nothing -> putStrLn "selected file has no local path" - Just path -> do - env <- openEpub path - case env of - Left err -> putStrLn ("open failed: " ++ err) - Right e -> loadBook e ctx + outcome <- runExceptT $ do + file <- pickerGetFile dg result + path <- getFilePath file + + env <- extractEnv path + + liftIO $ loadBook env ctx + + either putStrLn pure outcome + +pickerGetFile :: Gtk.FileDialog -> Gio.AsyncResult -> ExceptT String IO Gio.File +pickerGetFile dg result = + ExceptT $ first (const "picker cancelled or failed") + <$> (try (Gtk.fileDialogOpenFinish dg result) :: IO (Either SomeException Gio.File)) + +getFilePath :: Gio.File -> ExceptT String IO FilePath +getFilePath file = do + mp <- liftIO $ Gio.fileGetPath file + maybe (throwError "selected file has no local path") pure mp + + +extractEnv :: FilePath -> ExceptT String IO EpubEnv +extractEnv path = + ExceptT $ first ("open failed: " ++) <$> openEpub path -------------------------------------------------------------------------------- -- Navigation + rendering