packages feed

calamity-0.2.0.0: Calamity/Types/Model/Channel/Component.hs

-- | Message components
module Calamity.Types.Model.Channel.Component (
    Component (..),
    Button (..),
    button,
    button',
    ButtonStyle (..),
    ComponentType (..),
    componentType,
) where

import Calamity.Internal.AesonThings
import Calamity.Types.Model.Guild.Emoji
import Data.Aeson
import Data.Scientific (toBoundedInteger)
import qualified Data.Text as T
import GHC.Generics
import TextShow
import qualified TextShow.Generic as TSG

data Button = Button
    { style :: ButtonStyle
    , label :: Maybe T.Text
    , emoji :: Maybe RawEmoji
    , customID :: Maybe T.Text
    , url :: Maybe T.Text
    , disabled :: Bool
    }
    deriving (Show, Generic)
    deriving (TextShow) via TSG.FromGeneric Button
    deriving (ToJSON) via CalamityJSONKeepNothing Button
    deriving
        (FromJSON)
        via WithSpecialCases
                '["disabled" `IfNoneThen` DefaultToFalse]
                Button

data ButtonStyle
    = ButtonPrimary
    | ButtonSecondary
    | ButtonSuccess
    | ButtonDanger
    | ButtonLink
    deriving (Show, Generic)
    deriving (TextShow) via TSG.FromGeneric ButtonStyle

{- | Constuct a non-disabled 'Button' with the given 'ButtonStyle', all other
 fields are set to 'Nothing'
-}
button :: ButtonStyle -> Button
button s = Button s Nothing Nothing Nothing Nothing False

{- | Constuct a non-disabled 'Button' with the given 'ButtonStyle' and label,
 all other fields are set to 'Nothing'
-}
button' :: ButtonStyle -> T.Text -> Button
button' s l = Button s (Just l) Nothing Nothing Nothing False

instance ToJSON ButtonStyle where
    toJSON t = toJSON @Int $ case t of
        ButtonPrimary -> 1
        ButtonSecondary -> 2
        ButtonSuccess -> 3
        ButtonDanger -> 4
        ButtonLink -> 5

instance FromJSON ButtonStyle where
    parseJSON = withScientific "ChannelType" $ \n -> case toBoundedInteger @Int n of
        Just v -> case v of
            1 -> pure ButtonPrimary
            2 -> pure ButtonSecondary
            3 -> pure ButtonSuccess
            4 -> pure ButtonDanger
            5 -> pure ButtonLink
            _ -> fail $ "Invalid ButtonStyle: " <> show n
        Nothing -> fail $ "Invalid ButtonStyle: " <> show n

data Component
    = Button' Button
    | ActionRow' [Component]
    deriving (Show, Generic)
    deriving (TextShow) via TSG.FromGeneric Component

instance ToJSON Component where
    toJSON t =
        let (Object inner, type_) = case t of
                ActionRow' xs -> (Object ("components" .= xs), 1 :: Int)
                Button' b -> (toJSON b, 2 :: Int)
         in Object (inner <> ("type" .= type_))

instance FromJSON Component where
    parseJSON = withObject "Component" $ \v -> do
        type_ :: Int <- v .: "type"

        case type_ of
            1 -> do
                ActionRow' <$> v .: "components"
            2 -> Button' <$> parseJSON (Object v)
            _ -> fail $ "Invalid ComponentType: " <> show type_

componentType :: Component -> ComponentType
componentType (ActionRow' _) = ActionRowType
componentType (Button' _) = ButtonType

data ComponentType
    = ActionRowType
    | ButtonType
    deriving (Show, Generic)
    deriving (TextShow) via TSG.FromGeneric ComponentType

instance ToJSON ComponentType where
    toJSON x = toJSON @Int $ case x of
        ActionRowType -> 1
        ButtonType -> 2

instance FromJSON ComponentType where
    parseJSON = withScientific "ComponentType" $ \n -> case toBoundedInteger @Int n of
        Just 1 -> pure ActionRowType
        Just 2 -> pure ButtonType
        _ -> fail $ "Invalid ComponentType: " <> show n