refactor epPicker into smaller functions using ExceptT

This commit is contained in:
Marko Andjelic 2026-07-11 15:47:00 +01:00
commit 9f9e59b450
Signed by: marko
GPG key ID: 9C5E99C8C682FB59

View file

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