From 96176936e6dab471c4b9cab6d855d9e07422d193 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 18 Dec 2021 10:23:47 +0000 Subject: [PATCH] update welcome messages (#156) * simple welcome message * show welcome message only once * show onboarding progress * admin and groups * show full group names with /gs command * Update src/Simplex/Chat/Help.hs Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com> * Update src/Simplex/Chat/Help.hs Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com> --- src/Simplex/Chat.hs | 23 ++++++++++++---- src/Simplex/Chat/Help.hs | 55 +++++++++++++++++++++++---------------- src/Simplex/Chat/Store.hs | 23 ++++++++++------ src/Simplex/Chat/View.hs | 8 +++--- 4 files changed, 70 insertions(+), 39 deletions(-) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 216c1130e..7eddcf7ad 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -73,6 +73,8 @@ data ChatCommand | Welcome | AddContact | Connect AConnectionRequest + | ConnectAdmin + | SendAdminWelcome ContactName | DeleteContact ContactName | ListContacts | CreateMyAddress @@ -181,6 +183,7 @@ inputSubscriber = do SendGroupMessage g msg -> showSentGroupMessage g msg SendFile c f -> showSentFileInvitation c f SendGroupFile g f -> showSentGroupFileInvitation g f + SendAdminWelcome c -> forM_ adminWelcomeMessages $ showSentMessage c _ -> printToView [plain s] user <- readTVarIO =<< asks currentUser withAgentLock a . withLock l . void . runExceptT $ @@ -202,6 +205,8 @@ processChatCommand user@User {userId, profile} = \case showInvitation cReq Connect (ACR SCMInvitation cReq) -> connect cReq (XInfo profile) >> showSentConfirmation Connect (ACR SCMContact cReq) -> connect cReq (XContact profile Nothing) >> showSentInvitation + ConnectAdmin -> connect adminContactReq (XContact profile Nothing) >> showSentInvitation + SendAdminWelcome cName -> forM_ adminWelcomeMessages $ sendMessageCmd cName DeleteContact cName -> withStore (\st -> getContactGroupNames st userId cName) >>= \case [] -> do @@ -238,11 +243,7 @@ processChatCommand user@User {userId, profile} = \case `E.finally` deleteContactRequest st userId cName withAgent $ \a -> rejectContact a agentContactConnId agentInvitationId showContactRequestRejected cName - SendMessage cName msg -> do - contact <- withStore $ \st -> getContact st userId cName - let msgEvent = XMsgNew $ MsgContent MTText [] [MsgContentBody {contentType = SimplexContentType XCText, contentData = msg}] - sendDirectMessage (contactConnId contact) msgEvent - setActive $ ActiveC cName + SendMessage cName msg -> sendMessageCmd cName msg NewGroup gProfile -> do gVar <- asks idsDrg group <- withStore $ \st -> createNewGroup st gVar user gProfile @@ -369,6 +370,12 @@ processChatCommand user@User {userId, profile} = \case connect cReq msg = do connId <- withAgent $ \a -> joinConnection a cReq $ directMessage msg withStore $ \st -> createDirectConnection st userId connId + sendMessageCmd :: ContactName -> ByteString -> m () + sendMessageCmd cName msg = do + contact <- withStore $ \st -> getContact st userId cName + let msgEvent = XMsgNew $ MsgContent MTText [] [MsgContentBody {contentType = SimplexContentType XCText, contentData = msg}] + sendDirectMessage (contactConnId contact) msgEvent + setActive $ ActiveC cName contactMember :: Contact -> [GroupMember] -> Maybe GroupMember contactMember Contact {contactId} = find $ \GroupMember {memberContactId = cId, memberStatus = s} -> @@ -1189,6 +1196,8 @@ chatCommandP = <|> ("/freceive " <|> "/fr ") *> (ReceiveFile <$> A.decimal <*> optional (A.space *> filePath)) <|> ("/fcancel " <|> "/fc ") *> (CancelFile <$> A.decimal) <|> ("/fstatus " <|> "/fs ") *> (FileStatus <$> A.decimal) + <|> "/admin_welcome " *> (SendAdminWelcome <$> displayName) + <|> "/admin" $> ConnectAdmin <|> ("/address" <|> "/ad") $> CreateMyAddress <|> ("/delete_address" <|> "/da") $> DeleteMyAddress <|> ("/show_address" <|> "/sa") $> ShowMyAddress @@ -1220,3 +1229,7 @@ chatCommandP = <|> (" admin" $> GRAdmin) <|> (" member" $> GRMember) <|> pure GRAdmin + +adminContactReq :: ConnectionRequest 'CMContact +adminContactReq = + either error id $ parseAll connReqP' "https://simplex.chat/contact#/?smp=smp%3A%2F%2Fnxc7HnrnM8dOKgkMp008ub_9o9LXJlxlMrMpR-mfMQw%3D%40smp3.simplex.im%2F-TXnePw5eH5-4L7B%23&e2e=rsa%3AMIIBoDANBgkqhkiG9w0BAQEFAAOCAY0AMIIBiAKCAQEA6vpcsZggnYL38Qa2G5YU0W5uqnV8WAq_S3flIFU2kx4qW-aokVT8fo0CLJXv9aagdHObFfhc9SXcZPcm4T2NLnafKTgQa_HYFfj764l6cHkbSI-4JBE1gyhtaapsvrDGIdoiGDLgsF3AJVjqs8gavkuTsmw035aWMH-pkpc4qGlEWpNWp1Nn-7O4sdIIQ7yN48jsdCfeIY-BIk3kFR6s4oQOgiOcnir8e3x5tTuRMX1KWSiuzuqLHqgmcI1IqcPJPrBoTQLbXXEMGG1RsvIudxR03jejXXbQvlxXlNNrxwkniEe-P0rApGuCyv2NRMb4n0Wd3ZwewH7X-xtr16XNbQKBgDouGUHD1C55jB-w8W8VJRhFZS2xIYka9gJH1jjCFxHFzgjo69A_sObIamND1pF_JOzj_XCoA1fDICF95XbfS0rq9iS6xvX6M8Muq8QiJsfD5bRt5nh-Y3GK5rAFXS0ZtyOeh07iMLAMJ_EFxBQuKKDRu9_9KAvLL_plU0PuaMH3" diff --git a/src/Simplex/Chat/Help.hs b/src/Simplex/Chat/Help.hs index dad637985..e4711845c 100644 --- a/src/Simplex/Chat/Help.hs +++ b/src/Simplex/Chat/Help.hs @@ -3,6 +3,7 @@ module Simplex.Chat.Help ( chatWelcome, + adminWelcomeMessages, chatHelpInfo, filesHelpInfo, groupsHelpInfo, @@ -11,6 +12,7 @@ module Simplex.Chat.Help ) where +import Data.ByteString (ByteString) import Data.List (intersperse) import Data.Text (Text) import qualified Data.Text as T @@ -22,15 +24,6 @@ import System.Console.ANSI.Types highlight :: Text -> Markdown highlight = Markdown (Colored Cyan) -blue :: Text -> Markdown -blue = Markdown (Colored Blue) - -cyan :: Text -> Markdown -cyan = Markdown (Colored Cyan) - -yellow :: Text -> Markdown -yellow = Markdown (Colored Yellow) - green :: Text -> Markdown green = Markdown (Colored Green) @@ -44,32 +37,47 @@ chatWelcome :: User -> Onboarding -> [StyledString] chatWelcome user Onboarding {contactsCount, createdGroups, membersCount, filesSentCount, addressCount} = map styleMarkdown - [ blue " __ __", - cyan " ___ ___ __ __ ___ _ ___" <> blue "\\ \\ / /" <> yellow " ___ _ _ _ _____", - cyan " / __|_ _| \\/ | _ \\ | | __ " <> blue "\\ V /" <> yellow " / __| || | /_\\_ _|", - cyan " \\__ \\| || |\\/| | _/ |__| _|" <> blue " / . \\" <> yellow "| (__| __ |/ _ \\| |", - cyan " |___/___|_| |_|_| |____|___" <> blue "/_/ \\_\\" <> yellow "\\___|_||_/_/ \\_\\_|", + [ " __ __", + " ___ ___ __ __ ___ _ ___" <> "\\ \\ / /" <> " ___ _ _ _ _____", + " / __|_ _| \\/ | _ \\ | | __ " <> "\\ V /" <> " / __| || | /_\\_ _|", + " \\__ \\| || |\\/| | _/ |__| _|" <> " / . \\" <> "| (__| __ |/ _ \\| |", + " |___/___|_| |_|_| |____|___" <> "/_/ \\_\\" <> "\\___|_||_/_/ \\_\\_|", "", "Welcome " <> green userName <> "!", "Thank you for installing SimpleX Chat!", "", - "To try out how it works:", - "[" <> check (contactsCount >= 2) <> "] connect with 2 friends - " <> highlight "/help" <> " for instructions", - "[" <> check (createdGroups >= 1 && membersCount >= 2) <> "] create a group with them - " <> highlight "/group #friends", - "[" <> check (filesSentCount >= 1) <> "] send your photo, e.g. to the group - " <> highlight "/file #friends ./photo.jpg", - "[" <> check (addressCount >= 1) <> "] create your chat " <> highlight "/address" <> " and share it with your friends", + "We have created several groups that you can join to play with SimpleX Chat:", + highlight "#simplex" <> " (SimpleX Engineers 💻) - technical questions about running or contributing to SimpleX Chat", + highlight "#hacks" <> " (Ethical Hacking 🔓) - chatting about privacy, security, announced vulnerabilities etc.", + highlight "#music" <> " (Music 🎸) - favorite music of our team and users", + highlight "#rand" <> " (Random 😇) - anything interesting, just keep it decent and friendly please :)", + "", + "Connect to our groups admin to be added to these groups - " <> highlight "/admin", + "", + "To continue:", + "[" <> check (contactsCount >= 2) <> "] " <> highlight "/connect" <> " with 2 friends - " <> highlight "/help" <> " for instructions", + "[" <> check (createdGroups >= 1 && membersCount >= 2) <> "] create a " <> highlight "/group" <> " with them - " <> highlight "/g #friends", + "[" <> check (filesSentCount >= 1) <> "] send " <> highlight "/file" <> ", e.g. your photo, to the group - " <> highlight "/f #friends ./photo.jpg", + "[" <> check (addressCount >= 1) <> "] create your optional chat " <> highlight "/address" <> " and share it with your friends - " <> highlight "/ad", "", "To help us build SimpleX Chat:", "> star GitHub repo: https://github.com/simplex-chat/simplex-chat", "> join Reddit group: https://www.reddit.com/r/SimpleXChat/", "", - "To show this message again - " <> highlight "/welcome" + "To show this message again - " <> highlight "/welcome" <> " (or " <> highlight "/w" <> ")" ] where User {profile = Profile {displayName, fullName}} = user userName = if T.null fullName then displayName else fullName check c = if c then green "*" else " " +adminWelcomeMessages :: [ByteString] +adminWelcomeMessages = + [ "Hello - and welcome to SimpleX Chat!", + "Which community groups you'd you like to join:", + "!5 #simplex!, !5 #hacks!, !5 #music! or !5 #rand!" + ] + chatHelpInfo :: [StyledString] chatHelpInfo = map @@ -89,13 +97,14 @@ chatHelpInfo = indent <> highlight "@bob Hello, Bob!" <> " - Alice messages Bob (assuming Bob has display name 'bob').", indent <> highlight "@alice Hey, Alice!" <> " - Bob replies to Alice.", "", - green "Send file: " <> highlight "/file bob ./photo.jpg" <> " (see /help files)", + green "Send file: " <> highlight "/file bob ./photo.jpg", "", - green "Create group: " <> highlight "/group team" <> " (see /help groups)", + green "Create group: " <> highlight "/group team", "", - green "Create your address: " <> highlight "/address" <> " (see /help address)", + green "Create your address: " <> highlight "/address", "", green "Other commands:", + indent <> highlight "/help " <> " - help on: files, groups, address", indent <> highlight "/profile " <> " - show / update user profile", indent <> highlight "/delete " <> " - delete contact and all messages with them", indent <> highlight "/contacts " <> " - list contacts", diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index e2193fa4d..07ca7c8c9 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -973,16 +973,23 @@ deleteGroup st User {userId} Group {groupId, members, localDisplayName} = DB.execute db "DELETE FROM display_names WHERE user_id = ? AND local_display_name = ?" (userId, localDisplayName) getUserGroups :: MonadUnliftIO m => SQLiteStore -> User -> m [Group] -getUserGroups st user = +getUserGroups st user@User {userId} = liftIO . withTransaction st $ \db -> do - groupNames <- getUserGroupNames_ db $ userId user + groupNames <- map fromOnly <$> DB.query db "SELECT local_display_name FROM groups WHERE user_id = ?" (Only userId) map fst . rights <$> mapM (runExceptT . getGroup_ db user) groupNames -getUserGroupNames :: MonadUnliftIO m => SQLiteStore -> UserId -> m [GroupName] -getUserGroupNames st userId = liftIO $ withTransaction st (`getUserGroupNames_` userId) - -getUserGroupNames_ :: DB.Connection -> UserId -> IO [GroupName] -getUserGroupNames_ db userId = map fromOnly <$> DB.query db "SELECT local_display_name FROM groups WHERE user_id = ?" (Only userId) +getUserGroupNames :: MonadUnliftIO m => SQLiteStore -> UserId -> m [(GroupName, Text)] +getUserGroupNames st userId = + liftIO . withTransaction st $ \db -> + DB.query + db + [sql| + SELECT g.local_display_name, p.full_name + FROM groups g + JOIN group_profiles p USING (group_profile_id) + WHERE g.user_id = ? + |] + (Only userId) getGroupInvitation :: StoreMonad m => SQLiteStore -> User -> GroupName -> m ReceivedGroupInvitation getGroupInvitation st user localDisplayName = @@ -1594,7 +1601,7 @@ getSndFileTransfers_ db userId fileId = getOnboarding :: MonadUnliftIO m => SQLiteStore -> UserId -> m Onboarding getOnboarding st userId = liftIO . withTransaction st $ \db -> do - contactsCount <- intQuery db "SELECT COUNT(contact_id) FROM contacts WHERE user_id = ?" + contactsCount <- intQuery db "SELECT COUNT(contact_id) FROM contacts WHERE user_id = ? AND is_user = 0" createdGroups <- headOrZero <$> DB.query db "SELECT COUNT(g.group_id) FROM groups g JOIN group_members m WHERE g.user_id = ? AND m.member_status = ?" (userId, GSMemCreator) membersCount <- headOrZero <$> DB.query db "SELECT COUNT(group_member_id) FROM group_members WHERE user_id = ? AND (member_status = ? OR member_status = ?)" (userId, GSMemConnected, GSMemComplete) filesSentCount <- intQuery db "SELECT COUNT(s.file_id) FROM snd_files s JOIN files f USING (file_id) WHERE f.user_id = ?" diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 44e8bc9a9..80d5a1655 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -287,7 +287,7 @@ showLeftMember = printToView .: leftMember showGroupMembers :: ChatReader m => Group -> m () showGroupMembers = printToView . groupMembers -showGroupsList :: ChatReader m => [GroupName] -> m () +showGroupsList :: ChatReader m => [(GroupName, Text)] -> m () showGroupsList = printToView . groupsList showContactsMerged :: ChatReader m => Contact -> Contact -> m () @@ -482,9 +482,11 @@ groupMembers Group {membership, members} = map groupMember . filter (not . remov GSMemCreator -> "created group" _ -> "" -groupsList :: [GroupName] -> [StyledString] +groupsList :: [(GroupName, Text)] -> [StyledString] groupsList [] = ["you have no groups!", "to create: " <> highlight' "/g "] -groupsList gs = map ttyGroup $ sort gs +groupsList gs = map groupNames $ sort gs + where + groupNames (displayName, fullName) = ttyGroup displayName <> optFullName displayName fullName contactsMerged :: Contact -> Contact -> [StyledString] contactsMerged _to@Contact {localDisplayName = c1} _from@Contact {localDisplayName = c2} =