{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE RankNTypes #-}

-- | This module provides the Drawing functionality for the
-- ChannelList sidebar.  The sidebar is divided vertically into groups
-- and each group is rendered separately.
--
-- There are actually two UI modes handled by this code:
--
--   * Normal display of the channels, with various markers to
--     indicate the current channel, channels with unread messages,
--     user state (for Direct Message channels), etc.
--
--   * ChannelSelect display where the user is typing match characters
--     into a prompt at the ChannelList sidebar is showing only those
--     channels matching the entered text (and highlighting the
--     matching portion).

module Matterhorn.Draw.ChannelList (renderChannelList, renderChannelListHeader) where

import           Prelude ()
import           Matterhorn.Prelude

import           Brick
import           Brick.Widgets.Border
import           Brick.Widgets.Center (hCenter)
import           Brick.Widgets.Edit ( editContentsL )
import qualified Data.Text as T
import qualified Data.Text.Zipper as TZ
import           Data.Maybe ( fromJust )
import           Lens.Micro.Platform (non)

import qualified Network.Mattermost.Types as MM

import           Matterhorn.Draw.Util
import           Matterhorn.State.Channels
import           Matterhorn.Themes
import           Matterhorn.Types
import           Matterhorn.Types.Common ( sanitizeUserText )
import qualified Matterhorn.Zipper as Z

-- | Internal record describing each channel entry and its associated
-- attributes.  This is the object passed to the rendering function so
-- that it can determine how to render each channel.
data ChannelListEntryData =
    ChannelListEntryData { ChannelListEntryData -> Text
entrySigil       :: Text
                         , ChannelListEntryData -> Text
entryLabel       :: Text
                         , ChannelListEntryData -> Bool
entryHasUnread   :: Bool
                         , ChannelListEntryData -> Int
entryMentions    :: Int
                         , ChannelListEntryData -> Bool
entryIsRecent    :: Bool
                         , ChannelListEntryData -> Bool
entryIsReturn    :: Bool
                         , ChannelListEntryData -> Bool
entryIsCurrent   :: Bool
                         , ChannelListEntryData -> Bool
entryIsMuted     :: Bool
                         , ChannelListEntryData -> Maybe UserStatus
entryUserStatus  :: Maybe UserStatus
                         }

sbRenderer :: ScrollbarRenderer n
sbRenderer :: forall n. ScrollbarRenderer n
sbRenderer =
    forall n. ScrollbarRenderer n
verticalScrollbarRenderer { renderScrollbarHandleBefore :: Widget n
renderScrollbarHandleBefore = forall n. String -> Widget n
str String
"▲"
                              , renderScrollbarHandleAfter :: Widget n
renderScrollbarHandleAfter = forall n. String -> Widget n
str String
"▼"
                              }

renderChannelListHeader :: ChatState -> MM.TeamId -> Widget Name
renderChannelListHeader :: ChatState -> TeamId -> Widget Name
renderChannelListHeader ChatState
st TeamId
tId =
    forall n. [Widget n] -> Widget n
vBox [ forall {n}. Widget n
teamHeader
         , forall {n}. Widget n
selfHeader
         , forall {n}. Widget n
unreadCountHeader
         ]
    where
        myUsername_ :: Text
myUsername_ = ChatState -> Text
myUsername ChatState
st
        teamHeader :: Widget n
teamHeader = forall n. Widget n -> Widget n
hCenter forall a b. (a -> b) -> a -> b
$
                     forall n. AttrName -> Widget n -> Widget n
withDefAttr AttrName
clientEmphAttr forall a b. (a -> b) -> a -> b
$
                     forall n. Text -> Widget n
txt forall a b. (a -> b) -> a -> b
$ Text
"Team: " forall a. Semigroup a => a -> a -> a
<> Text
teamNameStr
        selfHeader :: Widget n
selfHeader = forall n. Widget n -> Widget n
hCenter forall a b. (a -> b) -> a -> b
$
                     forall a. Text -> Text -> Text -> Widget a
colorUsername Text
myUsername_ Text
myUsername_
                         (Char -> Text
T.singleton Char
statusSigil forall a. Semigroup a => a -> a -> a
<> Text
" " forall a. Semigroup a => a -> a -> a
<> Text -> Text
addUserSigil Text
myUsername_)
        teamNameStr :: Text
teamNameStr = Text -> Text
T.strip forall a b. (a -> b) -> a -> b
$ UserText -> Text
sanitizeUserText forall a b. (a -> b) -> a -> b
$ Team -> UserText
MM.teamDisplayName forall a b. (a -> b) -> a -> b
$ ChatState
stforall s a. s -> Getting a s a -> a
^.TeamId -> Lens' ChatState TeamState
csTeam(TeamId
tId)forall b c a. (b -> c) -> (a -> b) -> a -> c
.Lens' TeamState Team
tsTeam
        statusSigil :: Char
statusSigil = forall b a. b -> (a -> b) -> Maybe a -> b
maybe Char
' ' UserInfo -> Char
userSigilFromInfo Maybe UserInfo
me
        me :: Maybe UserInfo
me = UserId -> ChatState -> Maybe UserInfo
userById (ChatState -> UserId
myUserId ChatState
st) ChatState
st
        unreadCountHeader :: Widget n
unreadCountHeader = forall n. Widget n -> Widget n
hCenter forall a b. (a -> b) -> a -> b
$ forall n. Text -> Widget n
txt forall a b. (a -> b) -> a -> b
$ Text
"Unread: " forall a. Semigroup a => a -> a -> a
<> (String -> Text
T.pack forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> String
show Int
unreadCount)
        unreadCount :: Int
unreadCount = forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum forall a b. (a -> b) -> a -> b
$ (ChannelListGroup -> Int
channelListGroupUnread forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> a
fst) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a b. Zipper a b -> [(a, [b])]
Z.toList (ChatState
stforall s a. s -> Getting a s a -> a
^.TeamId -> Lens' ChatState TeamState
csTeam(TeamId
tId)forall b c a. (b -> c) -> (a -> b) -> a -> c
.Lens' TeamState (Zipper ChannelListGroup ChannelListEntry)
tsFocus)

renderChannelList :: ChatState -> MM.TeamId -> Widget Name
renderChannelList :: ChatState -> TeamId -> Widget Name
renderChannelList ChatState
st TeamId
tId =
    Widget Name
header forall n. Widget n -> Widget n -> Widget n
<=> Widget Name
vpBody
    where
        (VScrollBarOrientation
sbOrientation, Widget n -> Widget n
sbPad) = case ChatState
stforall s a. s -> Getting a s a -> a
^.Lens' ChatState ChatResources
csResourcesforall b c a. (b -> c) -> (a -> b) -> a -> c
.Lens' ChatResources Config
crConfigurationforall b c a. (b -> c) -> (a -> b) -> a -> c
.Lens' Config ChannelListOrientation
configChannelListOrientationL of
            ChannelListOrientation
ChannelListLeft -> (VScrollBarOrientation
OnLeft, forall n. Padding -> Widget n -> Widget n
padLeft (Int -> Padding
Pad Int
1))
            ChannelListOrientation
ChannelListRight -> (VScrollBarOrientation
OnRight, forall n. Padding -> Widget n -> Widget n
padRight (Int -> Padding
Pad Int
1))
        myUsername_ :: Text
myUsername_ = ChatState -> Text
myUsername ChatState
st
        channelName :: ChannelListEntry -> Name
channelName ChannelListEntry
e = ChannelId -> Name
ClickableChannelListEntry forall a b. (a -> b) -> a -> b
$ ChannelListEntry -> ChannelId
channelListEntryChannelId ChannelListEntry
e
        renderEntry :: ChatState -> ChannelListEntry -> Widget Name
renderEntry ChatState
s ChannelListEntry
e = forall n. Ord n => n -> Widget n -> Widget n
clickable (ChannelListEntry -> Name
channelName ChannelListEntry
e) forall a b. (a -> b) -> a -> b
$
                          TeamId -> Text -> ChannelListEntryData -> Widget Name
renderChannelListEntry TeamId
tId Text
myUsername_ forall a b. (a -> b) -> a -> b
$ ChatState -> TeamId -> ChannelListEntry -> ChannelListEntryData
mkChannelEntryData ChatState
s TeamId
tId ChannelListEntry
e
        header :: Widget Name
header = ChatState -> TeamId -> Widget Name
renderChannelListHeader ChatState
st TeamId
tId
        vpBody :: Widget Name
vpBody = forall n. ScrollbarRenderer n -> Widget n -> Widget n
withVScrollBarRenderer forall n. ScrollbarRenderer n
sbRenderer forall a b. (a -> b) -> a -> b
$
                 forall n. VScrollBarOrientation -> Widget n -> Widget n
withVScrollBars VScrollBarOrientation
sbOrientation forall a b. (a -> b) -> a -> b
$
                 forall n. Widget n -> Widget n
withVScrollBarHandles forall a b. (a -> b) -> a -> b
$
                 forall n.
(ClickableScrollbarElement -> n -> n) -> Widget n -> Widget n
withClickableVScrollBars ClickableScrollbarElement -> Name -> Name
VScrollBar forall a b. (a -> b) -> a -> b
$
                 forall n.
(Ord n, Show n) =>
n -> ViewportType -> Widget n -> Widget n
viewport (TeamId -> Name
ChannelListViewport TeamId
tId) ViewportType
Vertical forall a b. (a -> b) -> a -> b
$ forall n. Widget n -> Widget n
sbPad Widget Name
body
        body :: Widget Name
body = case TeamState -> Mode
teamMode forall a b. (a -> b) -> a -> b
$ ChatState
stforall s a. s -> Getting a s a -> a
^.TeamId -> Lens' ChatState TeamState
csTeam(TeamId
tId) of
            Mode
ChannelSelect ->
                let zipper :: Zipper ChannelListGroup ChannelSelectMatch
zipper = ChatState
stforall s a. s -> Getting a s a -> a
^.TeamId -> Lens' ChatState TeamState
csTeam(TeamId
tId)forall b c a. (b -> c) -> (a -> b) -> a -> c
.Lens' TeamState ChannelSelectState
tsChannelSelectStateforall b c a. (b -> c) -> (a -> b) -> a -> c
.Lens'
  ChannelSelectState (Zipper ChannelListGroup ChannelSelectMatch)
channelSelectMatches
                    matches :: [Widget Name]
matches = if forall a b. Zipper a b -> Bool
Z.isEmpty Zipper ChannelListGroup ChannelSelectMatch
zipper
                              then [forall n. Widget n -> Widget n
hCenter forall a b. (a -> b) -> a -> b
$ forall n. Text -> Widget n
txt Text
"No matches"]
                              else (forall e.
Bool
-> ChatState
-> (ChatState -> e -> Widget Name)
-> (ChannelListGroup, [e])
-> Widget Name
renderChannelListGroup Bool
False ChatState
st
                                       (TeamId
-> Maybe ChannelSelectMatch
-> ChatState
-> ChannelSelectMatch
-> Widget Name
renderChannelSelectListEntry TeamId
tId (forall a b. Zipper a b -> Maybe b
Z.focus Zipper ChannelListGroup ChannelSelectMatch
zipper)) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>
                                   forall a b. Zipper a b -> [(a, [b])]
Z.toList Zipper ChannelListGroup ChannelSelectMatch
zipper)
                in forall n. [Widget n] -> Widget n
vBox [Widget Name]
matches
            Mode
_ ->
                forall n. Ord n => n -> Widget n -> Widget n
cached (TeamId -> Name
ChannelSidebar TeamId
tId) forall a b. (a -> b) -> a -> b
$
                forall n. [Widget n] -> Widget n
vBox forall a b. (a -> b) -> a -> b
$
                (forall e.
Bool
-> ChatState
-> (ChatState -> e -> Widget Name)
-> (ChannelListGroup, [e])
-> Widget Name
renderChannelListGroup Bool
True ChatState
st ChatState -> ChannelListEntry -> Widget Name
renderEntry forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a b. Zipper a b -> [(a, [b])]
Z.toList (ChatState
stforall s a. s -> Getting a s a -> a
^.TeamId -> Lens' ChatState TeamState
csTeam(TeamId
tId)forall b c a. (b -> c) -> (a -> b) -> a -> c
.Lens' TeamState (Zipper ChannelListGroup ChannelListEntry)
tsFocus))

renderChannelListGroupHeading :: ChannelListGroup -> Widget Name
renderChannelListGroupHeading :: ChannelListGroup -> Widget Name
renderChannelListGroupHeading ChannelListGroup
g =
    let label :: ChannelListGroupLabel
label = ChannelListGroup -> ChannelListGroupLabel
channelListGroupLabel ChannelListGroup
g
        labelStr :: Text
labelStr = case ChannelListGroupLabel
label of
            ChannelListGroupLabel
ChannelGroupPublicChannels   -> Text
"Public Channels"
            ChannelListGroupLabel
ChannelGroupPrivateChannels  -> Text
"Private Channels"
            ChannelListGroupLabel
ChannelGroupFavoriteChannels -> Text
"Favorite Channels"
            ChannelListGroupLabel
ChannelGroupDirectMessages   -> Text
"Direct Messages"
        unread :: Int
unread = ChannelListGroup -> Int
channelListGroupUnread ChannelListGroup
g
        collapsed :: Bool
collapsed = ChannelListGroup -> Bool
channelListGroupCollapsed ChannelListGroup
g
        addUnread :: Widget n -> Widget n
addUnread = if Int
unread forall a. Ord a => a -> a -> Bool
> Int
0
                    then (forall n. Widget n -> Widget n -> Widget n
<+> (forall n. AttrName -> Widget n -> Widget n
withDefAttr AttrName
unreadGroupMarkerAttr forall a b. (a -> b) -> a -> b
$ forall n. Text -> Widget n
txt Text
"*"))
                    else forall a. a -> a
id
        addExpand :: Widget n -> Widget n
addExpand = if Bool
collapsed
                    then (forall n. Widget n -> Widget n -> Widget n
<+> (forall n. AttrName -> Widget n -> Widget n
withDefAttr AttrName
unreadGroupMarkerAttr forall a b. (a -> b) -> a -> b
$ forall n. Text -> Widget n
txt Text
"[+]"))
                    else forall a. a -> a
id
        labelWidget :: Widget n
labelWidget = forall n. Widget n -> Widget n
addExpand forall a b. (a -> b) -> a -> b
$ forall n. Widget n -> Widget n
addUnread forall a b. (a -> b) -> a -> b
$ forall n. AttrName -> Widget n -> Widget n
withDefAttr AttrName
channelListHeaderAttr forall a b. (a -> b) -> a -> b
$ forall n. Text -> Widget n
txt Text
labelStr
    in forall n. Widget n -> Widget n
hBorderWithLabel forall a b. (a -> b) -> a -> b
$ forall n. Ord n => n -> Widget n -> Widget n
clickable (ChannelListGroupLabel -> Name
ClickableChannelListGroupHeading ChannelListGroupLabel
label) forall {n}. Widget n
labelWidget

renderChannelListGroup :: Bool
                       -- ^ Whether to draw empty groups (just show
                       -- their headings)
                       -> ChatState
                       -> (ChatState -> e -> Widget Name)
                       -> (ChannelListGroup, [e])
                       -> Widget Name
renderChannelListGroup :: forall e.
Bool
-> ChatState
-> (ChatState -> e -> Widget Name)
-> (ChannelListGroup, [e])
-> Widget Name
renderChannelListGroup Bool
False ChatState
_ ChatState -> e -> Widget Name
_ (ChannelListGroup
_, []) = forall {n}. Widget n
emptyWidget
renderChannelListGroup Bool
_ ChatState
st ChatState -> e -> Widget Name
renderEntry (ChannelListGroup
group, [e]
es) =
    let heading :: Widget Name
heading = ChannelListGroup -> Widget Name
renderChannelListGroupHeading ChannelListGroup
group
        entryWidgets :: [Widget Name]
entryWidgets = ChatState -> e -> Widget Name
renderEntry ChatState
st forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [e]
es
    in if ChannelListGroup -> Int
channelListGroupEntries ChannelListGroup
group forall a. Ord a => a -> a -> Bool
> Int
0 Bool -> Bool -> Bool
|| (ChannelListGroup -> Bool
channelListGroupCollapsed ChannelListGroup
group)
       then forall n. [Widget n] -> Widget n
vBox (Widget Name
heading forall a. a -> [a] -> [a]
: [Widget Name]
entryWidgets)
       else forall {n}. Widget n
emptyWidget

mkChannelEntryData :: ChatState
                   -> MM.TeamId
                   -> ChannelListEntry
                   -> ChannelListEntryData
mkChannelEntryData :: ChatState -> TeamId -> ChannelListEntry -> ChannelListEntryData
mkChannelEntryData ChatState
st TeamId
tId ChannelListEntry
e =
    ChannelListEntryData { entrySigil :: Text
entrySigil       = Text
sigilWithSpace
                         , entryLabel :: Text
entryLabel       = Text
name
                         , entryHasUnread :: Bool
entryHasUnread   = Bool
unread
                         , entryMentions :: Int
entryMentions    = Int
mentions
                         , entryIsRecent :: Bool
entryIsRecent    = Bool
recent
                         , entryIsReturn :: Bool
entryIsReturn    = Bool
ret
                         , entryIsCurrent :: Bool
entryIsCurrent   = Bool
current
                         , entryIsMuted :: Bool
entryIsMuted     = Bool
muted
                         , entryUserStatus :: Maybe UserStatus
entryUserStatus  = Maybe UserStatus
status
                         }
    where
        cId :: ChannelId
cId = ChannelListEntry -> ChannelId
channelListEntryChannelId ChannelListEntry
e
        unread :: Bool
unread = ChannelListEntry -> Bool
channelListEntryUnread ChannelListEntry
e
        chan :: ClientChannel
chan = forall a. HasCallStack => Maybe a -> a
fromJust forall a b. (a -> b) -> a -> b
$ ChannelId -> ClientChannels -> Maybe ClientChannel
findChannelById ChannelId
cId (ChatState
stforall s a. s -> Getting a s a -> a
^.Lens' ChatState ClientChannels
csChannels)
        recent :: Bool
recent = ChatState -> TeamId -> ChannelId -> Bool
isRecentChannel ChatState
st TeamId
tId ChannelId
cId
        ret :: Bool
ret = ChatState -> TeamId -> ChannelId -> Bool
isReturnChannel ChatState
st TeamId
tId ChannelId
cId
        current :: Bool
current = ChatState -> TeamId -> ChannelId -> Bool
isCurrentChannel ChatState
st TeamId
tId ChannelId
cId
        muted :: Bool
muted = ChannelListEntry -> Bool
channelListEntryMuted ChannelListEntry
e
        (Text
name, Maybe Text
normalSigil, Bool
addSpace, Maybe UserStatus
status) = case ChannelListEntry -> ChannelListEntryType
channelListEntryType ChannelListEntry
e of
            ChannelListEntryType
CLChannel ->
                (ClientChannel
chanforall s a. s -> Getting a s a -> a
^.Lens' ClientChannel ChannelInfo
ccInfoforall b c a. (b -> c) -> (a -> b) -> a -> c
.Lens' ChannelInfo Text
cdDisplayName, forall a. Maybe a
Nothing, Bool
False, forall a. Maybe a
Nothing)
            ChannelListEntryType
CLGroupDM ->
                (ClientChannel
chanforall s a. s -> Getting a s a -> a
^.Lens' ClientChannel ChannelInfo
ccInfoforall b c a. (b -> c) -> (a -> b) -> a -> c
.Lens' ChannelInfo Text
cdDisplayName, forall a. a -> Maybe a
Just Text
" ", Bool
True, forall a. Maybe a
Nothing)
            CLUserDM UserId
uId ->
                let u :: UserInfo
u = forall a. HasCallStack => Maybe a -> a
fromJust forall a b. (a -> b) -> a -> b
$ UserId -> ChatState -> Maybe UserInfo
userById UserId
uId ChatState
st
                    uname :: Text
uname = if ChatState -> Bool
useNickname ChatState
st
                            then UserInfo
uforall s a. s -> Getting a s a -> a
^.Lens' UserInfo (Maybe Text)
uiNickNameforall b c a. (b -> c) -> (a -> b) -> a -> c
.forall a. Eq a => a -> Lens' (Maybe a) a
non (UserInfo
uforall s a. s -> Getting a s a -> a
^.Lens' UserInfo Text
uiName)
                            else UserInfo
uforall s a. s -> Getting a s a -> a
^.Lens' UserInfo Text
uiName
                in (Text
uname, forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ Char -> Text
T.singleton forall a b. (a -> b) -> a -> b
$ UserInfo -> Char
userSigilFromInfo UserInfo
u,
                    Bool
True, forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ UserInfo
uforall s a. s -> Getting a s a -> a
^.Lens' UserInfo UserStatus
uiStatus)
        sigilWithSpace :: Text
sigilWithSpace = Text
sigil forall a. Semigroup a => a -> a -> a
<> if Bool
addSpace then Text
" " else Text
""
        prevEditSigil :: Text
prevEditSigil = Text
"»"
        sigil :: Text
sigil = if Bool
current
                then forall a. a -> Maybe a -> a
fromMaybe Text
"" Maybe Text
normalSigil
                else case ClientChannel
chanforall s a. s -> Getting a s a -> a
^.Lens' ClientChannel (MessageInterface Name ())
ccMessageInterfaceforall b c a. (b -> c) -> (a -> b) -> a -> c
.forall n i. Lens' (MessageInterface n i) (EditState n)
miEditorforall b c a. (b -> c) -> (a -> b) -> a -> c
.forall n. Lens' (EditState n) EphemeralEditState
esEphemeralforall b c a. (b -> c) -> (a -> b) -> a -> c
.Lens' EphemeralEditState (Maybe Int)
eesInputHistoryPosition of
                    Just Int
_ -> Text
prevEditSigil
                    Maybe Int
Nothing ->
                        let emptyEditor :: Bool
emptyEditor = Text -> Bool
T.null forall a b. (a -> b) -> a -> b
$ [Text] -> Text
T.concat forall a b. (a -> b) -> a -> b
$ forall a. Monoid a => TextZipper a -> [a]
TZ.getText forall a b. (a -> b) -> a -> b
$
                                          ClientChannel
chanforall s a. s -> Getting a s a -> a
^.Lens' ClientChannel (MessageInterface Name ())
ccMessageInterfaceforall b c a. (b -> c) -> (a -> b) -> a -> c
.forall n i. Lens' (MessageInterface n i) (EditState n)
miEditorforall b c a. (b -> c) -> (a -> b) -> a -> c
.forall n. Lens' (EditState n) (Editor Text n)
esEditorforall b c a. (b -> c) -> (a -> b) -> a -> c
.forall t1 n t2.
Lens (Editor t1 n) (Editor t2 n) (TextZipper t1) (TextZipper t2)
editContentsL
                        in if Bool
emptyEditor
                           then forall a. a -> Maybe a -> a
fromMaybe Text
"" Maybe Text
normalSigil
                           else Text
prevEditSigil
        mentions :: Int
mentions = ClientChannel
chanforall s a. s -> Getting a s a -> a
^.Lens' ClientChannel ChannelInfo
ccInfoforall b c a. (b -> c) -> (a -> b) -> a -> c
.Lens' ChannelInfo Int
cdMentionCount

-- | Render an individual Channel List entry (in Normal mode) with
-- appropriate visual decorations.
renderChannelListEntry :: MM.TeamId -> Text -> ChannelListEntryData -> Widget Name
renderChannelListEntry :: TeamId -> Text -> ChannelListEntryData -> Widget Name
renderChannelListEntry TeamId
tId Text
myUName ChannelListEntryData
entry = Widget Name
body
    where
    body :: Widget Name
body = Widget Name -> Widget Name
decorate forall a b. (a -> b) -> a -> b
$ forall n. ChannelListEntryData -> Widget n -> Widget n
decorateEntry ChannelListEntryData
entry forall a b. (a -> b) -> a -> b
$ forall n. ChannelListEntryData -> Widget n -> Widget n
decorateMentions ChannelListEntryData
entry forall a b. (a -> b) -> a -> b
$ forall n. Padding -> Widget n -> Widget n
padRight Padding
Max forall a b. (a -> b) -> a -> b
$
           forall n. Text -> Widget n
entryWidget forall a b. (a -> b) -> a -> b
$ ChannelListEntryData -> Text
entrySigil ChannelListEntryData
entry forall a. Semigroup a => a -> a -> a
<> ChannelListEntryData -> Text
entryLabel ChannelListEntryData
entry
    decorate :: Widget Name -> Widget Name
decorate = if | ChannelListEntryData -> Bool
entryIsCurrent ChannelListEntryData
entry ->
                      forall n. Ord n => n -> Widget n -> Widget n
reportExtent (TeamId -> Name
SelectedChannelListEntry TeamId
tId) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall n. AttrName -> Widget n -> Widget n
forceAttr AttrName
currentChannelNameAttr
                  | ChannelListEntryData -> Int
entryMentions ChannelListEntryData
entry forall a. Ord a => a -> a -> Bool
> Int
0 Bool -> Bool -> Bool
&& Bool -> Bool
not (ChannelListEntryData -> Bool
entryIsMuted ChannelListEntryData
entry) ->
                      forall n. AttrName -> Widget n -> Widget n
forceAttr AttrName
mentionsChannelAttr
                  | ChannelListEntryData -> Bool
entryHasUnread ChannelListEntryData
entry ->
                      forall n. AttrName -> Widget n -> Widget n
forceAttr AttrName
unreadChannelAttr
                  | Bool
otherwise -> forall a. a -> a
id
    entryWidget :: Text -> Widget n
entryWidget = case ChannelListEntryData -> Maybe UserStatus
entryUserStatus ChannelListEntryData
entry of
                    Just UserStatus
Offline -> forall n. AttrName -> Widget n -> Widget n
withDefAttr AttrName
clientMessageAttr forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall n. Text -> Widget n
txt
                    Just UserStatus
_       -> forall a. Text -> Text -> Text -> Widget a
colorUsername Text
myUName (ChannelListEntryData -> Text
entryLabel ChannelListEntryData
entry)
                    Maybe UserStatus
Nothing      -> forall n. Text -> Widget n
txt

-- | Render an individual entry when in Channel Select mode,
-- highlighting the matching portion, or completely suppressing the
-- entry if it doesn't match.
renderChannelSelectListEntry :: MM.TeamId
                             -> Maybe ChannelSelectMatch
                             -> ChatState
                             -> ChannelSelectMatch
                             -> Widget Name
renderChannelSelectListEntry :: TeamId
-> Maybe ChannelSelectMatch
-> ChatState
-> ChannelSelectMatch
-> Widget Name
renderChannelSelectListEntry TeamId
tId Maybe ChannelSelectMatch
curMatch ChatState
st ChannelSelectMatch
match =
    let ChannelSelectMatch Text
preMatch Text
inMatch Text
postMatch Text
_ ChannelListEntry
entry = ChannelSelectMatch
match
        maybeSelect :: Widget n -> Widget n
maybeSelect = if (forall a. a -> Maybe a
Just ChannelListEntry
entry) forall a. Eq a => a -> a -> Bool
== (ChannelSelectMatch -> ChannelListEntry
matchEntry forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe ChannelSelectMatch
curMatch)
                      then forall n. Widget n -> Widget n
visible forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall n. AttrName -> Widget n -> Widget n
withDefAttr AttrName
currentChannelNameAttr
                      else forall a. a -> a
id
        entryData :: ChannelListEntryData
entryData = ChatState -> TeamId -> ChannelListEntry -> ChannelListEntryData
mkChannelEntryData ChatState
st TeamId
tId ChannelListEntry
entry
        decorate :: Widget n -> Widget n
decorate = if | ChannelListEntryData -> Int
entryMentions ChannelListEntryData
entryData forall a. Ord a => a -> a -> Bool
> Int
0 Bool -> Bool -> Bool
&& Bool -> Bool
not (ChannelListEntryData -> Bool
entryIsMuted ChannelListEntryData
entryData) ->
                          forall n. AttrName -> Widget n -> Widget n
withDefAttr AttrName
mentionsChannelAttr
                      | ChannelListEntryData -> Bool
entryHasUnread ChannelListEntryData
entryData ->
                          forall n. AttrName -> Widget n -> Widget n
withDefAttr AttrName
unreadChannelAttr
                      | Bool
otherwise -> forall a. a -> a
id
    in forall n. Ord n => n -> Widget n -> Widget n
clickable (ChannelSelectMatch -> Name
ClickableChannelSelectEntry ChannelSelectMatch
match) forall a b. (a -> b) -> a -> b
$
       forall n. Widget n -> Widget n
decorate forall a b. (a -> b) -> a -> b
$ forall n. Widget n -> Widget n
maybeSelect forall a b. (a -> b) -> a -> b
$
       forall n. ChannelListEntryData -> Widget n -> Widget n
decorateEntry ChannelListEntryData
entryData forall a b. (a -> b) -> a -> b
$ forall n. ChannelListEntryData -> Widget n -> Widget n
decorateMentions ChannelListEntryData
entryData forall a b. (a -> b) -> a -> b
$
       forall n. Padding -> Widget n -> Widget n
padRight Padding
Max forall a b. (a -> b) -> a -> b
$
         forall n. [Widget n] -> Widget n
hBox [ forall n. Text -> Widget n
txt forall a b. (a -> b) -> a -> b
$ ChannelListEntryData -> Text
entrySigil ChannelListEntryData
entryData forall a. Semigroup a => a -> a -> a
<> Text
preMatch
              , forall n. AttrName -> Widget n -> Widget n
forceAttr AttrName
channelSelectMatchAttr forall a b. (a -> b) -> a -> b
$ forall n. Text -> Widget n
txt Text
inMatch
              , forall n. Text -> Widget n
txt Text
postMatch
              ]

-- If this channel is the return channel, add a decoration to denote
-- that.
--
-- Otherwise, if this channel is the most recently viewed channel (prior
-- to the currently viewed channel), add a decoration to denote that.
decorateEntry :: ChannelListEntryData -> Widget n -> Widget n
decorateEntry :: forall n. ChannelListEntryData -> Widget n -> Widget n
decorateEntry ChannelListEntryData
entry =
    if ChannelListEntryData -> Bool
entryIsReturn ChannelListEntryData
entry
    then (forall n. Widget n -> Widget n -> Widget n
<+> (forall n. AttrName -> Widget n -> Widget n
withDefAttr AttrName
recentMarkerAttr forall a b. (a -> b) -> a -> b
$ forall n. String -> Widget n
str String
returnChannelSigil))
    else if ChannelListEntryData -> Bool
entryIsRecent ChannelListEntryData
entry
         then (forall n. Widget n -> Widget n -> Widget n
<+> (forall n. AttrName -> Widget n -> Widget n
withDefAttr AttrName
recentMarkerAttr forall a b. (a -> b) -> a -> b
$ forall n. String -> Widget n
str String
recentChannelSigil))
         else forall a. a -> a
id

decorateMentions :: ChannelListEntryData -> Widget n -> Widget n
decorateMentions :: forall n. ChannelListEntryData -> Widget n -> Widget n
decorateMentions ChannelListEntryData
entry
  | ChannelListEntryData -> Int
entryMentions ChannelListEntryData
entry forall a. Ord a => a -> a -> Bool
> Int
9 =
      (forall n. Widget n -> Widget n -> Widget n
<+> forall n. String -> Widget n
str String
"(9+)")
  | ChannelListEntryData -> Int
entryMentions ChannelListEntryData
entry forall a. Ord a => a -> a -> Bool
> Int
0 =
      (forall n. Widget n -> Widget n -> Widget n
<+> forall n. String -> Widget n
str (String
"(" forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> String
show (ChannelListEntryData -> Int
entryMentions ChannelListEntryData
entry) forall a. Semigroup a => a -> a -> a
<> String
")"))
  | ChannelListEntryData -> Bool
entryIsMuted ChannelListEntryData
entry =
      (forall n. Widget n -> Widget n -> Widget n
<+> forall n. String -> Widget n
str String
"(m)")
  | Bool
otherwise = forall a. a -> a
id

recentChannelSigil :: String
recentChannelSigil :: String
recentChannelSigil = String
"<"

returnChannelSigil :: String
returnChannelSigil :: String
returnChannelSigil = String
"~"