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