{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeOperators #-}

-- | Publishing views using the Blocks API.
--
-- This is used for App Home and modals.
--
-- <https://api.slack.com/methods/views.publish>
--
-- @since 2.1.0.0
module Web.Slack.Experimental.Views (
  -- * Types
  SlackView (..),
  HomeTabView (..),
  ModalView (..),
  Expected (..),

  -- * Requests and responses
  PublishReq (..),
  PublishResp (..),
  Api,
  viewsPublish,
) where

import Data.Aeson qualified as A
import Data.Aeson.KeyMap qualified as KM
import Servant.API (AuthProtect, FormUrlEncoded, JSON, Post, ReqBody, (:>))
import Servant.Client (ClientM, client)
import Servant.Client.Core (AuthenticatedRequest)
import Web.FormUrlEncoded (ToForm (..))
import Web.HttpApiData (ToHttpApiData (..))
import Web.Slack.AesonUtils (Expected (..), snakeCaseOptions, snakeCaseOptionsEatTrailingUnderscore, (.=!), (.=?))
import Web.Slack.Experimental.Blocks qualified as B
import Web.Slack.Experimental.Blocks.Types (SlackPlainTextOnly)
import Web.Slack.Internal (ResponseJSON (..), SlackConfig (..), mkSlackAuthenticateReq, run)
import Web.Slack.Pager (Response)
import Web.Slack.Prelude
import Web.Slack.Types (UserId)

-- | View definition for some Slack surface. Has an inner type of either
-- 'ModalView' or 'HomeTabView'.
--
-- <https://api.slack.com/reference/surfaces/views>
--
-- @since 2.1.0.0
data SlackView inner = SlackView
  { forall inner. SlackView inner -> Vector SlackBlock
blocks :: Vector B.SlackBlock
  , forall inner. SlackView inner -> Maybe Text
privateMetadata :: Maybe Text
  , forall inner. SlackView inner -> Maybe Text
callbackId :: Maybe Text
  , forall inner. SlackView inner -> Maybe Text
externalId :: Maybe Text
  , forall inner. SlackView inner -> inner
inner :: inner
  }
  deriving stock (Int -> SlackView inner -> ShowS
[SlackView inner] -> ShowS
SlackView inner -> String
(Int -> SlackView inner -> ShowS)
-> (SlackView inner -> String)
-> ([SlackView inner] -> ShowS)
-> Show (SlackView inner)
forall inner. Show inner => Int -> SlackView inner -> ShowS
forall inner. Show inner => [SlackView inner] -> ShowS
forall inner. Show inner => SlackView inner -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall inner. Show inner => Int -> SlackView inner -> ShowS
showsPrec :: Int -> SlackView inner -> ShowS
$cshow :: forall inner. Show inner => SlackView inner -> String
show :: SlackView inner -> String
$cshowList :: forall inner. Show inner => [SlackView inner] -> ShowS
showList :: [SlackView inner] -> ShowS
Show, SlackView inner -> SlackView inner -> Bool
(SlackView inner -> SlackView inner -> Bool)
-> (SlackView inner -> SlackView inner -> Bool)
-> Eq (SlackView inner)
forall inner.
Eq inner =>
SlackView inner -> SlackView inner -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: forall inner.
Eq inner =>
SlackView inner -> SlackView inner -> Bool
== :: SlackView inner -> SlackView inner -> Bool
$c/= :: forall inner.
Eq inner =>
SlackView inner -> SlackView inner -> Bool
/= :: SlackView inner -> SlackView inner -> Bool
Eq, (forall x. SlackView inner -> Rep (SlackView inner) x)
-> (forall x. Rep (SlackView inner) x -> SlackView inner)
-> Generic (SlackView inner)
forall x. Rep (SlackView inner) x -> SlackView inner
forall x. SlackView inner -> Rep (SlackView inner) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall inner x. Rep (SlackView inner) x -> SlackView inner
forall inner x. SlackView inner -> Rep (SlackView inner) x
$cfrom :: forall inner x. SlackView inner -> Rep (SlackView inner) x
from :: forall x. SlackView inner -> Rep (SlackView inner) x
$cto :: forall inner x. Rep (SlackView inner) x -> SlackView inner
to :: forall x. Rep (SlackView inner) x -> SlackView inner
Generic)

type role SlackView representational

instance (FromJSON inner) => FromJSON (SlackView inner) where
  parseJSON :: Value -> Parser (SlackView inner)
parseJSON = String
-> (Object -> Parser (SlackView inner))
-> Value
-> Parser (SlackView inner)
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"SlackView" \Object
o -> do
    Vector SlackBlock
blocks <- Object
o Object -> Key -> Parser (Vector SlackBlock)
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"blocks"
    Maybe Text
privateMetadata <- Object
o Object -> Key -> Parser (Maybe Text)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"private_metadata"
    Maybe Text
callbackId <- Object
o Object -> Key -> Parser (Maybe Text)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"callback_id"
    Maybe Text
externalId <- Object
o Object -> Key -> Parser (Maybe Text)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"external_id"
    inner
inner <- forall a. FromJSON a => Value -> Parser a
parseJSON @inner (Object -> Value
A.Object Object
o)
    SlackView inner -> Parser (SlackView inner)
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure SlackView {inner
Maybe Text
Vector SlackBlock
$sel:blocks:SlackView :: Vector SlackBlock
$sel:privateMetadata:SlackView :: Maybe Text
$sel:callbackId:SlackView :: Maybe Text
$sel:externalId:SlackView :: Maybe Text
$sel:inner:SlackView :: inner
blocks :: Vector SlackBlock
privateMetadata :: Maybe Text
callbackId :: Maybe Text
externalId :: Maybe Text
inner :: inner
..}

instance (ToJSON inner) => ToJSON (SlackView inner) where
  toJSON :: SlackView inner -> Value
toJSON SlackView {inner
Maybe Text
Vector SlackBlock
$sel:blocks:SlackView :: forall inner. SlackView inner -> Vector SlackBlock
$sel:privateMetadata:SlackView :: forall inner. SlackView inner -> Maybe Text
$sel:callbackId:SlackView :: forall inner. SlackView inner -> Maybe Text
$sel:externalId:SlackView :: forall inner. SlackView inner -> Maybe Text
$sel:inner:SlackView :: forall inner. SlackView inner -> inner
blocks :: Vector SlackBlock
privateMetadata :: Maybe Text
callbackId :: Maybe Text
externalId :: Maybe Text
inner :: inner
..} = case inner -> Value
forall a. ToJSON a => a -> Value
toJSON inner
inner of
    A.Object Object
innerObj ->
      Object -> Value
A.Object
        ( [(Key, Value)] -> Object
forall v. [(Key, v)] -> KeyMap v
KM.fromList
            ( [Maybe (Key, Value)] -> [(Key, Value)]
forall (f :: * -> *) t.
(IsSequence (f (Maybe t)), Functor f,
 Element (f (Maybe t)) ~ Maybe t) =>
f (Maybe t) -> f t
catMaybes
                [ Key
"blocks" Key -> Vector SlackBlock -> Maybe (Key, Value)
forall v. ToJSON v => Key -> v -> Maybe (Key, Value)
.=! Vector SlackBlock
blocks
                , Key
"private_metadata" Key -> Maybe Text -> Maybe (Key, Value)
forall v. ToJSON v => Key -> Maybe v -> Maybe (Key, Value)
.=? Maybe Text
privateMetadata
                , Key
"callback_id" Key -> Maybe Text -> Maybe (Key, Value)
forall v. ToJSON v => Key -> Maybe v -> Maybe (Key, Value)
.=? Maybe Text
callbackId
                , Key
"external_id" Key -> Maybe Text -> Maybe (Key, Value)
forall v. ToJSON v => Key -> Maybe v -> Maybe (Key, Value)
.=? Maybe Text
externalId
                ]
            )
            Object -> Object -> Object
forall a. Semigroup a => a -> a -> a
<> Object
innerObj
        )
    Value
_ -> String -> Value
forall a. HasCallStack => String -> a
error String
"inner of SlackView is not an object"

-- | @since 2.1.0.0
data HomeTabView = HomeTabView
  { HomeTabView -> Expected "home"
type_ :: Expected "home"
  }
  deriving stock (Int -> HomeTabView -> ShowS
[HomeTabView] -> ShowS
HomeTabView -> String
(Int -> HomeTabView -> ShowS)
-> (HomeTabView -> String)
-> ([HomeTabView] -> ShowS)
-> Show HomeTabView
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> HomeTabView -> ShowS
showsPrec :: Int -> HomeTabView -> ShowS
$cshow :: HomeTabView -> String
show :: HomeTabView -> String
$cshowList :: [HomeTabView] -> ShowS
showList :: [HomeTabView] -> ShowS
Show, HomeTabView -> HomeTabView -> Bool
(HomeTabView -> HomeTabView -> Bool)
-> (HomeTabView -> HomeTabView -> Bool) -> Eq HomeTabView
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: HomeTabView -> HomeTabView -> Bool
== :: HomeTabView -> HomeTabView -> Bool
$c/= :: HomeTabView -> HomeTabView -> Bool
/= :: HomeTabView -> HomeTabView -> Bool
Eq, (forall x. HomeTabView -> Rep HomeTabView x)
-> (forall x. Rep HomeTabView x -> HomeTabView)
-> Generic HomeTabView
forall x. Rep HomeTabView x -> HomeTabView
forall x. HomeTabView -> Rep HomeTabView x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. HomeTabView -> Rep HomeTabView x
from :: forall x. HomeTabView -> Rep HomeTabView x
$cto :: forall x. Rep HomeTabView x -> HomeTabView
to :: forall x. Rep HomeTabView x -> HomeTabView
Generic)

$(deriveJSON snakeCaseOptionsEatTrailingUnderscore ''HomeTabView)

-- | Modal view
--
-- <https://api.slack.com/reference/surfaces/views#modal>
--
-- @since 2.1.0.0
data ModalView = ModalView
  { ModalView -> Expected "modal"
type_ :: Expected "modal"
  , ModalView -> SlackPlainTextOnly
title :: SlackPlainTextOnly
  -- ^ Title of the modal on the top left. Maximum length of 24 characters.
  , ModalView -> Maybe SlackPlainTextOnly
close :: Maybe SlackPlainTextOnly
  -- ^ Text appearing on the close button on the bottom-right of the modal.
  -- Maximum length of 24 characters.
  , ModalView -> Maybe SlackPlainTextOnly
submit :: Maybe SlackPlainTextOnly
  -- ^ Text appearing on the submit buttonn. Maximum length of 24 characters.
  -- Must be 'SlackPlainTextOnly'.
  , ModalView -> Maybe Bool
submitDisabled :: Maybe Bool
  -- ^ Whether one or more inputs must be filled before enabling the submit button.
  -- For configuration modals: <https://api.slack.com/reference/workflows/configuration-view>
  }
  deriving stock (Int -> ModalView -> ShowS
[ModalView] -> ShowS
ModalView -> String
(Int -> ModalView -> ShowS)
-> (ModalView -> String)
-> ([ModalView] -> ShowS)
-> Show ModalView
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ModalView -> ShowS
showsPrec :: Int -> ModalView -> ShowS
$cshow :: ModalView -> String
show :: ModalView -> String
$cshowList :: [ModalView] -> ShowS
showList :: [ModalView] -> ShowS
Show, ModalView -> ModalView -> Bool
(ModalView -> ModalView -> Bool)
-> (ModalView -> ModalView -> Bool) -> Eq ModalView
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ModalView -> ModalView -> Bool
== :: ModalView -> ModalView -> Bool
$c/= :: ModalView -> ModalView -> Bool
/= :: ModalView -> ModalView -> Bool
Eq, (forall x. ModalView -> Rep ModalView x)
-> (forall x. Rep ModalView x -> ModalView) -> Generic ModalView
forall x. Rep ModalView x -> ModalView
forall x. ModalView -> Rep ModalView x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. ModalView -> Rep ModalView x
from :: forall x. ModalView -> Rep ModalView x
$cto :: forall x. Rep ModalView x -> ModalView
to :: forall x. Rep ModalView x -> ModalView
Generic)

$(deriveJSON snakeCaseOptionsEatTrailingUnderscore ''ModalView)

-- | Publishes the App Home view for a user.
--
-- <https://api.slack.com/methods/views.publish>
--
-- @since 2.1.0.0
data PublishReq = PublishReq
  { PublishReq -> UserId
userId :: UserId
  -- ^ User to whom the view is being published.
  , PublishReq -> SlackView HomeTabView
view :: SlackView HomeTabView
  -- ^ View payload.
  }
  deriving stock (Int -> PublishReq -> ShowS
[PublishReq] -> ShowS
PublishReq -> String
(Int -> PublishReq -> ShowS)
-> (PublishReq -> String)
-> ([PublishReq] -> ShowS)
-> Show PublishReq
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PublishReq -> ShowS
showsPrec :: Int -> PublishReq -> ShowS
$cshow :: PublishReq -> String
show :: PublishReq -> String
$cshowList :: [PublishReq] -> ShowS
showList :: [PublishReq] -> ShowS
Show)

instance ToForm PublishReq where
  toForm :: PublishReq -> Form
toForm PublishReq {UserId
SlackView HomeTabView
$sel:userId:PublishReq :: PublishReq -> UserId
$sel:view:PublishReq :: PublishReq -> SlackView HomeTabView
userId :: UserId
view :: SlackView HomeTabView
..} =
    [ (Text
"user_id" :: Text, UserId -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam UserId
userId)
    , (Text
"view", Text -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam (Text -> Text) -> (ByteString -> Text) -> ByteString -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. ByteString -> Text
forall textual binary. Utf8 textual binary => binary -> textual
decodeUtf8 (ByteString -> Text) -> ByteString -> Text
forall a b. (a -> b) -> a -> b
$ SlackView HomeTabView -> ByteString
forall a. ToJSON a => a -> ByteString
A.encode SlackView HomeTabView
view)
    ]

-- | @since 2.1.0.0
data PublishResp = PublishResp
  { PublishResp -> SlackView HomeTabView
view :: SlackView HomeTabView
  }
  deriving stock (Int -> PublishResp -> ShowS
[PublishResp] -> ShowS
PublishResp -> String
(Int -> PublishResp -> ShowS)
-> (PublishResp -> String)
-> ([PublishResp] -> ShowS)
-> Show PublishResp
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PublishResp -> ShowS
showsPrec :: Int -> PublishResp -> ShowS
$cshow :: PublishResp -> String
show :: PublishResp -> String
$cshowList :: [PublishResp] -> ShowS
showList :: [PublishResp] -> ShowS
Show)

$(deriveJSON snakeCaseOptions ''PublishResp)

-- | @since 2.1.0.0
type Api =
  "views.publish"
    :> AuthProtect "token"
    :> ReqBody '[FormUrlEncoded] PublishReq
    :> Post '[JSON] (ResponseJSON PublishResp)

-- | Publishes the App Home view for a user.
--
-- <https://api.slack.com/methods/views.publish>
--
-- @since 2.1.0.0
viewsPublish :: SlackConfig -> PublishReq -> IO (Response PublishResp)
viewsPublish :: SlackConfig -> PublishReq -> IO (Response PublishResp)
viewsPublish SlackConfig
slackConfig PublishReq
req = do
  let authR :: AuthenticatedRequest (AuthProtect "token")
authR = SlackConfig -> AuthenticatedRequest (AuthProtect "token")
mkSlackAuthenticateReq SlackConfig
slackConfig
  ClientM (ResponseJSON PublishResp)
-> Manager -> IO (Response PublishResp)
forall a. ClientM (ResponseJSON a) -> Manager -> IO (Response a)
run (AuthenticatedRequest (AuthProtect "token")
-> PublishReq -> ClientM (ResponseJSON PublishResp)
viewsPublish_ AuthenticatedRequest (AuthProtect "token")
authR PublishReq
req) (Manager -> IO (Response PublishResp))
-> (SlackConfig -> Manager)
-> SlackConfig
-> IO (Response PublishResp)
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. SlackConfig -> Manager
slackConfigManager (SlackConfig -> IO (Response PublishResp))
-> SlackConfig -> IO (Response PublishResp)
forall a b. (a -> b) -> a -> b
$ SlackConfig
slackConfig

viewsPublish_ :: AuthenticatedRequest (AuthProtect "token") -> PublishReq -> ClientM (ResponseJSON PublishResp)
viewsPublish_ :: AuthenticatedRequest (AuthProtect "token")
-> PublishReq -> ClientM (ResponseJSON PublishResp)
viewsPublish_ = Proxy Api -> Client ClientM Api
forall api.
HasClient ClientM api =>
Proxy api -> Client ClientM api
client (forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @Api)