{-# LANGUAGE UndecidableInstances #-}

module Conduit.Features.Articles.Articles.ListArticles where

import Prelude hiding (get, on)
import Conduit.App.Monad (AppM, runService)
import Conduit.DB.Core (MonadDB (..), mapDBResult)
import Conduit.Features.Account.Common.QueryAssociatedUser (queryAssociatedUser)
import Conduit.Features.Account.DB (User(..))
import Conduit.Features.Account.Types (UserID)
import Conduit.Features.Articles.Common.QueryFavStats (queryFavStats)
import Conduit.Features.Articles.DB (Favorite, mkManyArticles)
import Conduit.Features.Articles.Errors (ArticleError(..))
import Conduit.Features.Articles.Types (ManyArticles(..))
import Conduit.Identity.Auth (AuthedUser(..), maybeWithAuth)
import Conduit.Utils ((.-))
import Data.List (lookup)
import Data.Text.Lazy.Builder qualified as TB
import Database.Esqueleto.Experimental (exists, from, groupBy, in_, just, leftJoin, limit, offset, on, orderBy, select, subSelectList, table, val, valList, where_, (:&) (..), (==.))
import Database.Esqueleto.Experimental qualified as E
import Database.Esqueleto.Internal.Internal (unsafeSqlValue)
import Relude.Extra (bimapBoth)
import UnliftIO (MonadUnliftIO)
import Web.Scotty.Trans (ActionT, ScottyT, captureParams, get, json)

data FilterOps = FilterOps
  { FilterOps -> Maybe Text
filterTag    :: Maybe  Text
  , FilterOps -> Maybe [Text]
filterAuthor :: Maybe [Text]
  , FilterOps -> Maybe Text
filterFavBy  :: Maybe  Text
  , FilterOps -> Int64
filterLimit  :: Int64
  , FilterOps -> Int64
filterOffset :: Int64
  }

handleListArticles :: ScottyT AppM ()
handleListArticles :: ScottyT AppM ()
handleListArticles = RoutePattern -> ActionT AppM () -> ScottyT AppM ()
forall (m :: * -> *).
MonadUnliftIO m =>
RoutePattern -> ActionT m () -> ScottyT m ()
get RoutePattern
"/api/articles/" (ActionT AppM () -> ScottyT AppM ())
-> ActionT AppM () -> ScottyT AppM ()
forall a b. (a -> b) -> a -> b
$ (Maybe AuthedUser -> ActionT AppM ()) -> ActionT AppM ()
forall (m :: * -> *) c.
(MonadIO m, Has JWTInfo c m) =>
(Maybe AuthedUser -> ActionT m ()) -> ActionT m ()
maybeWithAuth \Maybe AuthedUser
user -> do
  FilterOps
filterOps <- ActionT AppM FilterOps
parseFilterOps
  ManyArticles
articles <- AppM (Either ArticleError ManyArticles)
-> ActionT AppM ManyArticles
forall e a. FeatureError e => AppM (Either e a) -> ActionT AppM a
runService (AppM (Either ArticleError ManyArticles)
 -> ActionT AppM ManyArticles)
-> AppM (Either ArticleError ManyArticles)
-> ActionT AppM ManyArticles
forall a b. (a -> b) -> a -> b
$ Maybe UserID
-> FilterOps -> AppM (Either ArticleError ManyArticles)
forall (m :: * -> *).
AquireArticles m =>
Maybe UserID -> FilterOps -> m (Either ArticleError ManyArticles)
findArticles (Maybe AuthedUser
user Maybe AuthedUser -> (AuthedUser -> UserID) -> Maybe UserID
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> AuthedUser -> UserID
authedUserID) FilterOps
filterOps
  ManyArticles -> ActionT AppM ()
forall a (m :: * -> *). (ToJSON a, MonadIO m) => a -> ActionT m ()
json ManyArticles
articles

parseFilterOps :: ActionT AppM FilterOps
parseFilterOps :: ActionT AppM FilterOps
parseFilterOps = do
  [(Text, Text)]
params <- ActionT AppM [Param]
forall (m :: * -> *). Monad m => ActionT m [Param]
captureParams ActionT AppM [Param]
-> ([Param] -> [(Text, Text)]) -> ActionT AppM [(Text, Text)]
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> (Param -> (Text, Text)) -> [Param] -> [(Text, Text)]
forall a b. (a -> b) -> [a] -> [b]
map ((Text -> Text) -> Param -> (Text, Text)
forall (f :: * -> * -> *) a b.
Bifunctor f =>
(a -> b) -> f a a -> f b b
bimapBoth Text -> Text
forall l s. LazyStrict l s => l -> s
toStrict)

  FilterOps -> ActionT AppM FilterOps
forall a. a -> ActionT AppM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (FilterOps -> ActionT AppM FilterOps)
-> FilterOps -> ActionT AppM FilterOps
forall a b. (a -> b) -> a -> b
$ FilterOps
    { $sel:filterTag:FilterOps :: Maybe Text
filterTag    =  Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"tag"       [(Text, Text)]
params
    , $sel:filterAuthor:FilterOps :: Maybe [Text]
filterAuthor =  Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"author"    [(Text, Text)]
params Maybe Text -> (Text -> [Text]) -> Maybe [Text]
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> (Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
:[])
    , $sel:filterFavBy:FilterOps :: Maybe Text
filterFavBy  =  Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"favorited" [(Text, Text)]
params
    , $sel:filterLimit:FilterOps :: Int64
filterLimit  = (Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"limit"     [(Text, Text)]
params Maybe Text -> (Text -> Maybe Int64) -> Maybe Int64
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Text -> String
forall a. ToString a => a -> String
toString (Text -> String) -> (String -> Maybe Int64) -> Text -> Maybe Int64
forall a b c. (a -> b) -> (b -> c) -> a -> c
.- String -> Maybe Int64
forall a. Read a => String -> Maybe a
readMaybe) Maybe Int64 -> Int64 -> Int64
forall a. Maybe a -> a -> a
?: Int64
20
    , $sel:filterOffset:FilterOps :: Int64
filterOffset = (Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"offset"    [(Text, Text)]
params Maybe Text -> (Text -> Maybe Int64) -> Maybe Int64
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Text -> String
forall a. ToString a => a -> String
toString (Text -> String) -> (String -> Maybe Int64) -> Text -> Maybe Int64
forall a b c. (a -> b) -> (b -> c) -> a -> c
.- String -> Maybe Int64
forall a. Read a => String -> Maybe a
readMaybe) Maybe Int64 -> Int64 -> Int64
forall a. Maybe a -> a -> a
?: Int64
0
    }

class (Monad m) => AquireArticles m where
  findArticles :: Maybe UserID -> FilterOps -> m (Either ArticleError ManyArticles)

instance (Monad m, MonadDB m, MonadUnliftIO m) => AquireArticles m where
  findArticles :: Maybe UserID -> FilterOps -> m (Either ArticleError ManyArticles)
  findArticles :: Maybe UserID -> FilterOps -> m (Either ArticleError ManyArticles)
findArticles Maybe UserID
userID FilterOps {Int64
Maybe [Text]
Maybe Text
$sel:filterTag:FilterOps :: FilterOps -> Maybe Text
$sel:filterAuthor:FilterOps :: FilterOps -> Maybe [Text]
$sel:filterFavBy:FilterOps :: FilterOps -> Maybe Text
$sel:filterLimit:FilterOps :: FilterOps -> Int64
$sel:filterOffset:FilterOps :: FilterOps -> Int64
filterTag :: Maybe Text
filterAuthor :: Maybe [Text]
filterFavBy :: Maybe Text
filterLimit :: Int64
filterOffset :: Int64
..} = ([(Entity Article, Entity User, Value Bool, Value Bool, Value Int)]
 -> ManyArticles)
-> Either
     DBError
     [(Entity Article, Entity User, Value Bool, Value Bool, Value Int)]
-> Either ArticleError ManyArticles
forall e a b.
FeatureError e =>
(a -> b) -> Either DBError a -> Either e b
mapDBResult [(Entity Article, Entity User, Value Bool, Value Bool, Value Int)]
-> ManyArticles
mkManyArticles (Either
   DBError
   [(Entity Article, Entity User, Value Bool, Value Bool, Value Int)]
 -> Either ArticleError ManyArticles)
-> m (Either
        DBError
        [(Entity Article, Entity User, Value Bool, Value Bool, Value Int)])
-> m (Either ArticleError ManyArticles)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SqlPersistT
  m
  [(Entity Article, Entity User, Value Bool, Value Bool, Value Int)]
-> m (Either
        DBError
        [(Entity Article, Entity User, Value Bool, Value Bool, Value Int)])
forall a. SqlPersistT m a -> m (Either DBError a)
forall (m :: * -> *) a.
MonadDB m =>
SqlPersistT m a -> m (Either DBError a)
runDB do
    SqlQuery
  (SqlExpr (Entity Article), SqlExpr (Entity User),
   SqlExpr (Value Bool), SqlExpr (Value Bool), SqlExpr (Value Int))
-> SqlPersistT
     m
     [(Entity Article, Entity User, Value Bool, Value Bool, Value Int)]
forall a r (m :: * -> *) backend.
(SqlSelect a r, MonadIO m, SqlBackendCanRead backend) =>
SqlQuery a -> ReaderT backend m [r]
select (SqlQuery
   (SqlExpr (Entity Article), SqlExpr (Entity User),
    SqlExpr (Value Bool), SqlExpr (Value Bool), SqlExpr (Value Int))
 -> SqlPersistT
      m
      [(Entity Article, Entity User, Value Bool, Value Bool, Value Int)])
-> SqlQuery
     (SqlExpr (Entity Article), SqlExpr (Entity User),
      SqlExpr (Value Bool), SqlExpr (Value Bool), SqlExpr (Value Int))
-> SqlPersistT
     m
     [(Entity Article, Entity User, Value Bool, Value Bool, Value Int)]
forall a b. (a -> b) -> a -> b
$ do
      (SqlExpr (Entity Article)
a :& SqlExpr (Entity User)
u, SqlExpr (Value Bool)
follows) <- Maybe UserID
-> (SqlExpr (Entity Article)
    -> SqlExpr (Entity User) -> SqlExpr (Value Bool))
-> SqlQuery
     (SqlExpr (Entity Article) :& SqlExpr (Entity User),
      SqlExpr (Value Bool))
forall table.
PersistEntity table =>
Maybe UserID
-> (SqlExpr (Entity table)
    -> SqlExpr (Entity User) -> SqlExpr (Value Bool))
-> SqlQuery
     (SqlExpr (Entity table) :& SqlExpr (Entity User),
      SqlExpr (Value Bool))
queryAssociatedUser Maybe UserID
userID \SqlExpr (Entity Article)
a SqlExpr (Entity User)
u ->
        SqlExpr (Entity Article)
a.author SqlExpr (Value UserId)
-> SqlExpr (Value UserId) -> SqlExpr (Value Bool)
forall typ.
PersistField typ =>
SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool)
==. SqlExpr (Entity User)
u.id

      (SqlExpr (Value UserId), SqlExpr (Value (Key Article)))
-> SqlQuery ()
forall a. ToSomeValues a => a -> SqlQuery ()
groupBy (SqlExpr (Entity User)
u.id, SqlExpr (Entity Article)
a.id)

      Maybe [Text] -> ([Text] -> SqlQuery ()) -> SqlQuery ()
forall (f :: * -> *) a.
Applicative f =>
Maybe a -> (a -> f ()) -> f ()
whenJust Maybe [Text]
filterAuthor (([Text] -> SqlQuery ()) -> SqlQuery ())
-> ([Text] -> SqlQuery ()) -> SqlQuery ()
forall a b. (a -> b) -> a -> b
$ \[Text]
names -> SqlExpr (Value Bool) -> SqlQuery ()
where_ (SqlExpr (Value Bool) -> SqlQuery ())
-> SqlExpr (Value Bool) -> SqlQuery ()
forall a b. (a -> b) -> a -> b
$ SqlExpr (Entity User)
u.username SqlExpr (Value Text)
-> SqlExpr (ValueList Text) -> SqlExpr (Value Bool)
forall typ.
PersistField typ =>
SqlExpr (Value typ)
-> SqlExpr (ValueList typ) -> SqlExpr (Value Bool)
`in_` [Text] -> SqlExpr (ValueList Text)
forall typ. PersistField typ => [typ] -> SqlExpr (ValueList typ)
valList [Text]
names

      Maybe Text -> (Text -> SqlQuery ()) -> SqlQuery ()
forall (f :: * -> *) a.
Applicative f =>
Maybe a -> (a -> f ()) -> f ()
whenJust Maybe Text
filterFavBy ((Text -> SqlQuery ()) -> SqlQuery ())
-> (Text -> SqlQuery ()) -> SqlQuery ()
forall a b. (a -> b) -> a -> b
$ \Text
name -> SqlExpr (Value Bool) -> SqlQuery ()
where_ (SqlExpr (Value Bool) -> SqlQuery ())
-> SqlExpr (Value Bool) -> SqlQuery ()
forall a b. (a -> b) -> a -> b
$
        SqlQuery () -> SqlExpr (Value Bool)
exists (SqlQuery () -> SqlExpr (Value Bool))
-> SqlQuery () -> SqlExpr (Value Bool)
forall a b. (a -> b) -> a -> b
$ do
          (SqlExpr (Entity Favorite)
_ :& ToMaybeT (SqlExpr (Entity User))
u') <- From
  (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
-> SqlQuery
     (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
forall a a'. ToFrom a a' => a -> SqlQuery a'
from (From
   (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
 -> SqlQuery
      (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User))))
-> From
     (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
-> SqlQuery
     (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
forall a b. (a -> b) -> a -> b
$
            forall ent. PersistEntity ent => From (SqlExpr (Entity ent))
table @Favorite
              From (SqlExpr (Entity Favorite))
-> (From (SqlExpr (Entity User)),
    (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
    -> SqlExpr (Value Bool))
-> From
     (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
forall a a' b b' rhs.
(ToFrom a a', ToFrom b b', ToMaybe b',
 HasOnClause rhs (a' :& ToMaybeT b'),
 rhs ~ (b, (a' :& ToMaybeT b') -> SqlExpr (Value Bool))) =>
a -> rhs -> From (a' :& ToMaybeT b')
`leftJoin`
            forall ent. PersistEntity ent => From (SqlExpr (Entity ent))
table @User
              From (SqlExpr (Entity User))
-> ((SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
    -> SqlExpr (Value Bool))
-> (From (SqlExpr (Entity User)),
    (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
    -> SqlExpr (Value Bool))
forall a b.
ValidOnClause a =>
a -> (b -> SqlExpr (Value Bool)) -> (a, b -> SqlExpr (Value Bool))
`on` \(SqlExpr (Entity Favorite)
f :& ToMaybeT (SqlExpr (Entity User))
u') ->
                SqlExpr (Value UserId) -> SqlExpr (Value (Maybe UserId))
forall typ. SqlExpr (Value typ) -> SqlExpr (Value (Maybe typ))
just SqlExpr (Entity Favorite)
f.user SqlExpr (Value (Maybe UserId))
-> SqlExpr (Value (Maybe UserId)) -> SqlExpr (Value Bool)
forall typ.
PersistField typ =>
SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool)
==. ToMaybeT (SqlExpr (Entity User))
u'.id

          SqlExpr (Value Bool) -> SqlQuery ()
where_ (SqlExpr (Value Bool) -> SqlQuery ())
-> SqlExpr (Value Bool) -> SqlQuery ()
forall a b. (a -> b) -> a -> b
$ ToMaybeT (SqlExpr (Entity User))
u'.username SqlExpr (Value (Maybe Text))
-> SqlExpr (Value (Maybe Text)) -> SqlExpr (Value Bool)
forall typ.
PersistField typ =>
SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool)
==. SqlExpr (Value Text) -> SqlExpr (Value (Maybe Text))
forall typ. SqlExpr (Value typ) -> SqlExpr (Value (Maybe typ))
just (Text -> SqlExpr (Value Text)
forall typ. PersistField typ => typ -> SqlExpr (Value typ)
val Text
name)

      Maybe Text -> (Text -> SqlQuery ()) -> SqlQuery ()
forall (f :: * -> *) a.
Applicative f =>
Maybe a -> (a -> f ()) -> f ()
whenJust Maybe Text
filterFavBy ((Text -> SqlQuery ()) -> SqlQuery ())
-> (Text -> SqlQuery ()) -> SqlQuery ()
forall a b. (a -> b) -> a -> b
$ \Text
name -> SqlExpr (Value Bool) -> SqlQuery ()
where_ (SqlExpr (Value Bool) -> SqlQuery ())
-> SqlExpr (Value Bool) -> SqlQuery ()
forall a b. (a -> b) -> a -> b
$
        SqlExpr (Value Text) -> SqlExpr (Value (Maybe Text))
forall typ. SqlExpr (Value typ) -> SqlExpr (Value (Maybe typ))
just (Text -> SqlExpr (Value Text)
forall typ. PersistField typ => typ -> SqlExpr (Value typ)
val Text
name) SqlExpr (Value (Maybe Text))
-> SqlExpr (ValueList (Maybe Text)) -> SqlExpr (Value Bool)
forall typ.
PersistField typ =>
SqlExpr (Value typ)
-> SqlExpr (ValueList typ) -> SqlExpr (Value Bool)
`in_` SqlQuery (SqlExpr (Value (Maybe Text)))
-> SqlExpr (ValueList (Maybe Text))
forall a.
PersistField a =>
SqlQuery (SqlExpr (Value a)) -> SqlExpr (ValueList a)
subSelectList do
          (SqlExpr (Entity Favorite)
_ :& ToMaybeT (SqlExpr (Entity User))
u') <- From
  (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
-> SqlQuery
     (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
forall a a'. ToFrom a a' => a -> SqlQuery a'
from (From
   (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
 -> SqlQuery
      (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User))))
-> From
     (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
-> SqlQuery
     (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
forall a b. (a -> b) -> a -> b
$
            forall ent. PersistEntity ent => From (SqlExpr (Entity ent))
table @Favorite
              From (SqlExpr (Entity Favorite))
-> (From (SqlExpr (Entity User)),
    (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
    -> SqlExpr (Value Bool))
-> From
     (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
forall a a' b b' rhs.
(ToFrom a a', ToFrom b b', ToMaybe b',
 HasOnClause rhs (a' :& ToMaybeT b'),
 rhs ~ (b, (a' :& ToMaybeT b') -> SqlExpr (Value Bool))) =>
a -> rhs -> From (a' :& ToMaybeT b')
`leftJoin`
            forall ent. PersistEntity ent => From (SqlExpr (Entity ent))
table @User
              From (SqlExpr (Entity User))
-> ((SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
    -> SqlExpr (Value Bool))
-> (From (SqlExpr (Entity User)),
    (SqlExpr (Entity Favorite) :& ToMaybeT (SqlExpr (Entity User)))
    -> SqlExpr (Value Bool))
forall a b.
ValidOnClause a =>
a -> (b -> SqlExpr (Value Bool)) -> (a, b -> SqlExpr (Value Bool))
`on` \(SqlExpr (Entity Favorite)
f :& ToMaybeT (SqlExpr (Entity User))
u') ->
                SqlExpr (Value UserId) -> SqlExpr (Value (Maybe UserId))
forall typ. SqlExpr (Value typ) -> SqlExpr (Value (Maybe typ))
just SqlExpr (Entity Favorite)
f.user SqlExpr (Value (Maybe UserId))
-> SqlExpr (Value (Maybe UserId)) -> SqlExpr (Value Bool)
forall typ.
PersistField typ =>
SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool)
==. ToMaybeT (SqlExpr (Entity User))
u'.id

          SqlExpr (Value (Maybe Text))
-> SqlQuery (SqlExpr (Value (Maybe Text)))
forall a. a -> SqlQuery a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ToMaybeT (SqlExpr (Entity User))
u'.username

      -- temp until I figure out how to get esqueleto to work with goddamn arrays
      -- for some reason, while the [Text] array is serialized, is has an 's' prepended to each element,
      -- hence the 's' prepended here also for comparison purposes
      Maybe Text -> (Text -> SqlQuery ()) -> SqlQuery ()
forall (f :: * -> *) a.
Applicative f =>
Maybe a -> (a -> f ()) -> f ()
whenJust Maybe Text
filterTag ((Text -> SqlQuery ()) -> SqlQuery ())
-> (Text -> SqlQuery ()) -> SqlQuery ()
forall a b. (a -> b) -> a -> b
$ \Text
tag' -> SqlExpr (Value Bool) -> SqlQuery ()
where_ (SqlExpr (Value Bool) -> SqlQuery ())
-> SqlExpr (Value Bool) -> SqlQuery ()
forall a b. (a -> b) -> a -> b
$ Builder -> SqlExpr (Value Bool)
forall a. Builder -> SqlExpr (Value a)
unsafeSqlValue (Builder
"'s" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
TB.fromText Text
tag' Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"' = ANY(array(select json_array_elements_text(tags::json)))")

      Int64 -> SqlQuery ()
limit  Int64
filterLimit
      Int64 -> SqlQuery ()
offset Int64
filterOffset

      let (SqlExpr (Value Bool)
favorited, SqlExpr (Value Int)
numFavs) = Maybe UserID
-> SqlExpr (Entity Article)
-> (SqlExpr (Value Bool), SqlExpr (Value Int))
queryFavStats Maybe UserID
userID SqlExpr (Entity Article)
a

      [SqlExpr OrderBy] -> SqlQuery ()
orderBy [SqlExpr (Value UTCTime) -> SqlExpr OrderBy
forall a. PersistField a => SqlExpr (Value a) -> SqlExpr OrderBy
E.desc SqlExpr (Entity Article)
a.created]

      (SqlExpr (Entity Article), SqlExpr (Entity User),
 SqlExpr (Value Bool), SqlExpr (Value Bool), SqlExpr (Value Int))
-> SqlQuery
     (SqlExpr (Entity Article), SqlExpr (Entity User),
      SqlExpr (Value Bool), SqlExpr (Value Bool), SqlExpr (Value Int))
forall a. a -> SqlQuery a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (SqlExpr (Entity Article)
a, SqlExpr (Entity User)
u, SqlExpr (Value Bool)
follows, SqlExpr (Value Bool)
favorited, SqlExpr (Value Int)
numFavs)