packages feed

espial-0.0.37: src/Model/FileBookmark.hs

module Model.FileBookmark where

import ClassyPrelude.Yesod hiding (Value, exists, groupBy, on, (<=.), (==.), (>=.), (||.))
import Data.Aeson qualified as A
import Data.Aeson.Types qualified as A (parseFail)
import Model
import Model.Custom
import Types

-- * FileBookmarks

data FileBookmark = FileBookmark
  { fileBookmarkHref :: !Text,
    fileBookmarkDescription :: !Text,
    fileBookmarkExtended :: !Text,
    fileBookmarkTime :: !UTCTime,
    fileBookmarkShared :: !Bool,
    fileBookmarkToRead :: !Bool,
    fileBookmarkSelected :: !(Maybe Bool),
    fileBookmarkArchiveHref :: !(Maybe Text),
    fileBookmarkTags :: !Text
  }
  deriving (Show, Eq, Typeable, Ord)

instance FromJSON FileBookmark where
  parseJSON (Object o) =
    FileBookmark
      <$> o
      .: "href"
      <*> (parseDescriptionValue =<< o .: "description")
      <*> o
      .: "extended"
      <*> o
      .: "time"
      <*> (boolFromYesNo <$> o .: "shared")
      <*> (boolFromYesNo <$> o .: "toread")
      <*> (o A..:? "selected")
      <*> (o A..:? "archive_url")
      <*> (o .: "tags")
    where
      parseDescriptionValue (String t) = pure t
      parseDescriptionValue (Bool _) = pure ""
      parseDescriptionValue _ = A.parseFail "bad parse"
  parseJSON _ = A.parseFail "bad parse"

instance ToJSON FileBookmark where
  toJSON FileBookmark {..} =
    object
      [ "href" .= fileBookmarkHref,
        "description" .= fileBookmarkDescription,
        "extended" .= fileBookmarkExtended,
        "time" .= fileBookmarkTime,
        "shared" .= boolToYesNo fileBookmarkShared,
        "toread" .= boolToYesNo fileBookmarkToRead,
        "selected" .= fileBookmarkSelected,
        "archive_url" .= fileBookmarkArchiveHref,
        "tags" .= fileBookmarkTags
      ]
  toEncoding FileBookmark {..} =
    A.pairs
      ( "href" .= fileBookmarkHref
      <> "description" .= fileBookmarkDescription
      <> "extended" .= fileBookmarkExtended
      <> "time" .= fileBookmarkTime
      <> "shared" .= boolToYesNo fileBookmarkShared
      <> "toread" .= boolToYesNo fileBookmarkToRead
      <> "selected" .= fileBookmarkSelected
      <> "archive_url" .= fileBookmarkArchiveHref
      <> "tags" .= fileBookmarkTags
      )

boolFromYesNo :: Text -> Bool
boolFromYesNo "yes" = True
boolFromYesNo _ = False

boolToYesNo :: Bool -> Text
boolToYesNo True = "yes"
boolToYesNo _ = "no"

fileBookmarkToBookmark :: UserId -> FileBookmark -> IO Bookmark
fileBookmarkToBookmark user FileBookmark {..} = do
  slug <- mkBmSlug
  pure
    $ Bookmark
      { bookmarkUserId = user,
        bookmarkSlug = slug,
        bookmarkHref = fileBookmarkHref,
        bookmarkDescription = fileBookmarkDescription,
        bookmarkExtended = fileBookmarkExtended,
        bookmarkTime = fileBookmarkTime,
        bookmarkShared = fileBookmarkShared,
        bookmarkToRead = fileBookmarkToRead,
        bookmarkSelected = Just True == fileBookmarkSelected,
        bookmarkArchiveHref = fileBookmarkArchiveHref
      }

bookmarkTofileBookmark :: Bookmark -> Text -> FileBookmark
bookmarkTofileBookmark Bookmark {..} tags =
  FileBookmark
    { fileBookmarkHref = bookmarkHref,
      fileBookmarkDescription = bookmarkDescription,
      fileBookmarkExtended = bookmarkExtended,
      fileBookmarkTime = bookmarkTime,
      fileBookmarkShared = bookmarkShared,
      fileBookmarkToRead = bookmarkToRead,
      fileBookmarkSelected = Just bookmarkSelected,
      fileBookmarkArchiveHref = bookmarkArchiveHref,
      fileBookmarkTags = tags
    }

readFileBookmarks :: (MonadIO m) => FilePath -> m (Either String [FileBookmark])
readFileBookmarks fpath =
  A.eitherDecode' . fromStrict <$> readFile fpath

insertFileBookmarks :: Key User -> [FileBookmark] -> DB Int
insertFileBookmarks userId fmarks = do
  bmarks <- liftIO $ mapM (fileBookmarkToBookmark userId) fmarks
  mbids <- mapM insertUnique bmarks
  mapM_ (void . insertUnique)
    $ concatMap (uncurry (mkBookmarkTags userId))
    $ catMaybes
    $ zipWith
      (\mbid tags -> (,tags) <$> mbid)
      mbids
      (extractTags <$> fmarks)
  pure (length bmarks)
  where
    extractTags = words . fileBookmarkTags

getFileBookmarks :: Key User -> DB [FileBookmark]
getFileBookmarks user = do
  marks <- allUserBookmarks user
  pure $ fmap (\(bm, t) -> bookmarkTofileBookmark (entityVal bm) t) marks

exportFileBookmarks :: Key User -> FilePath -> DB ()
exportFileBookmarks user fpath =
  liftIO . A.encodeFile fpath =<< getFileBookmarks user