2021-07-12 19:00:03 +01:00
|
|
|
{-# LANGUAGE DuplicateRecordFields #-}
|
2022-01-26 16:18:27 +04:00
|
|
|
{-# LANGUAGE GADTs #-}
|
2021-06-25 18:18:24 +01:00
|
|
|
{-# LANGUAGE LambdaCase #-}
|
2021-07-12 19:00:03 +01:00
|
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
2021-06-25 18:18:24 +01:00
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
|
|
2022-01-24 16:07:17 +00:00
|
|
|
module Simplex.Chat.View where
|
|
|
|
|
|
2021-09-05 14:08:29 +01:00
|
|
|
import Data.Function (on)
|
2021-09-04 07:32:56 +01:00
|
|
|
import Data.Int (Int64)
|
2022-01-24 16:07:17 +00:00
|
|
|
import Data.List (groupBy, intersperse, sortOn)
|
2021-07-04 18:42:24 +01:00
|
|
|
import Data.Text (Text)
|
2021-06-25 18:18:24 +01:00
|
|
|
import qualified Data.Text as T
|
2022-01-24 16:07:17 +00:00
|
|
|
import Data.Time.Clock (DiffTime)
|
2021-06-25 18:18:24 +01:00
|
|
|
import Data.Time.Format (defaultTimeLocale, formatTime)
|
2022-01-24 16:07:17 +00:00
|
|
|
import Data.Time.LocalTime (ZonedTime (..), localDay, localTimeOfDay, timeOfDayToTime, utcToZonedTime)
|
2021-09-04 07:32:56 +01:00
|
|
|
import Numeric (showFFloat)
|
2021-06-25 18:18:24 +01:00
|
|
|
import Simplex.Chat.Controller
|
2022-01-24 16:07:17 +00:00
|
|
|
import Simplex.Chat.Help
|
2021-06-25 18:18:24 +01:00
|
|
|
import Simplex.Chat.Markdown
|
2022-01-24 16:07:17 +00:00
|
|
|
import Simplex.Chat.Messages
|
|
|
|
|
import Simplex.Chat.Protocol
|
2021-07-12 19:00:03 +01:00
|
|
|
import Simplex.Chat.Store (StoreError (..))
|
2021-06-25 18:18:24 +01:00
|
|
|
import Simplex.Chat.Styled
|
2021-07-04 18:42:24 +01:00
|
|
|
import Simplex.Chat.Types
|
2021-06-25 18:18:24 +01:00
|
|
|
import Simplex.Messaging.Agent.Protocol
|
2022-01-11 08:50:44 +00:00
|
|
|
import Simplex.Messaging.Encoding.String
|
2021-12-08 13:09:51 +00:00
|
|
|
import qualified Simplex.Messaging.Protocol as SMP
|
2021-06-25 18:18:24 +01:00
|
|
|
import System.Console.ANSI.Types
|
|
|
|
|
|
2022-01-24 16:07:17 +00:00
|
|
|
serializeChatResponse :: ChatResponse -> String
|
|
|
|
|
serializeChatResponse = unlines . map unStyle . responseToView ""
|
|
|
|
|
|
|
|
|
|
responseToView :: String -> ChatResponse -> [StyledString]
|
|
|
|
|
responseToView cmd = \case
|
2022-01-26 16:18:27 +04:00
|
|
|
CRNewChatItem (AChatItem _ _ chat item) -> viewChatItem chat item
|
2022-01-26 21:20:08 +00:00
|
|
|
CRCmdAccepted _ -> r []
|
2022-01-24 16:07:17 +00:00
|
|
|
CRChatHelp section -> case section of
|
|
|
|
|
HSMain -> r chatHelpInfo
|
|
|
|
|
HSFiles -> r filesHelpInfo
|
|
|
|
|
HSGroups -> r groupsHelpInfo
|
|
|
|
|
HSMyAddress -> r myAddressHelpInfo
|
|
|
|
|
HSMarkdown -> r markdownInfo
|
|
|
|
|
CRWelcome user -> r $ chatWelcome user
|
|
|
|
|
CRContactsList cs -> r $ viewContactsList cs
|
|
|
|
|
CRUserContactLink cReq -> r $ connReqContact_ "Your chat address:" cReq
|
|
|
|
|
CRContactRequestRejected c -> r [ttyContact c <> ": contact request rejected"]
|
|
|
|
|
CRGroupCreated g -> r $ viewGroupCreated g
|
|
|
|
|
CRGroupMembers g -> r $ viewGroupMembers g
|
|
|
|
|
CRGroupsList gs -> r $ viewGroupsList gs
|
2022-01-26 16:18:27 +04:00
|
|
|
CRSentGroupInvitation g c -> r ["invitation to join the group " <> ttyGroup' g <> " sent to " <> ttyContact' c]
|
2022-01-24 16:07:17 +00:00
|
|
|
CRFileTransferStatus ftStatus -> r $ viewFileTransferStatus ftStatus
|
|
|
|
|
CRUserProfile p -> r $ viewUserProfile p
|
|
|
|
|
CRUserProfileNoChange -> r ["user profile did not change"]
|
|
|
|
|
CRVersionInfo -> r [plain versionStr, plain updateStr]
|
|
|
|
|
CRChatCmdError e -> r $ viewChatError e
|
|
|
|
|
CRInvitation cReq -> r' $ viewConnReqInvitation cReq
|
|
|
|
|
CRSentConfirmation -> r' ["confirmation sent!"]
|
|
|
|
|
CRSentInvitation -> r' ["connection request sent!"]
|
|
|
|
|
CRContactDeleted c -> r' [ttyContact c <> ": contact is deleted"]
|
|
|
|
|
CRAcceptingContactRequest c -> r' [ttyContact c <> ": accepting contact request..."]
|
|
|
|
|
CRUserContactLinkCreated cReq -> r' $ connReqContact_ "Your new chat address is created!" cReq
|
|
|
|
|
CRUserContactLinkDeleted -> r' viewUserContactLinkDeleted
|
2022-01-26 16:18:27 +04:00
|
|
|
CRUserAcceptedGroupSent _g -> r' [] -- [ttyGroup' g <> ": joining the group..."]
|
|
|
|
|
CRUserDeletedMember g m -> r' [ttyGroup' g <> ": you removed " <> ttyMember m <> " from the group"]
|
|
|
|
|
CRLeftMemberUser g -> r' $ [ttyGroup' g <> ": you left the group"] <> groupPreserved g
|
|
|
|
|
CRGroupDeletedUser g -> r' [ttyGroup' g <> ": you deleted the group"]
|
2022-01-24 16:07:17 +00:00
|
|
|
CRRcvFileAccepted RcvFileTransfer {fileId, senderDisplayName = c} filePath ->
|
|
|
|
|
r' ["saving file " <> sShow fileId <> " from " <> ttyContact c <> " to " <> plain filePath]
|
|
|
|
|
CRRcvFileAcceptedSndCancelled ft -> r' $ viewRcvFileSndCancelled ft
|
|
|
|
|
CRSndGroupFileCancelled fts -> r' $ viewSndGroupFileCancelled fts
|
|
|
|
|
CRRcvFileCancelled ft -> r' $ receivingFile_ "cancelled" ft
|
|
|
|
|
CRUserProfileUpdated p p' -> r' $ viewUserProfileUpdated p p'
|
|
|
|
|
CRContactUpdated c c' -> viewContactUpdated c c'
|
|
|
|
|
CRContactsMerged intoCt mergedCt -> viewContactsMerged intoCt mergedCt
|
|
|
|
|
CRReceivedContactRequest c p -> viewReceivedContactRequest c p
|
|
|
|
|
CRRcvFileStart ft -> receivingFile_ "started" ft
|
|
|
|
|
CRRcvFileComplete ft -> receivingFile_ "completed" ft
|
|
|
|
|
CRRcvFileSndCancelled ft -> viewRcvFileSndCancelled ft
|
|
|
|
|
CRSndFileStart ft -> sendingFile_ "started" ft
|
|
|
|
|
CRSndFileComplete ft -> sendingFile_ "completed" ft
|
|
|
|
|
CRSndFileCancelled ft -> sendingFile_ "cancelled" ft
|
|
|
|
|
CRSndFileRcvCancelled ft@SndFileTransfer {recipientDisplayName = c} ->
|
|
|
|
|
[ttyContact c <> " cancelled receiving " <> sndFile ft]
|
|
|
|
|
CRContactConnected ct -> [ttyFullContact ct <> ": contact is connected"]
|
2022-01-26 16:18:27 +04:00
|
|
|
CRContactAnotherClient c -> [ttyContact' c <> ": contact is connected to another client"]
|
|
|
|
|
CRContactDisconnected c -> [ttyContact' c <> ": disconnected from server (messages will be queued)"]
|
|
|
|
|
CRContactSubscribed c -> [ttyContact' c <> ": connected to server"]
|
|
|
|
|
CRContactSubError c e -> [ttyContact' c <> ": contact error " <> sShow e]
|
|
|
|
|
CRGroupInvitation GroupInfo {localDisplayName = ldn, groupProfile = GroupProfile {fullName}} ->
|
2022-01-24 16:07:17 +00:00
|
|
|
[groupInvitation ldn fullName]
|
|
|
|
|
CRReceivedGroupInvitation g c role -> viewReceivedGroupInvitation g c role
|
2022-01-26 16:18:27 +04:00
|
|
|
CRUserJoinedGroup g -> [ttyGroup' g <> ": you joined the group"]
|
|
|
|
|
CRJoinedGroupMember g m -> [ttyGroup' g <> ": " <> ttyMember m <> " joined the group "]
|
|
|
|
|
CRJoinedGroupMemberConnecting g host m -> [ttyGroup' g <> ": " <> ttyMember host <> " added " <> ttyFullMember m <> " to the group (connecting...)"]
|
|
|
|
|
CRConnectedToGroupMember g m -> [ttyGroup' g <> ": " <> connectedMember m <> " is connected"]
|
|
|
|
|
CRDeletedMemberUser g by -> [ttyGroup' g <> ": " <> ttyMember by <> " removed you from the group"] <> groupPreserved g
|
|
|
|
|
CRDeletedMember g by m -> [ttyGroup' g <> ": " <> ttyMember by <> " removed " <> ttyMember m <> " from the group"]
|
|
|
|
|
CRLeftMember g m -> [ttyGroup' g <> ": " <> ttyMember m <> " left the group"]
|
2022-01-24 16:07:17 +00:00
|
|
|
CRGroupEmpty g -> [ttyFullGroup g <> ": group is empty"]
|
|
|
|
|
CRGroupRemoved g -> [ttyFullGroup g <> ": you are no longer a member or group deleted"]
|
2022-01-26 16:18:27 +04:00
|
|
|
CRGroupDeleted g m -> [ttyGroup' g <> ": " <> ttyMember m <> " deleted the group", "use " <> highlight ("/d #" <> groupName g) <> " to delete the local copy of the group"]
|
|
|
|
|
CRMemberSubError g c e -> [ttyGroup' g <> " member " <> ttyContact c <> " error: " <> sShow e]
|
2022-01-24 16:07:17 +00:00
|
|
|
CRGroupSubscribed g -> [ttyFullGroup g <> ": connected to server(s)"]
|
|
|
|
|
CRSndFileSubError SndFileTransfer {fileId, fileName} e ->
|
|
|
|
|
["sent file " <> sShow fileId <> " (" <> plain fileName <> ") error: " <> sShow e]
|
|
|
|
|
CRRcvFileSubError RcvFileTransfer {fileId, fileInvitation = FileInvitation {fileName}} e ->
|
|
|
|
|
["received file " <> sShow fileId <> " (" <> plain fileName <> ") error: " <> sShow e]
|
|
|
|
|
CRUserContactLinkSubscribed -> ["Your address is active! To show: " <> highlight' "/sa"]
|
|
|
|
|
CRUserContactLinkSubError e -> ["user address error: " <> sShow e, "to delete your address: " <> highlight' "/da"]
|
|
|
|
|
CRMessageError prefix err -> [plain prefix <> ": " <> plain err]
|
|
|
|
|
CRChatError e -> viewChatError e
|
|
|
|
|
where
|
|
|
|
|
r = (plain cmd :)
|
|
|
|
|
-- this function should be `id` in case of asynchronous command responses
|
|
|
|
|
r' = r
|
2021-06-25 18:18:24 +01:00
|
|
|
|
2022-01-26 16:18:27 +04:00
|
|
|
viewChatItem :: ChatInfo c -> ChatItem c d -> [StyledString]
|
|
|
|
|
viewChatItem chat item = case (chat, item) of
|
|
|
|
|
(DirectChat c, DirectChatItem ciMeta content) -> case ciMeta of
|
|
|
|
|
CISndMeta meta -> case content of
|
|
|
|
|
CIMsgContent mc -> viewSentMessage to mc meta
|
|
|
|
|
CISndFileInvitation fId fPath -> viewSentFileInvitation to fId fPath meta
|
|
|
|
|
CIRcvMeta meta mOk -> case content of
|
|
|
|
|
CIMsgContent mc -> viewReceivedMessage from meta mc mOk
|
|
|
|
|
CIRcvFileInvitation ft -> viewReceivedFileInvitation from meta ft mOk
|
|
|
|
|
where
|
|
|
|
|
to = ttyToContact' c
|
|
|
|
|
from = ttyFromContact' c
|
|
|
|
|
(GroupChat g, SndGroupChatItem (CISndMeta meta) content) -> case content of
|
|
|
|
|
CIMsgContent mc -> viewSentMessage to mc meta
|
|
|
|
|
CISndFileInvitation fId fPath -> viewSentFileInvitation to fId fPath meta
|
|
|
|
|
where
|
|
|
|
|
to = ttyToGroup g
|
|
|
|
|
(GroupChat g, RcvGroupChatItem c (CIRcvMeta meta mOk) content) -> case content of
|
|
|
|
|
CIMsgContent mc -> viewReceivedMessage from meta mc mOk
|
|
|
|
|
CIRcvFileInvitation ft -> viewReceivedFileInvitation from meta ft mOk
|
|
|
|
|
where
|
|
|
|
|
from = ttyFromGroup' g c
|
|
|
|
|
where
|
|
|
|
|
ttyToContact' Contact {localDisplayName = c} = ttyToContact c
|
|
|
|
|
ttyFromContact' Contact {localDisplayName = c} = ttyFromContact c
|
|
|
|
|
ttyFromGroup' g GroupMember {localDisplayName = c} = ttyFromGroup g c
|
|
|
|
|
|
2022-01-21 11:09:33 +00:00
|
|
|
viewInvalidConnReq :: [StyledString]
|
|
|
|
|
viewInvalidConnReq =
|
|
|
|
|
[ "",
|
|
|
|
|
"Connection link is invalid, possibly it was created in a previous version.",
|
|
|
|
|
"Please ask your contact to check " <> highlight' "/version" <> " and update if needed.",
|
|
|
|
|
plain updateStr
|
|
|
|
|
]
|
2021-08-22 15:56:36 +01:00
|
|
|
|
2022-01-21 11:09:33 +00:00
|
|
|
viewConnReqInvitation :: ConnReqInvitation -> [StyledString]
|
|
|
|
|
viewConnReqInvitation cReq =
|
2021-12-08 13:09:51 +00:00
|
|
|
[ "pass this invitation link to your contact (via another channel): ",
|
2021-06-25 18:18:24 +01:00
|
|
|
"",
|
2022-01-11 08:50:44 +00:00
|
|
|
(plain . strEncode) cReq,
|
2021-06-25 18:18:24 +01:00
|
|
|
"",
|
2021-12-08 13:09:51 +00:00
|
|
|
"and ask them to connect: " <> highlight' "/c <invitation_link_above>"
|
2021-06-25 18:18:24 +01:00
|
|
|
]
|
|
|
|
|
|
2022-01-21 11:09:33 +00:00
|
|
|
viewContactsList :: [Contact] -> [StyledString]
|
|
|
|
|
viewContactsList =
|
2021-12-10 11:45:58 +00:00
|
|
|
let ldn = T.toLower . (localDisplayName :: Contact -> ContactName)
|
|
|
|
|
in map ttyFullContact . sortOn ldn
|
|
|
|
|
|
2022-01-21 11:09:33 +00:00
|
|
|
viewUserContactLinkDeleted :: [StyledString]
|
|
|
|
|
viewUserContactLinkDeleted =
|
2021-12-08 13:09:51 +00:00
|
|
|
[ "Your chat address is deleted - accepted contacts will remain connected.",
|
|
|
|
|
"To create a new chat address use " <> highlight' "/ad"
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
connReqContact_ :: StyledString -> ConnReqContact -> [StyledString]
|
|
|
|
|
connReqContact_ intro cReq =
|
|
|
|
|
[ intro,
|
|
|
|
|
"",
|
2022-01-11 08:50:44 +00:00
|
|
|
(plain . strEncode) cReq,
|
2021-12-08 13:09:51 +00:00
|
|
|
"",
|
|
|
|
|
"Anybody can send you contact requests with: " <> highlight' "/c <contact_link_above>",
|
|
|
|
|
"to show it again: " <> highlight' "/sa",
|
|
|
|
|
"to delete it: " <> highlight' "/da" <> " (accepted contacts will remain connected)"
|
|
|
|
|
]
|
|
|
|
|
|
2022-01-21 11:09:33 +00:00
|
|
|
viewReceivedContactRequest :: ContactName -> Profile -> [StyledString]
|
|
|
|
|
viewReceivedContactRequest c Profile {fullName} =
|
2021-12-08 13:09:51 +00:00
|
|
|
[ ttyFullName c fullName <> " wants to connect to you!",
|
|
|
|
|
"to accept: " <> highlight ("/ac " <> c),
|
|
|
|
|
"to reject: " <> highlight ("/rc " <> c) <> " (the sender will NOT be notified)"
|
|
|
|
|
]
|
|
|
|
|
|
2022-01-26 16:18:27 +04:00
|
|
|
viewGroupCreated :: GroupInfo -> [StyledString]
|
|
|
|
|
viewGroupCreated g@GroupInfo {localDisplayName} =
|
2021-07-16 07:40:55 +01:00
|
|
|
[ "group " <> ttyFullGroup g <> " is created",
|
2021-07-24 10:26:28 +01:00
|
|
|
"use " <> highlight ("/a " <> localDisplayName <> " <name>") <> " to add members"
|
2021-07-16 07:40:55 +01:00
|
|
|
]
|
|
|
|
|
|
2022-01-26 16:18:27 +04:00
|
|
|
viewCannotResendInvitation :: GroupInfo -> ContactName -> [StyledString]
|
|
|
|
|
viewCannotResendInvitation GroupInfo {localDisplayName = gn} c =
|
|
|
|
|
[ ttyContact c <> " is already invited to group " <> ttyGroup gn,
|
|
|
|
|
"to re-send invitation: " <> highlight ("/rm " <> gn <> " " <> c) <> ", " <> highlight ("/a " <> gn <> " " <> c)
|
2022-01-06 23:39:58 +04:00
|
|
|
]
|
|
|
|
|
|
2022-01-26 16:18:27 +04:00
|
|
|
viewReceivedGroupInvitation :: GroupInfo -> Contact -> GroupMemberRole -> [StyledString]
|
|
|
|
|
viewReceivedGroupInvitation g c role =
|
|
|
|
|
[ ttyFullGroup g <> ": " <> ttyContact' c <> " invites you to join the group as " <> plain (strEncode role),
|
|
|
|
|
"use " <> highlight ("/j " <> groupName g) <> " to accept"
|
2021-07-16 07:40:55 +01:00
|
|
|
]
|
|
|
|
|
|
2022-01-26 16:18:27 +04:00
|
|
|
groupPreserved :: GroupInfo -> [StyledString]
|
|
|
|
|
groupPreserved g = ["use " <> highlight ("/d #" <> groupName g) <> " to delete the group"]
|
2021-08-02 20:10:24 +01:00
|
|
|
|
2021-07-24 10:26:28 +01:00
|
|
|
connectedMember :: GroupMember -> StyledString
|
|
|
|
|
connectedMember m = case memberCategory m of
|
|
|
|
|
GCPreMember -> "member " <> ttyFullMember m
|
|
|
|
|
GCPostMember -> "new member " <> ttyMember m -- without fullName as as it was shown in joinedGroupMemberConnecting
|
|
|
|
|
_ -> "member " <> ttyMember m -- these case is not used
|
|
|
|
|
|
2022-01-21 11:09:33 +00:00
|
|
|
viewGroupMembers :: Group -> [StyledString]
|
2022-01-26 16:18:27 +04:00
|
|
|
viewGroupMembers (Group GroupInfo {membership} members) = map groupMember . filter (not . removedOrLeft) $ membership : members
|
2021-07-27 08:08:05 +01:00
|
|
|
where
|
|
|
|
|
removedOrLeft m = let s = memberStatus m in s == GSMemRemoved || s == GSMemLeft
|
|
|
|
|
groupMember m = ttyFullMember m <> ": " <> role m <> ", " <> category m <> status m
|
2022-01-11 08:50:44 +00:00
|
|
|
role m = plain . strEncode $ memberRole (m :: GroupMember)
|
2021-07-27 08:08:05 +01:00
|
|
|
category m = case memberCategory m of
|
|
|
|
|
GCUserMember -> "you, "
|
|
|
|
|
GCInviteeMember -> "invited, "
|
|
|
|
|
GCHostMember -> "host, "
|
|
|
|
|
_ -> ""
|
|
|
|
|
status m = case memberStatus m of
|
|
|
|
|
GSMemRemoved -> "removed"
|
|
|
|
|
GSMemLeft -> "left"
|
|
|
|
|
GSMemInvited -> "not yet joined"
|
|
|
|
|
GSMemConnected -> "connected"
|
|
|
|
|
GSMemComplete -> "connected"
|
|
|
|
|
GSMemCreator -> "created group"
|
|
|
|
|
_ -> ""
|
|
|
|
|
|
2022-01-24 16:07:17 +00:00
|
|
|
viewGroupsList :: [GroupInfo] -> [StyledString]
|
2022-01-21 11:09:33 +00:00
|
|
|
viewGroupsList [] = ["you have no groups!", "to create: " <> highlight' "/g <name>"]
|
2022-01-24 16:07:17 +00:00
|
|
|
viewGroupsList gs = map groupSS $ sortOn ldn_ gs
|
2021-12-18 10:23:47 +00:00
|
|
|
where
|
2022-01-24 16:07:17 +00:00
|
|
|
ldn_ = T.toLower . (localDisplayName :: GroupInfo -> GroupName)
|
2022-01-26 16:18:27 +04:00
|
|
|
groupSS GroupInfo {localDisplayName = ldn, groupProfile = GroupProfile {fullName}, membership} =
|
|
|
|
|
case memberStatus membership of
|
2022-01-24 16:07:17 +00:00
|
|
|
GSMemInvited -> groupInvitation ldn fullName
|
|
|
|
|
_ -> ttyGroup ldn <> optFullName ldn fullName
|
2022-01-21 11:09:33 +00:00
|
|
|
|
2022-01-06 23:39:58 +04:00
|
|
|
groupInvitation :: GroupName -> Text -> StyledString
|
|
|
|
|
groupInvitation displayName fullName =
|
2022-01-06 14:24:33 +04:00
|
|
|
highlight ("#" <> displayName)
|
|
|
|
|
<> optFullName displayName fullName
|
|
|
|
|
<> " - you are invited ("
|
|
|
|
|
<> highlight ("/j " <> displayName)
|
|
|
|
|
<> " to join, "
|
|
|
|
|
<> highlight ("/d #" <> displayName)
|
|
|
|
|
<> " to delete invitation)"
|
2021-12-10 11:45:58 +00:00
|
|
|
|
2022-01-21 11:09:33 +00:00
|
|
|
viewContactsMerged :: Contact -> Contact -> [StyledString]
|
2022-01-24 16:07:17 +00:00
|
|
|
viewContactsMerged _into@Contact {localDisplayName = c1} _merged@Contact {localDisplayName = c2} =
|
2021-07-27 08:08:05 +01:00
|
|
|
[ "contact " <> ttyContact c2 <> " is merged into " <> ttyContact c1,
|
|
|
|
|
"use " <> ttyToContact c1 <> highlight' "<message>" <> " to send messages"
|
|
|
|
|
]
|
|
|
|
|
|
2022-01-21 11:09:33 +00:00
|
|
|
viewUserProfile :: Profile -> [StyledString]
|
|
|
|
|
viewUserProfile Profile {displayName, fullName} =
|
2021-08-22 15:56:36 +01:00
|
|
|
[ "user profile: " <> ttyFullName displayName fullName,
|
2021-09-04 07:32:56 +01:00
|
|
|
"use " <> highlight' "/p <display name> [<full name>]" <> " to change it",
|
2021-08-22 15:56:36 +01:00
|
|
|
"(the updated profile will be sent to all your contacts)"
|
|
|
|
|
]
|
|
|
|
|
|
2022-01-24 16:07:17 +00:00
|
|
|
viewUserProfileUpdated :: Profile -> Profile -> [StyledString]
|
|
|
|
|
viewUserProfileUpdated Profile {displayName = n, fullName} Profile {displayName = n', fullName = fullName'}
|
|
|
|
|
| n == n' && fullName == fullName' = []
|
|
|
|
|
| n == n' = ["user full name " <> (if T.null fullName' || fullName' == n' then "removed" else "changed to " <> plain fullName') <> notified]
|
|
|
|
|
| otherwise = ["user profile is changed to " <> ttyFullName n' fullName' <> notified]
|
|
|
|
|
where
|
|
|
|
|
notified = " (your contacts are notified)"
|
2021-08-22 15:56:36 +01:00
|
|
|
|
2022-01-21 11:09:33 +00:00
|
|
|
viewContactUpdated :: Contact -> Contact -> [StyledString]
|
|
|
|
|
viewContactUpdated
|
2021-08-22 15:56:36 +01:00
|
|
|
Contact {localDisplayName = n, profile = Profile {fullName}}
|
|
|
|
|
Contact {localDisplayName = n', profile = Profile {fullName = fullName'}}
|
|
|
|
|
| n == n' && fullName == fullName' = []
|
|
|
|
|
| n == n' = ["contact " <> ttyContact n <> fullNameUpdate]
|
|
|
|
|
| otherwise =
|
|
|
|
|
[ "contact " <> ttyContact n <> " changed to " <> ttyFullName n' fullName',
|
|
|
|
|
"use " <> ttyToContact n' <> highlight' "<message>" <> " to send messages"
|
|
|
|
|
]
|
|
|
|
|
where
|
|
|
|
|
fullNameUpdate = if T.null fullName' || fullName' == n' then " removed full name" else " updated full name: " <> plain fullName'
|
|
|
|
|
|
2022-01-26 16:18:27 +04:00
|
|
|
viewReceivedMessage :: StyledString -> CIMetaProps -> MsgContent -> MsgIntegrity -> [StyledString]
|
2022-01-24 16:07:17 +00:00
|
|
|
viewReceivedMessage from meta mc = receivedWithTime_ from meta (ttyMsgContent mc)
|
2021-09-04 07:32:56 +01:00
|
|
|
|
2022-01-26 16:18:27 +04:00
|
|
|
receivedWithTime_ :: StyledString -> CIMetaProps -> [StyledString] -> MsgIntegrity -> [StyledString]
|
|
|
|
|
receivedWithTime_ from CIMetaProps {localItemTs, createdAt} styledMsg mOk = do
|
2022-01-24 16:07:17 +00:00
|
|
|
prependFirst (formattedTime <> " " <> from) styledMsg ++ showIntegrity mOk
|
2021-06-25 18:18:24 +01:00
|
|
|
where
|
2022-01-24 16:07:17 +00:00
|
|
|
formattedTime :: StyledString
|
|
|
|
|
formattedTime =
|
2022-01-26 16:18:27 +04:00
|
|
|
let localTime = zonedTimeToLocalTime localItemTs
|
|
|
|
|
tz = zonedTimeZone localItemTs
|
2021-06-25 18:18:24 +01:00
|
|
|
format =
|
2022-01-24 16:07:17 +00:00
|
|
|
if (localDay localTime < localDay (zonedTimeToLocalTime $ utcToZonedTime tz createdAt))
|
2021-06-25 18:18:24 +01:00
|
|
|
&& (timeOfDayToTime (localTimeOfDay localTime) > (6 * 60 * 60 :: DiffTime))
|
|
|
|
|
then "%m-%d" -- if message is from yesterday or before and 6 hours has passed since midnight
|
|
|
|
|
else "%H:%M"
|
|
|
|
|
in styleTime $ formatTime defaultTimeLocale format localTime
|
|
|
|
|
showIntegrity :: MsgIntegrity -> [StyledString]
|
|
|
|
|
showIntegrity MsgOk = []
|
|
|
|
|
showIntegrity (MsgError err) = msgError $ case err of
|
|
|
|
|
MsgSkipped fromId toId ->
|
|
|
|
|
"skipped message ID " <> show fromId
|
|
|
|
|
<> if fromId == toId then "" else ".." <> show toId
|
|
|
|
|
MsgBadId msgId -> "unexpected message ID " <> show msgId
|
|
|
|
|
MsgBadHash -> "incorrect message hash"
|
|
|
|
|
MsgDuplicate -> "duplicate message ID"
|
|
|
|
|
msgError :: String -> [StyledString]
|
|
|
|
|
msgError s = [styled (Colored Red) s]
|
|
|
|
|
|
2022-01-26 16:18:27 +04:00
|
|
|
viewSentMessage :: StyledString -> MsgContent -> CIMetaProps -> [StyledString]
|
2022-01-24 16:07:17 +00:00
|
|
|
viewSentMessage to = sentWithTime_ . prependFirst to . ttyMsgContent
|
2022-01-21 11:09:33 +00:00
|
|
|
|
2022-01-26 16:18:27 +04:00
|
|
|
viewSentFileInvitation :: StyledString -> FileTransferId -> FilePath -> CIMetaProps -> [StyledString]
|
2022-01-24 16:07:17 +00:00
|
|
|
viewSentFileInvitation to fId fPath = sentWithTime_ $ ttySentFile to fId fPath
|
2022-01-21 11:09:33 +00:00
|
|
|
|
2022-01-26 16:18:27 +04:00
|
|
|
sentWithTime_ :: [StyledString] -> CIMetaProps -> [StyledString]
|
|
|
|
|
sentWithTime_ styledMsg CIMetaProps {localItemTs} =
|
|
|
|
|
prependFirst (ttyMsgTime localItemTs <> " ") styledMsg
|
2022-01-21 11:09:33 +00:00
|
|
|
|
2022-01-24 16:07:17 +00:00
|
|
|
ttyMsgTime :: ZonedTime -> StyledString
|
|
|
|
|
ttyMsgTime = styleTime . formatTime defaultTimeLocale "%H:%M"
|
2021-09-05 14:08:29 +01:00
|
|
|
|
2022-01-24 16:07:17 +00:00
|
|
|
ttyMsgContent :: MsgContent -> [StyledString]
|
|
|
|
|
ttyMsgContent = \case
|
|
|
|
|
MCText t -> msgPlain t
|
|
|
|
|
MCUnknown -> ["unknown message type"]
|
2021-09-05 14:08:29 +01:00
|
|
|
|
2022-01-24 16:07:17 +00:00
|
|
|
ttySentFile :: StyledString -> FileTransferId -> FilePath -> [StyledString]
|
|
|
|
|
ttySentFile to fId fPath = ["/f " <> to <> ttyFilePath fPath, "use " <> highlight ("/fc " <> show fId) <> " to cancel sending"]
|
2021-06-25 18:18:24 +01:00
|
|
|
|
|
|
|
|
prependFirst :: StyledString -> [StyledString] -> [StyledString]
|
|
|
|
|
prependFirst s [] = [s]
|
|
|
|
|
prependFirst s (s' : ss) = (s <> s') : ss
|
|
|
|
|
|
2021-07-04 18:42:24 +01:00
|
|
|
msgPlain :: Text -> [StyledString]
|
|
|
|
|
msgPlain = map styleMarkdownText . T.lines
|
|
|
|
|
|
2022-01-24 16:07:17 +00:00
|
|
|
viewRcvFileSndCancelled :: RcvFileTransfer -> [StyledString]
|
|
|
|
|
viewRcvFileSndCancelled ft@RcvFileTransfer {senderDisplayName = c} =
|
|
|
|
|
[ttyContact c <> " cancelled sending " <> rcvFile ft]
|
2021-09-04 07:32:56 +01:00
|
|
|
|
2022-01-21 11:09:33 +00:00
|
|
|
viewSndGroupFileCancelled :: [SndFileTransfer] -> [StyledString]
|
|
|
|
|
viewSndGroupFileCancelled fts =
|
2021-09-05 14:08:29 +01:00
|
|
|
case filter (\SndFileTransfer {fileStatus = s} -> s /= FSCancelled && s /= FSComplete) fts of
|
|
|
|
|
[] -> ["sending file can't be cancelled"]
|
|
|
|
|
ts@(ft : _) -> ["cancelled sending " <> sndFile ft <> " to " <> listMembers ts]
|
2021-09-04 07:32:56 +01:00
|
|
|
|
2021-09-05 14:08:29 +01:00
|
|
|
sendingFile_ :: StyledString -> SndFileTransfer -> [StyledString]
|
|
|
|
|
sendingFile_ status ft@SndFileTransfer {recipientDisplayName = c} =
|
|
|
|
|
[status <> " sending " <> sndFile ft <> " to " <> ttyContact c]
|
2021-09-04 07:32:56 +01:00
|
|
|
|
2021-09-05 14:08:29 +01:00
|
|
|
sndFile :: SndFileTransfer -> StyledString
|
2022-01-26 21:20:08 +00:00
|
|
|
sndFile SndFileTransfer {fileId, fileName} = fileTransferStr fileId fileName
|
2021-09-05 14:08:29 +01:00
|
|
|
|
2022-01-26 16:18:27 +04:00
|
|
|
viewReceivedFileInvitation :: StyledString -> CIMetaProps -> RcvFileTransfer -> MsgIntegrity -> [StyledString]
|
2022-01-24 16:07:17 +00:00
|
|
|
viewReceivedFileInvitation from meta ft = receivedWithTime_ from meta (receivedFileInvitation_ ft)
|
2022-01-21 11:09:33 +00:00
|
|
|
|
|
|
|
|
receivedFileInvitation_ :: RcvFileTransfer -> [StyledString]
|
|
|
|
|
receivedFileInvitation_ RcvFileTransfer {fileId, fileInvitation = FileInvitation {fileName, fileSize}} =
|
2021-09-05 14:08:29 +01:00
|
|
|
[ "sends file " <> ttyFilePath fileName <> " (" <> humanReadableSize fileSize <> " / " <> sShow fileSize <> " bytes)",
|
2021-09-04 07:32:56 +01:00
|
|
|
"use " <> highlight ("/fr " <> show fileId <> " [<dir>/ | <path>]") <> " to receive it"
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
humanReadableSize :: Integer -> StyledString
|
|
|
|
|
humanReadableSize size
|
|
|
|
|
| size < kB = sShow size <> " bytes"
|
|
|
|
|
| size < mB = hrSize kB "KiB"
|
|
|
|
|
| size < gB = hrSize mB "MiB"
|
|
|
|
|
| otherwise = hrSize gB "GiB"
|
|
|
|
|
where
|
|
|
|
|
hrSize sB name = plain $ unwords [showFFloat (Just 1) (fromIntegral size / (fromIntegral sB :: Double)) "", name]
|
|
|
|
|
kB = 1024
|
|
|
|
|
mB = kB * 1024
|
|
|
|
|
gB = mB * 1024
|
|
|
|
|
|
2021-09-05 14:08:29 +01:00
|
|
|
receivingFile_ :: StyledString -> RcvFileTransfer -> [StyledString]
|
|
|
|
|
receivingFile_ status ft@RcvFileTransfer {senderDisplayName = c} =
|
|
|
|
|
[status <> " receiving " <> rcvFile ft <> " from " <> ttyContact c]
|
2021-09-04 07:32:56 +01:00
|
|
|
|
2021-09-05 14:08:29 +01:00
|
|
|
rcvFile :: RcvFileTransfer -> StyledString
|
2022-01-26 21:20:08 +00:00
|
|
|
rcvFile RcvFileTransfer {fileId, fileInvitation = FileInvitation {fileName}} = fileTransferStr fileId fileName
|
2021-09-05 14:08:29 +01:00
|
|
|
|
2022-01-26 21:20:08 +00:00
|
|
|
fileTransferStr :: Int64 -> String -> StyledString
|
|
|
|
|
fileTransferStr fileId fileName = "file " <> sShow fileId <> " (" <> ttyFilePath fileName <> ")"
|
2021-09-04 07:32:56 +01:00
|
|
|
|
2022-01-21 11:09:33 +00:00
|
|
|
viewFileTransferStatus :: (FileTransfer, [Integer]) -> [StyledString]
|
|
|
|
|
viewFileTransferStatus (FTSnd [ft@SndFileTransfer {fileStatus, fileSize, chunkSize}], chunksNum) =
|
2021-09-05 14:08:29 +01:00
|
|
|
["sending " <> sndFile ft <> " " <> sndStatus]
|
2021-09-04 07:32:56 +01:00
|
|
|
where
|
|
|
|
|
sndStatus = case fileStatus of
|
2021-09-05 14:08:29 +01:00
|
|
|
FSNew -> "not accepted yet"
|
2021-09-04 07:32:56 +01:00
|
|
|
FSAccepted -> "just started"
|
2021-09-05 14:08:29 +01:00
|
|
|
FSConnected -> "progress " <> fileProgress chunksNum chunkSize fileSize
|
|
|
|
|
FSComplete -> "complete"
|
|
|
|
|
FSCancelled -> "cancelled"
|
2022-01-21 11:09:33 +00:00
|
|
|
viewFileTransferStatus (FTSnd [], _) = ["no file transfers (empty group)"]
|
|
|
|
|
viewFileTransferStatus (FTSnd fts@(ft : _), chunksNum) =
|
2021-09-05 14:08:29 +01:00
|
|
|
case concatMap membersTransferStatus $ groupBy ((==) `on` fs) $ sortOn fs fts of
|
|
|
|
|
[membersStatus] -> ["sending " <> sndFile ft <> " " <> membersStatus]
|
|
|
|
|
membersStatuses -> ("sending " <> sndFile ft <> ": ") : map (" " <>) membersStatuses
|
|
|
|
|
where
|
|
|
|
|
fs = fileStatus :: SndFileTransfer -> FileStatus
|
|
|
|
|
membersTransferStatus [] = []
|
|
|
|
|
membersTransferStatus ts@(SndFileTransfer {fileStatus, fileSize, chunkSize} : _) = [sndStatus <> ": " <> listMembers ts]
|
|
|
|
|
where
|
|
|
|
|
sndStatus = case fileStatus of
|
|
|
|
|
FSNew -> "not accepted"
|
|
|
|
|
FSAccepted -> "just started"
|
|
|
|
|
FSConnected -> "in progress (" <> sShow (sum chunksNum * chunkSize * 100 `div` (toInteger (length chunksNum) * fileSize)) <> "%)"
|
|
|
|
|
FSComplete -> "complete"
|
|
|
|
|
FSCancelled -> "cancelled"
|
2022-01-21 11:09:33 +00:00
|
|
|
viewFileTransferStatus (FTRcv ft@RcvFileTransfer {fileId, fileInvitation = FileInvitation {fileSize}, fileStatus, chunkSize}, chunksNum) =
|
2021-09-05 14:08:29 +01:00
|
|
|
["receiving " <> rcvFile ft <> " " <> rcvStatus]
|
2021-09-04 07:32:56 +01:00
|
|
|
where
|
|
|
|
|
rcvStatus = case fileStatus of
|
2021-09-05 14:08:29 +01:00
|
|
|
RFSNew -> "not accepted yet, use " <> highlight ("/fr " <> show fileId) <> " to receive file"
|
2021-09-04 07:32:56 +01:00
|
|
|
RFSAccepted _ -> "just started"
|
2021-09-05 14:08:29 +01:00
|
|
|
RFSConnected _ -> "progress " <> fileProgress chunksNum chunkSize fileSize
|
|
|
|
|
RFSComplete RcvFileInfo {filePath} -> "complete, path: " <> plain filePath
|
|
|
|
|
RFSCancelled RcvFileInfo {filePath} -> "cancelled, received part path: " <> plain filePath
|
|
|
|
|
|
|
|
|
|
listMembers :: [SndFileTransfer] -> StyledString
|
|
|
|
|
listMembers = mconcat . intersperse ", " . map (ttyContact . recipientDisplayName)
|
2021-09-04 07:32:56 +01:00
|
|
|
|
|
|
|
|
fileProgress :: [Integer] -> Integer -> Integer -> StyledString
|
|
|
|
|
fileProgress chunksNum chunkSize fileSize =
|
|
|
|
|
sShow (sum chunksNum * chunkSize * 100 `div` fileSize) <> "% of " <> humanReadableSize fileSize
|
|
|
|
|
|
2022-01-21 11:09:33 +00:00
|
|
|
viewChatError :: ChatError -> [StyledString]
|
|
|
|
|
viewChatError = \case
|
2021-07-16 07:40:55 +01:00
|
|
|
ChatError err -> case err of
|
2022-01-24 16:07:17 +00:00
|
|
|
CEInvalidConnReq -> viewInvalidConnReq
|
|
|
|
|
CEContactGroups c gNames -> [ttyContact c <> ": contact cannot be deleted, it is a member of the group(s) " <> ttyGroups gNames]
|
2021-07-16 07:40:55 +01:00
|
|
|
CEGroupDuplicateMember c -> ["contact " <> ttyContact c <> " is already in the group"]
|
|
|
|
|
CEGroupDuplicateMemberId -> ["cannot add member - duplicate member ID"]
|
2021-07-24 10:26:28 +01:00
|
|
|
CEGroupUserRole -> ["you have insufficient permissions for this group command"]
|
|
|
|
|
CEGroupContactRole c -> ["contact " <> ttyContact c <> " has insufficient permissions for this group action"]
|
2022-01-26 16:18:27 +04:00
|
|
|
CEGroupNotJoined g -> ["you did not join this group, use " <> highlight ("/join #" <> groupName g)]
|
2021-07-24 10:26:28 +01:00
|
|
|
CEGroupMemberNotActive -> ["you cannot invite other members yet, try later"]
|
2022-01-05 20:46:35 +04:00
|
|
|
CEGroupMemberUserRemoved -> ["you are no longer a member of the group"]
|
2021-08-02 20:10:24 +01:00
|
|
|
CEGroupMemberNotFound c -> ["contact " <> ttyContact c <> " is not a group member"]
|
2022-01-24 16:07:17 +00:00
|
|
|
CEGroupMemberIntroNotFound c -> ["group member intro not found for " <> ttyContact c]
|
|
|
|
|
CEGroupCantResendInvitation g c -> viewCannotResendInvitation g c
|
2021-07-16 07:40:55 +01:00
|
|
|
CEGroupInternal s -> ["chat group bug: " <> plain s]
|
2021-09-04 07:32:56 +01:00
|
|
|
CEFileNotFound f -> ["file not found: " <> plain f]
|
|
|
|
|
CEFileAlreadyReceiving f -> ["file is already accepted: " <> plain f]
|
|
|
|
|
CEFileAlreadyExists f -> ["file already exists: " <> plain f]
|
|
|
|
|
CEFileRead f e -> ["cannot read file " <> plain f, sShow e]
|
|
|
|
|
CEFileWrite f e -> ["cannot write file " <> plain f, sShow e]
|
|
|
|
|
CEFileSend fileId e -> ["error sending file " <> sShow fileId <> ": " <> sShow e]
|
|
|
|
|
CEFileRcvChunk e -> ["error receiving file: " <> plain e]
|
|
|
|
|
CEFileInternal e -> ["file error: " <> plain e]
|
2022-01-11 08:50:44 +00:00
|
|
|
CEAgentVersion -> ["unsupported agent version"]
|
2022-01-24 16:07:17 +00:00
|
|
|
CECommandError e -> ["bad chat command: " <> plain e]
|
2021-09-04 07:32:56 +01:00
|
|
|
-- e -> ["chat error: " <> sShow e]
|
2021-07-12 19:00:03 +01:00
|
|
|
ChatErrorStore err -> case err of
|
2021-07-14 20:11:41 +01:00
|
|
|
SEDuplicateName -> ["this display name is already used by user, contact or group"]
|
2021-07-12 19:00:03 +01:00
|
|
|
SEContactNotFound c -> ["no contact " <> ttyContact c]
|
|
|
|
|
SEContactNotReady c -> ["contact " <> ttyContact c <> " is not active yet"]
|
2021-07-16 07:40:55 +01:00
|
|
|
SEGroupNotFound g -> ["no group " <> ttyGroup g]
|
|
|
|
|
SEGroupAlreadyJoined -> ["you already joined this group"]
|
2021-09-04 07:32:56 +01:00
|
|
|
SEFileNotFound fileId -> fileNotFound fileId
|
|
|
|
|
SESndFileNotFound fileId -> fileNotFound fileId
|
|
|
|
|
SERcvFileNotFound fileId -> fileNotFound fileId
|
2021-12-08 13:09:51 +00:00
|
|
|
SEDuplicateContactLink -> ["you already have chat address, to show: " <> highlight' "/sa"]
|
|
|
|
|
SEUserContactLinkNotFound -> ["no chat address, to create: " <> highlight' "/ad"]
|
|
|
|
|
SEContactRequestNotFound c -> ["no contact request from " <> ttyContact c]
|
2021-09-04 07:32:56 +01:00
|
|
|
e -> ["chat db error: " <> sShow e]
|
2021-12-08 13:09:51 +00:00
|
|
|
ChatErrorAgent err -> case err of
|
|
|
|
|
SMP SMP.AUTH -> ["error: this connection is deleted"]
|
|
|
|
|
e -> ["smp agent error: " <> sShow e]
|
2021-09-04 07:32:56 +01:00
|
|
|
ChatErrorMessage e -> ["chat message error: " <> sShow e]
|
|
|
|
|
where
|
|
|
|
|
fileNotFound fileId = ["file " <> sShow fileId <> " not found"]
|
2021-06-25 18:18:24 +01:00
|
|
|
|
2021-07-14 20:11:41 +01:00
|
|
|
ttyContact :: ContactName -> StyledString
|
2021-07-04 18:42:24 +01:00
|
|
|
ttyContact = styled (Colored Green)
|
2021-06-25 18:18:24 +01:00
|
|
|
|
2022-01-26 16:18:27 +04:00
|
|
|
ttyContact' :: Contact -> StyledString
|
|
|
|
|
ttyContact' Contact {localDisplayName = c} = ttyContact c
|
|
|
|
|
|
2021-07-16 07:40:55 +01:00
|
|
|
ttyFullContact :: Contact -> StyledString
|
|
|
|
|
ttyFullContact Contact {localDisplayName, profile = Profile {fullName}} =
|
2021-07-24 10:26:28 +01:00
|
|
|
ttyFullName localDisplayName fullName
|
|
|
|
|
|
|
|
|
|
ttyMember :: GroupMember -> StyledString
|
|
|
|
|
ttyMember GroupMember {localDisplayName} = ttyContact localDisplayName
|
|
|
|
|
|
|
|
|
|
ttyFullMember :: GroupMember -> StyledString
|
|
|
|
|
ttyFullMember GroupMember {localDisplayName, memberProfile = Profile {fullName}} =
|
|
|
|
|
ttyFullName localDisplayName fullName
|
|
|
|
|
|
|
|
|
|
ttyFullName :: ContactName -> Text -> StyledString
|
|
|
|
|
ttyFullName c fullName = ttyContact c <> optFullName c fullName
|
2021-07-16 07:40:55 +01:00
|
|
|
|
2021-07-14 20:11:41 +01:00
|
|
|
ttyToContact :: ContactName -> StyledString
|
2021-07-07 22:46:38 +01:00
|
|
|
ttyToContact c = styled (Colored Cyan) $ "@" <> c <> " "
|
2021-06-25 18:18:24 +01:00
|
|
|
|
2021-07-14 20:11:41 +01:00
|
|
|
ttyFromContact :: ContactName -> StyledString
|
2021-07-04 18:42:24 +01:00
|
|
|
ttyFromContact c = styled (Colored Yellow) $ c <> "> "
|
2021-06-25 18:18:24 +01:00
|
|
|
|
2021-07-14 20:11:41 +01:00
|
|
|
ttyGroup :: GroupName -> StyledString
|
2021-07-12 19:00:03 +01:00
|
|
|
ttyGroup g = styled (Colored Blue) $ "#" <> g
|
2021-06-25 18:18:24 +01:00
|
|
|
|
2022-01-26 16:18:27 +04:00
|
|
|
ttyGroup' :: GroupInfo -> StyledString
|
|
|
|
|
ttyGroup' = ttyGroup . groupName
|
|
|
|
|
|
2022-01-24 16:07:17 +00:00
|
|
|
ttyGroups :: [GroupName] -> StyledString
|
|
|
|
|
ttyGroups [] = ""
|
|
|
|
|
ttyGroups [g] = ttyGroup g
|
|
|
|
|
ttyGroups (g : gs) = ttyGroup g <> ", " <> ttyGroups gs
|
|
|
|
|
|
2022-01-26 16:18:27 +04:00
|
|
|
ttyFullGroup :: GroupInfo -> StyledString
|
|
|
|
|
ttyFullGroup GroupInfo {localDisplayName = g, groupProfile = GroupProfile {fullName}} =
|
|
|
|
|
ttyGroup g <> optFullName g fullName
|
2021-07-16 07:40:55 +01:00
|
|
|
|
2022-01-26 16:18:27 +04:00
|
|
|
ttyFromGroup :: GroupInfo -> ContactName -> StyledString
|
|
|
|
|
ttyFromGroup GroupInfo {localDisplayName = g} c = styled (Colored Yellow) $ "#" <> g <> " " <> c <> "> "
|
2021-07-24 10:26:28 +01:00
|
|
|
|
2022-01-26 16:18:27 +04:00
|
|
|
ttyToGroup :: GroupInfo -> StyledString
|
|
|
|
|
ttyToGroup GroupInfo {localDisplayName = g} = styled (Colored Cyan) $ "#" <> g <> " "
|
2021-07-24 10:26:28 +01:00
|
|
|
|
2021-09-05 14:08:29 +01:00
|
|
|
ttyFilePath :: FilePath -> StyledString
|
|
|
|
|
ttyFilePath = plain
|
|
|
|
|
|
2021-07-24 10:26:28 +01:00
|
|
|
optFullName :: ContactName -> Text -> StyledString
|
2021-07-16 07:40:55 +01:00
|
|
|
optFullName localDisplayName fullName
|
2021-07-24 10:26:28 +01:00
|
|
|
| T.null fullName || localDisplayName == fullName = ""
|
2021-07-16 07:40:55 +01:00
|
|
|
| otherwise = plain (" (" <> fullName <> ")")
|
|
|
|
|
|
|
|
|
|
highlight :: StyledFormat a => a -> StyledString
|
|
|
|
|
highlight = styled (Colored Cyan)
|
|
|
|
|
|
|
|
|
|
highlight' :: String -> StyledString
|
|
|
|
|
highlight' = highlight
|
|
|
|
|
|
2021-06-25 18:18:24 +01:00
|
|
|
styleTime :: String -> StyledString
|
|
|
|
|
styleTime = Styled [SetColor Foreground Vivid Black]
|