forked from marko/svitak
explicit types
This commit is contained in:
parent
7cd9e79c94
commit
55b04ecf44
6 changed files with 51 additions and 36 deletions
|
|
@ -26,8 +26,8 @@ import qualified Data.Text as T
|
|||
import qualified Data.Text.Encoding as TE
|
||||
import System.FilePath (normalise, takeDirectory, (</>))
|
||||
import Text.HTML.Scalpel (Scraper, ScraperT, anySelector, attr, chroot, chroots, html, innerHTML, scrapeStringLike, text)
|
||||
import Types (Chapter (..), EpubAction, EpubEnv (..))
|
||||
import Navigation (navFinder)
|
||||
import Types (Chapter (..), EpubAction, EpubEnv (..), InternalPath(..), ChapterIndex(..))
|
||||
import Navigation (navFinder, lndmkScraper)
|
||||
|
||||
openEpub :: FilePath -> IO (Either String EpubEnv)
|
||||
openEpub path = do
|
||||
|
|
@ -74,7 +74,7 @@ ncxScraper = chroots "navPoint" $ do
|
|||
title <- T.strip <$> text "navLabel"
|
||||
pure (path, title)
|
||||
|
||||
resolveSpine :: EpubAction [(FilePath, T.Text)]
|
||||
resolveSpine :: EpubAction [(InternalPath, T.Text)]
|
||||
resolveSpine = do
|
||||
env <- ask
|
||||
let xmlStr = opfXml env
|
||||
|
|
@ -99,12 +99,12 @@ resolveSpine = do
|
|||
|
||||
pure $ fromMaybe [] (scrapeStringLike raw ncxScraper)
|
||||
|
||||
pure [(p, fromMaybe ("Chapter " <> T.pack (show (i :: Int))) (lookup p tocMap)) | (ref, i) <- zip refs [1..] , let ident = DSpin.siIdRef ref , Just p <- [lookupHref ident]]
|
||||
pure [(InternalPath p, fromMaybe ("Chapter " <> T.pack (show (i :: Int))) (lookup p tocMap)) | (ref, i) <- zip refs [1..] , let ident = DSpin.siIdRef ref , Just p <- [lookupHref ident]]
|
||||
|
||||
_ -> pure []
|
||||
|
||||
getChapter :: (FilePath, T.Text) -> Int -> EpubAction Chapter
|
||||
getChapter (filename, tocTitle) idx = do
|
||||
getChapter :: (InternalPath, T.Text) -> ChapterIndex -> EpubAction Chapter
|
||||
getChapter (InternalPath filename, tocTitle) idx = do
|
||||
env <- ask
|
||||
let full = normalise (baseDir env </> filename)
|
||||
case findEntryByPath full (archive env) of
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Navigation
|
|||
where
|
||||
|
||||
import Text.HTML.Scalpel (Scraper, attr, (@:), (@=), chroot, chroots, text, match)
|
||||
import Types (NavItem(..))
|
||||
import Types (NavItem(..), EpubType (..), InternalPath(..))
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Control.Applicative (optional)
|
||||
import Control.Monad (guard)
|
||||
|
|
@ -21,21 +21,21 @@ import qualified Data.Text as T
|
|||
navFinder :: Scraper String FilePath
|
||||
navFinder = attr "href" ("item" @: ["properties" @= "nav"])
|
||||
|
||||
itemScraper :: [T.Text] -> Scraper T.Text NavItem
|
||||
itemScraper :: [EpubType] -> Scraper T.Text NavItem
|
||||
itemScraper allowedTypes = do
|
||||
label <- text "a"
|
||||
path <- attr "href" "a"
|
||||
rawTypes <- fromMaybe "" <$> optional (attr "epub:type" "a")
|
||||
|
||||
let foundTypes = T.words rawTypes
|
||||
let foundTypes = map EpubType $ T.words rawTypes
|
||||
guard $ any (`elem` allowedTypes) foundTypes
|
||||
|
||||
pure $ NavItem label (T.unpack path) foundTypes
|
||||
pure $ NavItem label (InternalPath $ T.unpack path) foundTypes
|
||||
|
||||
lndmkScraper :: [T.Text] -> Scraper T.Text [NavItem]
|
||||
lndmkScraper :: [EpubType] -> Scraper T.Text [NavItem]
|
||||
lndmkScraper labels =
|
||||
chroot ("nav" @: [match navPredicate]) $ chroots "li" (itemScraper labels)
|
||||
|
||||
where
|
||||
navPredicate "epub:type" xs = T.pack xs `elem` labels
|
||||
navPredicate "epub:type" xs = EpubType (T.pack xs) `elem` labels
|
||||
navPredicate _ _ = False
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ import qualified Data.Text.IO as TIO
|
|||
import System.Directory (XdgDirectory (XdgData), createDirectoryIfMissing, getXdgDirectory)
|
||||
import System.FilePath ((</>))
|
||||
import Text.Read (readMaybe)
|
||||
import Types (ChapterIndex(..))
|
||||
|
||||
saveLastRead :: FilePath -> Int -> IO ()
|
||||
saveLastRead bPath chapterIdx = do
|
||||
saveLastRead :: FilePath -> ChapterIndex -> IO ()
|
||||
saveLastRead bPath (ChapterIndex chapterIdx) = do
|
||||
dataDir <- getXdgDirectory XdgData "svitak"
|
||||
createDirectoryIfMissing True dataDir
|
||||
|
||||
|
|
@ -22,7 +23,7 @@ saveLastRead bPath chapterIdx = do
|
|||
TIO.writeFile configFile content
|
||||
putStrLn $ "Progress saved to: " ++ configFile
|
||||
|
||||
loadLastRead :: IO (Maybe (FilePath, Int))
|
||||
loadLastRead :: IO (Maybe (FilePath, ChapterIndex))
|
||||
loadLastRead = do
|
||||
dataDir <- getXdgDirectory XdgData "svitak"
|
||||
let configFile = dataDir </> "last_read.txt"
|
||||
|
|
@ -36,5 +37,5 @@ loadLastRead = do
|
|||
case linesOfFile of
|
||||
[path, idxStr]
|
||||
| Just idx <- readMaybe (T.unpack idxStr) ->
|
||||
pure $ Just (T.unpack path, idx)
|
||||
pure $ Just (T.unpack path, ChapterIndex idx)
|
||||
_ -> pure Nothing
|
||||
|
|
|
|||
12
app/State.hs
12
app/State.hs
|
|
@ -6,11 +6,11 @@ module State
|
|||
)
|
||||
where
|
||||
|
||||
import Types (AppState(..), Chapter(chapterTags, chapterTitle), ChapterView(..), EpubEnv)
|
||||
import Types (AppState(..), Chapter(chapterTags, chapterTitle), ChapterView(..), EpubEnv, ChapterIndex(..))
|
||||
|
||||
-- Get an element from a list without crashing
|
||||
safeGet :: Int -> [a] -> Maybe a
|
||||
safeGet n xs
|
||||
safeGet :: ChapterIndex -> [a] -> Maybe a
|
||||
safeGet (ChapterIndex n) xs
|
||||
| n < 0 = Nothing
|
||||
| otherwise = case drop n xs of
|
||||
(x : _) -> Just x
|
||||
|
|
@ -21,7 +21,7 @@ initialState :: EpubEnv -> AppState
|
|||
initialState env =
|
||||
AppState
|
||||
{ cEnv = env,
|
||||
cIdx = 0,
|
||||
cIdx = ChapterIndex 0,
|
||||
cSpine = [],
|
||||
bTitle = "Svitak - Loading...",
|
||||
zoomlvl = 1.0,
|
||||
|
|
@ -31,10 +31,10 @@ initialState env =
|
|||
}
|
||||
|
||||
-- prepare the data for the UI
|
||||
prepareView :: AppState -> Int -> Maybe ChapterView
|
||||
prepareView :: AppState -> ChapterIndex -> Maybe ChapterView
|
||||
prepareView st index = do
|
||||
chapter <- safeGet index (cAllChapters st)
|
||||
return $ ChapterView
|
||||
pure $ ChapterView
|
||||
{ viewTitle = bTitle st <> " - " <> chapterTitle chapter
|
||||
, viewHtml = chapterTags chapter
|
||||
, viewIdx = index
|
||||
|
|
|
|||
31
app/Types.hs
31
app/Types.hs
|
|
@ -1,3 +1,5 @@
|
|||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||
|
||||
module Types
|
||||
( EpubEnv(..),
|
||||
ChapterView(..),
|
||||
|
|
@ -5,7 +7,10 @@ module Types
|
|||
AppState(..),
|
||||
UserAction(..),
|
||||
EpubAction,
|
||||
NavItem(..)
|
||||
NavItem(..),
|
||||
ChapterIndex(..),
|
||||
InternalPath(..),
|
||||
EpubType(..),
|
||||
)
|
||||
where
|
||||
|
||||
|
|
@ -15,6 +20,16 @@ import qualified Codec.Epub.Data.Metadata as DMeta
|
|||
import qualified Codec.Epub.Data.Manifest as DMan
|
||||
import Control.Concurrent.Async (Async)
|
||||
import Control.Monad.Reader (ReaderT)
|
||||
import Data.String (IsString)
|
||||
|
||||
newtype ChapterIndex = ChapterIndex Int
|
||||
deriving (Show, Eq, Ord, Num)
|
||||
|
||||
newtype InternalPath = InternalPath FilePath
|
||||
deriving (Show, Eq)
|
||||
|
||||
newtype EpubType = EpubType T.Text
|
||||
deriving (Show,Eq, IsString)
|
||||
|
||||
data EpubEnv = EpubEnv
|
||||
{ archive :: Archive,
|
||||
|
|
@ -28,20 +43,20 @@ data EpubEnv = EpubEnv
|
|||
data ChapterView = ChapterView
|
||||
{ viewTitle :: T.Text,
|
||||
viewHtml :: T.Text,
|
||||
viewIdx :: Int
|
||||
viewIdx :: ChapterIndex
|
||||
}
|
||||
|
||||
data Chapter = Chapter
|
||||
{ chapterTitle :: T.Text,
|
||||
chapterTags :: T.Text,
|
||||
chapterIdx :: Int
|
||||
chapterIdx :: ChapterIndex
|
||||
}
|
||||
|
||||
data AppState = AppState
|
||||
{ cEnv :: EpubEnv,
|
||||
cIdx :: Int,
|
||||
cIdx :: ChapterIndex,
|
||||
zoomlvl :: Double,
|
||||
cSpine :: [(FilePath, T.Text)],
|
||||
cSpine :: [(InternalPath, T.Text)],
|
||||
bTitle :: T.Text,
|
||||
cAllChapters :: [Chapter],
|
||||
activeTask :: Maybe (Async ()),
|
||||
|
|
@ -55,10 +70,10 @@ data UserAction
|
|||
| PrevChapter
|
||||
| ZoomIn
|
||||
| ZoomOut
|
||||
| LoadSpecific Int
|
||||
| LoadSpecific ChapterIndex
|
||||
|
||||
data NavItem = NavItem
|
||||
{ navLabel :: T.Text,
|
||||
navPath :: FilePath,
|
||||
navTypes :: [T.Text]
|
||||
navPath :: InternalPath,
|
||||
navTypes :: [EpubType]
|
||||
} deriving (Show)
|
||||
|
|
|
|||
11
app/UI.hs
11
app/UI.hs
|
|
@ -22,10 +22,10 @@ import qualified GI.WebKit as WebKit
|
|||
import Persistence (loadLastRead, saveLastRead)
|
||||
import State (initialState, prepareView)
|
||||
import Text.HTML.Scalpel (Scraper, attr, chroots, text)
|
||||
import Types (AppState (..), ChapterView (..), UserAction (..))
|
||||
import Types (AppState (..), ChapterView (..), UserAction (..), ChapterIndex(..))
|
||||
|
||||
updateUI :: Gtk.ApplicationWindow -> WebKit.WebView -> ChapterView -> Int -> Int -> IO ()
|
||||
updateUI window webView view currentIdx totalCount = do
|
||||
updateUI :: Gtk.ApplicationWindow -> WebKit.WebView -> ChapterView -> ChapterIndex -> Int -> IO ()
|
||||
updateUI window webView view (ChapterIndex currentIdx) totalCount = do
|
||||
let ttext = T.pack (show (currentIdx + 1)) <> "/ " <> T.pack (show totalCount) <> ": " <> viewTitle view
|
||||
Gtk.set window [#title := ttext]
|
||||
WebKit.webViewLoadHtml webView (viewHtml view) Nothing
|
||||
|
|
@ -46,9 +46,8 @@ handleAction stateTVar window webView action = do
|
|||
NextChapter -> cIdx st + 1
|
||||
PrevChapter -> cIdx st - 1
|
||||
LoadSpecific i -> i
|
||||
_ -> cIdx st
|
||||
|
||||
let safeIdx = max 0 (min (totalChapters - 1) nextIdx)
|
||||
let safeIdx = max 0 (min (ChapterIndex (totalChapters - 1)) nextIdx)
|
||||
|
||||
case prepareView st safeIdx of
|
||||
Nothing -> pure ()
|
||||
|
|
@ -67,7 +66,7 @@ initAppServices :: EpubEnv -> TVar AppState -> (UserAction -> IO ()) -> IO ()
|
|||
initAppServices env stateTVar actionHandler = do
|
||||
_ <- async $ do
|
||||
ispine <- runReaderT resolveSpine env
|
||||
allChapters <- runReaderT (zipWithM getChapter ispine [0 ..]) env
|
||||
allChapters <- runReaderT (zipWithM getChapter ispine (map ChapterIndex [0 ..])) env
|
||||
|
||||
let titles = metaTitles (eMetadata env)
|
||||
let bookTitle = case find (\t -> titleType t == Just "main") titles of
|
||||
|
|
|
|||
Loading…
Reference in a new issue