mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-14 04:59:56 +00:00
core: allow creating a user without making it active
Adds keepActiveUser to NewUser. When set, the new user is created but the
current active user is preserved.
This is needed to create a profile for an invitation: the prepared chat is
resolved under the active user by APIChangePreparedContactUser, so the profile
that owns the invitation has to stay active until the chat has been moved to
the new one. Creating and activating in one step makes that reassignment
impossible without switching back and forth.
BoolDef gives the field omittedField = False, so clients that do not send it -
iOS, the CLI, and any older caller - keep the current behaviour of activating
the new user.
keepActiveUser is ignored when there is no active user to keep, which would
otherwise leave the app with none at all.
Note the response is still CRActiveUser: it carries the created user, which is
not the active one on this path. Documented at the field.
Regenerates the API docs and the generated TypeScript and Python client types,
which are checked by the Bot API docs tests.
The generator emits BoolDef fields as required in the client types, so the two
hand-maintained clients that build a NewUser literal - simplex-chat-python and
simplex-chat-nodejs - stop type-checking until the field is added there too. No
test covers those; a4e3a1ea1 did the same when clientService was added.
This commit is contained in:
@@ -3054,6 +3054,7 @@ SubscribeError:
|
||||
- pastTimestamp: bool
|
||||
- userChatRelay: bool
|
||||
- clientService: bool
|
||||
- keepActiveUser: bool
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -3348,6 +3348,7 @@ export interface NewUser {
|
||||
pastTimestamp: boolean
|
||||
userChatRelay: boolean
|
||||
clientService: boolean
|
||||
keepActiveUser: boolean
|
||||
}
|
||||
|
||||
export interface NoteFolder {
|
||||
|
||||
@@ -867,7 +867,7 @@ export class ChatApi {
|
||||
* Network usage: no.
|
||||
*/
|
||||
async apiCreateActiveUser(profile?: T.Profile): Promise<T.User> {
|
||||
const r = await this.sendChatCmd(CC.CreateActiveUser.cmdString({newUser: {profile, pastTimestamp: false, userChatRelay: false, clientService: false}}))
|
||||
const r = await this.sendChatCmd(CC.CreateActiveUser.cmdString({newUser: {profile, pastTimestamp: false, userChatRelay: false, clientService: false, keepActiveUser: false}}))
|
||||
if (r.type === "activeUser") return r.user
|
||||
throw new ChatCommandError("unexpected response", r)
|
||||
}
|
||||
|
||||
@@ -642,7 +642,7 @@ class ChatApi:
|
||||
raise
|
||||
|
||||
async def api_create_active_user(self, profile: T.Profile | None = None) -> T.User:
|
||||
new_user: T.NewUser = {"pastTimestamp": False, "userChatRelay": False, "clientService": False}
|
||||
new_user: T.NewUser = {"pastTimestamp": False, "userChatRelay": False, "clientService": False, "keepActiveUser": False}
|
||||
if profile is not None:
|
||||
new_user["profile"] = profile
|
||||
r = await self.send_chat_cmd(CC.CreateActiveUser_cmd_string({"newUser": new_user}))
|
||||
|
||||
@@ -2337,6 +2337,7 @@ class NewUser(TypedDict):
|
||||
pastTimestamp: bool
|
||||
userChatRelay: bool
|
||||
clientService: bool
|
||||
keepActiveUser: bool
|
||||
|
||||
class NoteFolder(TypedDict):
|
||||
noteFolderId: int # int64
|
||||
|
||||
@@ -160,7 +160,7 @@ createActiveUser cc CoreChatOpts {chatRelay, headless} createBot_ userDisplayNam
|
||||
createUser loop False $ mkProfile displayName
|
||||
mkProfile displayName = Profile {displayName, fullName = "", shortDescr = Nothing, description = Nothing, image = Nothing, contactLink = Nothing, peerType = Nothing, preferences = Nothing, badge = Nothing, contactDomain = Nothing}
|
||||
createUser onError clientService p =
|
||||
execChatCommand' (CreateActiveUser NewUser {profile = Just p, pastTimestamp = False, userChatRelay = BoolDef chatRelay, clientService = BoolDef clientService}) 0 `runReaderT` cc >>= \case
|
||||
execChatCommand' (CreateActiveUser NewUser {profile = Just p, pastTimestamp = False, userChatRelay = BoolDef chatRelay, clientService = BoolDef clientService, keepActiveUser = BoolDef False}) 0 `runReaderT` cc >>= \case
|
||||
Right (CRActiveUser user) -> pure user
|
||||
r -> printResponseEvent (Nothing, Nothing) (config cc) r >> onError
|
||||
|
||||
|
||||
@@ -415,7 +415,7 @@ parseChatCommand = A.parseOnly chatCommandP . B.dropWhileEnd isSpace
|
||||
processChatCommand :: StoreCxt -> NetworkRequestMode -> ChatCommand -> CM ChatResponse
|
||||
processChatCommand cxt nm = \case
|
||||
ShowActiveUser -> withUser' $ pure . CRActiveUser
|
||||
CreateActiveUser NewUser {profile, pastTimestamp, userChatRelay, clientService} -> do
|
||||
CreateActiveUser NewUser {profile, pastTimestamp, userChatRelay, clientService, keepActiveUser} -> do
|
||||
forM_ profile $ \p@Profile {displayName, image} -> do
|
||||
checkValidName displayName
|
||||
checkProfileImageSize image
|
||||
@@ -427,17 +427,21 @@ processChatCommand cxt nm = \case
|
||||
when (n == displayName) . throwChatError $
|
||||
if activeUser || isNothing viewPwdHash then CEUserExists displayName else CEInvalidDisplayName {displayName, validName = ""}
|
||||
when (isTrue userChatRelay && isTrue userChatRelay') $ throwChatError CEChatRelayExists
|
||||
(uss, (smp', xftp')) <- chooseServers =<< readTVarIO u
|
||||
curUser_ <- readTVarIO u
|
||||
(uss, (smp', xftp')) <- chooseServers curUser_
|
||||
let service = isTrue clientService
|
||||
-- keepActiveUser can only be honoured when there is a user to keep,
|
||||
-- otherwise it would leave no active user at all.
|
||||
activateNewUser = isNothing curUser_ || not (isTrue keepActiveUser)
|
||||
auId <- withAgent $ \a -> createUser a service smp' xftp'
|
||||
ts <- liftIO $ getCurrentTime >>= if pastTimestamp then coupleDaysAgo else pure
|
||||
user <- withFastStore $ \db -> do
|
||||
user <- createUserRecordAt db (AgentUserId auId) (isTrue userChatRelay) service p True ts
|
||||
user <- createUserRecordAt db (AgentUserId auId) (isTrue userChatRelay) service p activateNewUser ts
|
||||
mapM_ (setUserServers db user ts) uss
|
||||
createPresetContactCards db user `catchAllErrors` \_ -> pure ()
|
||||
createNoteFolder db user
|
||||
pure user
|
||||
atomically . writeTVar u $ Just user
|
||||
when activateNewUser $ atomically . writeTVar u $ Just user
|
||||
pure $ CRActiveUser user
|
||||
where
|
||||
createPresetContactCards :: DB.Connection -> User -> ExceptT StoreError IO ()
|
||||
@@ -5907,7 +5911,7 @@ chatCommandP =
|
||||
(cName, shortDescr) <- profileNameDescr
|
||||
service <- (" service=" *> onOffP) <|> pure False
|
||||
let profile = Just Profile {displayName = cName, fullName = "", shortDescr, description = Nothing, image = Nothing, contactLink = Nothing, peerType = Nothing, preferences = Nothing, badge = Nothing, contactDomain = Nothing}
|
||||
pure NewUser {profile, pastTimestamp = False, userChatRelay = BoolDef relay, clientService = BoolDef service}
|
||||
pure NewUser {profile, pastTimestamp = False, userChatRelay = BoolDef relay, clientService = BoolDef service, keepActiveUser = BoolDef False}
|
||||
newBotUserP = do
|
||||
files_ <- optional $ "files=" *> onOffP <* A.space
|
||||
service <- ("service=" *> onOffP <* A.space) <|> pure False
|
||||
@@ -5916,7 +5920,7 @@ chatCommandP =
|
||||
Just True -> Nothing
|
||||
_ -> Just (emptyChatPrefs :: Preferences) {files = Just FilesPreference {allow = FANo}}
|
||||
profile = Just Profile {displayName = cName, fullName = "", shortDescr, description = Nothing, image = Nothing, contactLink = Nothing, peerType = Just CPTBot, preferences, badge = Nothing, contactDomain = Nothing}
|
||||
pure NewUser {profile, pastTimestamp = False, userChatRelay = BoolDef False, clientService = BoolDef service}
|
||||
pure NewUser {profile, pastTimestamp = False, userChatRelay = BoolDef False, clientService = BoolDef service, keepActiveUser = BoolDef False}
|
||||
jsonP :: J.FromJSON a => Parser a
|
||||
jsonP = J.eitherDecodeStrict' <$?> A.takeByteString
|
||||
groupProfile = do
|
||||
|
||||
@@ -154,7 +154,11 @@ data NewUser = NewUser
|
||||
{ profile :: Maybe Profile,
|
||||
pastTimestamp :: Bool,
|
||||
userChatRelay :: BoolDef,
|
||||
clientService :: BoolDef
|
||||
clientService :: BoolDef,
|
||||
-- when set, the user is created without becoming active, preserving the current
|
||||
-- one; absent/False activates it as before. The response is CRActiveUser either
|
||||
-- way - it carries the created user, which is then not the active one.
|
||||
keepActiveUser :: BoolDef
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user