This commit is contained in:
JRoberts 2023-01-03 15:43:27 +04:00
parent c1ced70836
commit 9f60e5752e
9 changed files with 81 additions and 12 deletions

View File

@ -1611,7 +1611,7 @@ sealed class CC {
val cmdString: String get() = when (this) {
is Console -> cmd
is ShowActiveUser -> "/u"
is CreateActiveUser -> "/u ${profile.displayName} ${profile.fullName}"
is CreateActiveUser -> "/create user ${profile.displayName} ${profile.fullName}"
is StartChat -> "/_start subscribe=on expire=${onOff(expire)}"
is ApiStopChat -> "/_stop"
is SetFilesFolder -> "/_files_folder $filesFolder"

View File

@ -95,7 +95,7 @@ public enum ChatCommand {
get {
switch self {
case .showActiveUser: return "/u"
case let .createActiveUser(profile): return "/u \(profile.displayName) \(profile.fullName)"
case let .createActiveUser(profile): return "/create user \(profile.displayName) \(profile.fullName)"
case let .startChat(subscribe, expire): return "/_start subscribe=\(onOff(subscribe)) expire=\(onOff(expire))"
case .apiStopChat: return "/_stop"
case .apiActivateChat: return "/_app activate"

View File

@ -450,7 +450,7 @@ export function cmdString(cmd: ChatCommand): string {
case "showActiveUser":
return "/u"
case "createActiveUser":
return `/u ${JSON.stringify(cmd.profile)}`
return `/create user ${JSON.stringify(cmd.profile)}`
case "startChat":
return `/_start subscribe=${cmd.subscribeConnections ? "on" : "off"} expire=${cmd.expireChatItems ? "on" : "off"}`
case "apiStopChat":

View File

@ -253,10 +253,27 @@ processChatCommand = \case
ShowActiveUser -> withUser' $ pure . CRActiveUser
CreateActiveUser p -> do
u <- asks currentUser
whenM (isJust <$> readTVarIO u) $ throwChatError CEActiveUserExists
-- whenM (isJust <$> readTVarIO u) $ throwChatError CEActiveUserExists
user <- withStore $ \db -> createUser db p True
atomically . writeTVar u $ Just user
pure $ CRActiveUser user
ListUsers -> do
users <- withStore' $ \db -> getUsers db
pure $ CRUsersList users
APISetActiveUser userId -> do
u <- asks currentUser
user <- withStore $ \db -> getSetActiveUser db userId
atomically . writeTVar u $ Just user
pure CRCmdOk
SetActiveUser uName -> withUserName uName APISetActiveUser
APIDeleteUser _userId -> do
-- check not the only user
-- withStore' $ \db -> deleteUser db userId
-- ? other cleanup
-- set active user to first/arbitrary user?
-- unset if current user
pure CRCmdOk
DeleteUser uName -> withUserName uName APIDeleteUser
StartChat subConns enableExpireCIs -> withUser' $ \user ->
asks agentAsync >>= readTVarIO >>= \case
Just _ -> pure CRChatRunning
@ -1233,6 +1250,8 @@ processChatCommand = \case
withStoreChanged a = checkChatStopped $ a >> setStoreChanged $> CRCmdOk
checkStoreNotChanged :: m ChatResponse -> m ChatResponse
checkStoreNotChanged = ifM (asks chatStoreChanged >>= readTVarIO) (throwChatError CEChatStoreChanged)
withUserName :: UserName -> (UserId -> ChatCommand) -> m ChatResponse
withUserName uName cmd = withStore (`getUserIdByName` uName) >>= processChatCommand . cmd
withContactName :: ContactName -> (ContactId -> ChatCommand) -> m ChatResponse
withContactName cName cmd = withUser $ \user ->
withStore (\db -> getContactIdByName db user cName) >>= processChatCommand . cmd
@ -3538,7 +3557,7 @@ getCreateActiveUser st = do
Right user -> pure user
selectUser :: [User] -> IO User
selectUser [user] = do
withTransaction st (`setActiveUser` userId user)
withTransaction st (`setActiveUser` userId (user :: User))
pure user
selectUser users = do
putStrLn "Select user profile:"
@ -3553,7 +3572,7 @@ getCreateActiveUser st = do
| n <= 0 || n > length users -> putStrLn "invalid user number" >> loop
| otherwise -> do
let user = users !! (n - 1)
withTransaction st (`setActiveUser` userId user)
withTransaction st (`setActiveUser` userId (user :: User))
pure user
userStr :: User -> String
userStr User {localDisplayName, profile = LocalProfile {fullName}} =
@ -3626,7 +3645,12 @@ chatCommandP =
choice
[ "/mute " *> ((`ShowMessages` False) <$> chatNameP'),
"/unmute " *> ((`ShowMessages` True) <$> chatNameP'),
("/user " <|> "/u ") *> (CreateActiveUser <$> userProfile),
"/create user " *> (CreateActiveUser <$> userProfile),
"/users" $> ListUsers,
"/_user " *> (APISetActiveUser <$> A.decimal),
("/user " <|> "/u ") *> (SetActiveUser <$> displayName),
"/_delete user " *> (APIDeleteUser <$> A.decimal),
"/delete user " *> (DeleteUser <$> displayName),
("/user" <|> "/u") $> ShowActiveUser,
"/_start subscribe=" *> (StartChat <$> onOffP <* " expire=" <*> onOffP),
"/_start" $> StartChat True True,

View File

@ -140,6 +140,11 @@ instance ToJSON HelpSection where
data ChatCommand
= ShowActiveUser
| CreateActiveUser Profile
| ListUsers
| APISetActiveUser UserId
| SetActiveUser UserName
| APIDeleteUser UserId
| DeleteUser UserName
| StartChat {subscribeConnections :: Bool, enableExpireChatItems :: Bool}
| APIStopChat
| APIActivateChat
@ -286,6 +291,7 @@ data ChatCommand
data ChatResponse
= CRActiveUser {user :: User}
| CRUsersList {users :: [User]}
| CRChatStarted
| CRChatRunning
| CRChatStopped

View File

@ -28,6 +28,8 @@ module Simplex.Chat.Store
createUser,
getUsers,
setActiveUser,
getSetActiveUser,
getUserIdByName,
createDirectConnection,
createConnReqConnection,
getProfileById,
@ -460,6 +462,30 @@ setActiveUser db userId = do
DB.execute_ db "UPDATE users SET active_user = 0"
DB.execute db "UPDATE users SET active_user = 1 WHERE user_id = ?" (Only userId)
getSetActiveUser :: DB.Connection -> UserId -> ExceptT StoreError IO User
getSetActiveUser db userId = do
liftIO $ setActiveUser db userId
getUser_ db userId
getUser_ :: DB.Connection -> UserId -> ExceptT StoreError IO User
getUser_ db userId =
ExceptT . firstRow toUser (SEUserNotFound userId) $
DB.query
db
[sql|
SELECT u.user_id, u.contact_id, p.contact_profile_id, u.active_user, u.local_display_name, p.full_name, p.image, p.preferences
FROM users u
JOIN contacts c ON u.contact_id = c.contact_id
JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id
WHERE u.user_id = ?
|]
(Only userId)
getUserIdByName :: DB.Connection -> UserName -> ExceptT StoreError IO Int64
getUserIdByName db uName =
ExceptT . firstRow fromOnly (SEUserNotFoundByName uName) $
DB.query db "SELECT user_id FROM users WHERE local_display_name = ?" (Only uName)
createConnReqConnection :: DB.Connection -> UserId -> ConnId -> ConnReqUriHash -> XContactId -> Maybe Profile -> Maybe GroupLinkId -> IO PendingContactConnection
createConnReqConnection db userId acId cReqHash xContactId incognitoProfile groupLinkId = do
createdAt <- getCurrentTime
@ -4803,7 +4829,9 @@ randomBytes gVar = atomically . stateTVar gVar . randomBytesGenerate
-- These error type constructors must be added to mobile apps
data StoreError
= SEDuplicateName
| SEContactNotFound {contactId :: Int64}
| SEUserNotFound {userId :: UserId}
| SEUserNotFoundByName {contactName :: ContactName}
| SEContactNotFound {contactId :: ContactId}
| SEContactNotFoundByName {contactName :: ContactName}
| SEContactNotReady {contactName :: ContactName}
| SEDuplicateContactLink

View File

@ -216,6 +216,8 @@ instance ToJSON ConnReqUriHash where
data ContactOrRequest = CORContact Contact | CORRequest UserContactRequest
type UserName = Text
type ContactName = Text
type GroupName = Text

View File

@ -59,6 +59,7 @@ serializeChatResponse user_ ts = unlines . map unStyle . responseToView user_ Fa
responseToView :: Maybe User -> Bool -> Bool -> CurrentTime -> ChatResponse -> [StyledString]
responseToView user_ testView liveItems ts = \case
CRActiveUser User {profile} -> viewUserProfile $ fromLocalProfile profile
CRUsersList users -> viewUsersList users
CRChatStarted -> ["chat started"]
CRChatRunning -> ["chat is running"]
CRChatStopped -> ["chat stopped"]
@ -256,6 +257,13 @@ responseToView user_ testView liveItems ts = \case
| muted chat chatItem = []
| otherwise = s
viewUsersList :: [User] -> [StyledString]
viewUsersList =
let ldn = T.toLower . (localDisplayName :: User -> ContactName)
in map (\user@User {profile = LocalProfile {displayName, fullName}} -> ttyFullName displayName fullName <> active user) . sortOn ldn
where
active User {activeUser} = if activeUser then highlight' " (active)" else ""
muted :: ChatInfo c -> ChatItem c d -> Bool
muted chat ChatItem {chatDir} = case (chat, chatDir) of
(DirectChat Contact {chatSettings = DisableNtfs}, CIDirectRcv) -> True
@ -1179,6 +1187,7 @@ viewChatError = \case
-- e -> ["chat error: " <> sShow e]
ChatErrorStore err -> case err of
SEDuplicateName -> ["this display name is already used by user, contact or group"]
SEUserNotFoundByName u -> ["no user " <> ttyContact u]
SEContactNotFoundByName c -> ["no contact " <> ttyContact c]
SEContactNotReady c -> ["contact " <> ttyContact c <> " is not active yet"]
SEGroupNotFoundByName g -> ["no group " <> ttyGroup g]

View File

@ -25,9 +25,9 @@ noActiveUser = "{\"resp\":{\"type\":\"chatCmdError\",\"chatError\":{\"type\":\"e
activeUserExists :: String
#if defined(darwin_HOST_OS) && defined(swiftJSON)
activeUserExists = "{\"resp\":{\"chatCmdError\":{\"chatError\":{\"error\":{\"errorType\":{\"activeUserExists\":{}}}}}}}"
activeUserExists = "{\"resp\":{\"chatCmdError\":{\"chatError\":{\"errorStore\":{\"storeError\":{\"duplicateName\":{}}}}}}}"
#else
activeUserExists = "{\"resp\":{\"type\":\"chatCmdError\",\"chatError\":{\"type\":\"error\",\"errorType\":{\"type\":\"activeUserExists\"}}}}"
activeUserExists = "{\"resp\":{\"type\":\"chatCmdError\",\"chatError\":{\"type\":\"errorStore\",\"storeError\":{\"type\":\"duplicateName\"}}}}"
#endif
activeUser :: String
@ -85,7 +85,7 @@ testChatApiNoUser = withTmpFiles $ do
Left (DBMErrorNotADatabase _) <- chatMigrateInit testDBPrefix "myKey"
chatSendCmd cc "/u" `shouldReturn` noActiveUser
chatSendCmd cc "/_start" `shouldReturn` noActiveUser
chatSendCmd cc "/u alice Alice" `shouldReturn` activeUser
chatSendCmd cc "/create user alice Alice" `shouldReturn` activeUser
chatSendCmd cc "/_start" `shouldReturn` chatStarted
testChatApi :: IO ()
@ -98,7 +98,7 @@ testChatApi = withTmpFiles $ do
Left (DBMErrorNotADatabase _) <- chatMigrateInit dbPrefix ""
Left (DBMErrorNotADatabase _) <- chatMigrateInit dbPrefix "anotherKey"
chatSendCmd cc "/u" `shouldReturn` activeUser
chatSendCmd cc "/u alice Alice" `shouldReturn` activeUserExists
chatSendCmd cc "/create user alice Alice" `shouldReturn` activeUserExists
chatSendCmd cc "/_start" `shouldReturn` chatStarted
chatRecvMsg cc `shouldReturn` contactSubSummary
chatRecvMsg cc `shouldReturn` userContactSubSummary