mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-11-04 19:27:55 +00:00
core, ui: terminal chat layout for LLM and service bot chats (#7811)
* plan: UI improvements to improve LLM usage via SimpleX * core, ui: types for chat layout * core, ui: settings for alternative terminal-like chat layout for managing LLMs or other service bots * plan: message priority * fixes * query plan * update plans * remove silent messages plan * rename migrations * implement layout * amend * fix * fix * fix * improve * time on the right, sender name in blue * no avatar in direct chats * better previews * update, fixes * update * time gap * changes * ui: terminal layout line heights, gaps and time placement * ui: simplify terminal meta layout, correct its docs and tests * ui: right-to-left emoji edge in terminal rows, time placement tests * android, desktop: raise Kotlin compiler memory to 4 GB * ui: terminal captions as follow-up text, classic event separators * ui: terminal media edge and overlay time, plan decisions * ui: simplify terminal row separation, correct doc line references * ui: keep the media time inside terminal rows, restore caption conditions * ui: terminal heading capitals at large font scales, idle live message time * ui: rename the classic voice-with-text flag, correct the meta copy docs * ui: smaller terminal layout diff, sender gap and meta names * ui: terminal row as tall as the media time * docs: remove the chat layout section from the bots readme * docs: shorten the terminal layout section of the chat view * core: address settings API as on master, /auto_accept reads the stored layout * ui: smaller terminal headings * ui: classic sender rows and avatar rule as on master * ui: classic reply quote text colour as on master * ios: keep the chat layout setting with other UI settings * ui: go-to button at the end of terminal rows, no gutter in direct chats * ui: forwarded arrow on the header line of terminal rows, above the time * ui: forwarded arrow centred above the time line of terminal rows --------- Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
This commit is contained in:
1 parent
4f81c6f632
commit
7810fc4752
103 files changed
+5098
-901
No files matched your search
@@ -58,7 +58,8 @@ data AppSettings = AppSettings
|
||||
uiCurrentThemeIds :: Maybe (Map ThemeColorScheme Text),
|
||||
uiThemes :: Maybe [UITheme],
|
||||
oneHandUI :: Maybe Bool,
|
||||
chatBottomBar :: Maybe Bool
|
||||
chatBottomBar :: Maybe Bool,
|
||||
uiChatLayout :: Maybe ChatLayout
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
@@ -109,7 +110,8 @@ defaultAppSettings =
|
||||
uiCurrentThemeIds = Nothing,
|
||||
uiThemes = Nothing,
|
||||
oneHandUI = Just True,
|
||||
chatBottomBar = Just True
|
||||
chatBottomBar = Just True,
|
||||
uiChatLayout = Just CLClassic
|
||||
}
|
||||
|
||||
defaultParseAppSettings :: AppSettings
|
||||
@@ -147,7 +149,8 @@ defaultParseAppSettings =
|
||||
uiCurrentThemeIds = Nothing,
|
||||
uiThemes = Nothing,
|
||||
oneHandUI = Nothing,
|
||||
chatBottomBar = Nothing
|
||||
chatBottomBar = Nothing,
|
||||
uiChatLayout = Nothing
|
||||
}
|
||||
|
||||
combineAppSettings :: AppSettings -> AppSettings -> AppSettings
|
||||
@@ -185,7 +188,8 @@ combineAppSettings platformDefaults storedSettings =
|
||||
uiCurrentThemeIds = p uiCurrentThemeIds,
|
||||
uiThemes = p uiThemes,
|
||||
oneHandUI = p oneHandUI,
|
||||
chatBottomBar = p chatBottomBar
|
||||
chatBottomBar = p chatBottomBar,
|
||||
uiChatLayout = p uiChatLayout
|
||||
}
|
||||
where
|
||||
p :: (AppSettings -> Maybe a) -> Maybe a
|
||||
@@ -240,6 +244,7 @@ instance FromJSON AppSettings where
|
||||
uiThemes <- p "uiThemes"
|
||||
oneHandUI <- p "oneHandUI"
|
||||
chatBottomBar <- p "chatBottomBar"
|
||||
uiChatLayout <- p "uiChatLayout"
|
||||
pure
|
||||
AppSettings
|
||||
{ appPlatform,
|
||||
@@ -274,7 +279,8 @@ instance FromJSON AppSettings where
|
||||
uiCurrentThemeIds,
|
||||
uiThemes,
|
||||
oneHandUI,
|
||||
chatBottomBar
|
||||
chatBottomBar,
|
||||
uiChatLayout
|
||||
}
|
||||
where
|
||||
p key = v .:? key <|> pure Nothing
|
||||
|
||||
@@ -67,8 +67,8 @@ initializeBotAddress' logAddress pqRatchet_ doAutoAccept cc = do
|
||||
putStrLn $ "Bot's contact address is: " <> B.unpack (maybe (strEncode uri) strEncode shortUri)
|
||||
when (isJust shortUri) $ putStrLn $ "Full contact address for old clients: " <> B.unpack (strEncode uri)
|
||||
let aa = if doAutoAccept then Just AutoAccept {acceptIncognito = False} else Nothing
|
||||
settings = AddressSettings {businessAddress = False, autoAccept = aa, autoReply = Nothing}
|
||||
void $ sendChatCmd cc $ SetAddressSettings Nothing settings
|
||||
settings = AddressSettings {businessAddress = False, autoAccept = aa, chatLayout = Nothing, autoReply = Nothing}
|
||||
void $ sendChatCmd cc $ SetAddressSettings Nothing settings Nothing
|
||||
|
||||
sendMessage :: ChatController -> Contact -> Text -> IO ()
|
||||
sendMessage cc ct = sendComposedMessage cc ct Nothing . MCText
|
||||
|
||||
@@ -593,7 +593,7 @@ data ChatCommand
|
||||
| APISetProfileAddress {userId :: UserId, enable :: Bool}
|
||||
| SetProfileAddress Bool
|
||||
| APISetAddressSettings {userId :: UserId, pqRatchet :: Maybe Bool, settings :: AddressSettings}
|
||||
| SetAddressSettings {pqRatchet :: Maybe Bool, settings :: AddressSettings}
|
||||
| SetAddressSettings {pqRatchet :: Maybe Bool, settings :: AddressSettings, chatLayout :: Maybe (Maybe ChatLayout)}
|
||||
| AcceptContact IncognitoEnabled ContactName
|
||||
| RejectContact ContactName Bool
|
||||
| ForwardMessage {toChatName :: ChatName, fromContactName :: ContactName, forwardedMsg :: Text}
|
||||
@@ -679,6 +679,7 @@ data ChatCommand
|
||||
| SetGroupFeature AGroupFeatureNoRole GroupName GroupFeatureEnabled
|
||||
| SetGroupFeatureRole AGroupFeatureRole GroupName GroupFeatureEnabled (Maybe GroupMemberRole)
|
||||
| SetGroupMemberAdmissionReview GroupName (Maybe MemberCriteria)
|
||||
| SetGroupChatLayout GroupName (Maybe ChatLayout)
|
||||
| SetUserTimedMessages Bool -- UserId (not used in UI)
|
||||
| SetContactTimedMessages ContactName (Maybe TimedMessagesEnabled)
|
||||
| SetGroupTimedMessages GroupName (Maybe Int)
|
||||
|
||||
@@ -100,6 +100,7 @@ import Simplex.Chat.Store.Shared
|
||||
import Simplex.Chat.Types
|
||||
import Simplex.Chat.Types.Preferences
|
||||
import Simplex.Chat.Types.Shared
|
||||
import Simplex.Chat.Types.UITheme (ChatLayout (..))
|
||||
import Simplex.Chat.Util (liftIOEither, zipWith3')
|
||||
import qualified Simplex.Chat.Util as U
|
||||
import Simplex.Chat.Web (webPreviewWorker)
|
||||
@@ -2196,14 +2197,14 @@ processChatCommand cxt nm = \case
|
||||
pure $ CRConnectionPlan user ccLink planSimplexName otherSimplexName plan
|
||||
APIConnectPlan _ Nothing _ _ -> throwChatError CEInvalidConnReq
|
||||
APIPrepareContact userId accLink verifiedDomain contactSLinkData -> withUserId userId $ \user -> do
|
||||
let ContactShortLinkData {profile, message, business} = contactSLinkData
|
||||
let ContactShortLinkData {profile, message, business, chatLayout} = contactSLinkData
|
||||
welcomeSharedMsgId <- forM message $ \_ -> getSharedMsgId
|
||||
case accLink of
|
||||
ACCL SCMContact ccLink
|
||||
| business -> do
|
||||
let Profile {preferences} = profile
|
||||
groupPreferences = maybe defaultBusinessGroupPrefs businessGroupPrefs preferences
|
||||
groupProfile = businessGroupProfile profile groupPreferences
|
||||
groupProfile = businessGroupProfile profile groupPreferences chatLayout
|
||||
gVar <- asks random
|
||||
(gInfo, hostMember_) <- withStore $ \db -> createPreparedGroup db gVar cxt user groupProfile True ccLink welcomeSharedMsgId False GRMember Nothing verifiedDomain
|
||||
hostMember <- maybe (throwCmdError "no host member") pure hostMember_
|
||||
@@ -2217,8 +2218,9 @@ processChatCommand cxt nm = \case
|
||||
Just (AChatItem SCTGroup dir _ ci) -> Chat cInfo [CChatItem dir ci] emptyChatStats {unreadCount = 1, minUnreadItemId = chatItemId' ci}
|
||||
_ -> Chat cInfo [] emptyChatStats
|
||||
pure $ CRNewPreparedChat user $ AChat SCTGroup chat
|
||||
ACCL _ (CCLink cReq sLnk) -> do
|
||||
ct <- withStore $ \db -> createPreparedContact db cxt user (linkPresHeader <$> sLnk) profile accLink welcomeSharedMsgId (True <$ verifiedDomain)
|
||||
ACCL m (CCLink cReq sLnk) -> do
|
||||
let ctLayout = case m of SCMContact -> chatLayout; SCMInvitation -> Nothing
|
||||
ct <- withStore $ \db -> createPreparedContact db cxt user (linkPresHeader <$> sLnk) profile accLink welcomeSharedMsgId ctLayout (True <$ verifiedDomain)
|
||||
void $ createChatItem user (CDDirectSnd ct) False CIChatBanner Nothing Nothing (Just epochStart)
|
||||
let cd = CDDirectRcv ct
|
||||
createItem sharedMsgId content = createChatItem user cd False content sharedMsgId Nothing Nothing
|
||||
@@ -2540,8 +2542,9 @@ processChatCommand cxt nm = \case
|
||||
ucl'' <- if shortLinkDataSet then setMyAddressData False pqInitKeys user ucl' else pure ucl'
|
||||
withFastStore' $ \db -> updateUserAddressSettings db userContactLinkId settings
|
||||
pure $ CRUserContactLinkUpdated user ucl''
|
||||
SetAddressSettings pqRatchet_ settings -> withUser $ \User {userId} ->
|
||||
processChatCommand cxt nm $ APISetAddressSettings userId pqRatchet_ settings
|
||||
SetAddressSettings pqRatchet_ settings chatLayout_ -> withUser $ \user@User {userId} -> do
|
||||
UserContactLink {addressSettings = AddressSettings {chatLayout}} <- withFastStore (`getUserAddress` user)
|
||||
processChatCommand cxt nm $ APISetAddressSettings userId pqRatchet_ (settings :: AddressSettings) {chatLayout = fromMaybe chatLayout chatLayout_}
|
||||
AcceptContact incognito cName -> withUser $ \User {userId} -> do
|
||||
connReqId <- withFastStore $ \db -> getContactRequestIdByName db userId cName
|
||||
processChatCommand cxt nm $ APIAcceptContact incognito connReqId
|
||||
@@ -3676,6 +3679,8 @@ processChatCommand cxt nm = \case
|
||||
case memberAdmission of
|
||||
Nothing -> p {memberAdmission = Just (emptyGroupMemberAdmission :: GroupMemberAdmission) {review = reviewAdmissionApplication}}
|
||||
Just ma -> p {memberAdmission = Just (ma :: GroupMemberAdmission) {review = reviewAdmissionApplication}}
|
||||
SetGroupChatLayout gName chatLayout ->
|
||||
updateGroupProfileByName gName $ \p -> p {chatLayout}
|
||||
SetUserTimedMessages onOff -> withUserProfileLock "updateProfile" $ withUser $ \user@User {profile} -> do
|
||||
let allowed = if onOff then FAYes else FANo
|
||||
pref = TimedMessagesPreference allowed Nothing
|
||||
@@ -4836,7 +4841,8 @@ processChatCommand cxt nm = \case
|
||||
contactShortLinkData p settings =
|
||||
let msg = autoReply =<< settings
|
||||
business = maybe False businessAddress settings
|
||||
contactData = ContactShortLinkData p msg business Nothing
|
||||
layout = settings >>= \AddressSettings {chatLayout} -> chatLayout
|
||||
contactData = ContactShortLinkData p msg business layout Nothing
|
||||
in encodeShortLinkData contactData
|
||||
relayShortLinkData :: Profile -> UserLinkData
|
||||
relayShortLinkData Profile {displayName, fullName, shortDescr, image} =
|
||||
@@ -6617,7 +6623,7 @@ chatCommandP =
|
||||
"/_profile_address " *> (APISetProfileAddress <$> A.decimal <* A.space <*> onOffP),
|
||||
("/profile_address " <|> "/pa ") *> (SetProfileAddress <$> onOffP),
|
||||
"/_address_settings " *> (APISetAddressSettings <$> A.decimal <*> optional (" pq_ratchet=" *> onOffP) <* A.space <*> jsonP),
|
||||
"/auto_accept" *> (SetAddressSettings <$> optional (" pq_ratchet=" *> onOffP) <* A.space <*> autoAcceptP),
|
||||
"/auto_accept" *> (uncurry . SetAddressSettings <$> optional (" pq_ratchet=" *> onOffP) <* A.space <*> autoAcceptP),
|
||||
("/accept" <|> "/ac") *> (AcceptContact <$> incognitoP <* A.space <* char_ '@' <*> displayNameP),
|
||||
("/reject " <|> "/rc ") *> char_ '@' *> (RejectContact <$> displayNameP <*> (" notify" $> True <|> pure False)),
|
||||
("/markdown" <|> "/m") $> ChatHelp HSMarkdown,
|
||||
@@ -6653,6 +6659,7 @@ chatCommandP =
|
||||
"/set support #" *> (SetGroupFeature (AGFNR SGFSupport) <$> displayNameP <*> (A.space *> strP)),
|
||||
"/set links #" *> (SetGroupFeatureRole (AGFR SGFSimplexLinks) <$> displayNameP <*> _strP <*> optional memberRole),
|
||||
"/set admission review #" *> (SetGroupMemberAdmissionReview <$> displayNameP <*> (A.space *> memberCriteriaP)),
|
||||
"/set layout #" *> (SetGroupChatLayout <$> displayNameP <* A.space <*> chatLayoutP_),
|
||||
("/incognito" <* optional (A.space *> onOffP)) $> ChatHelp HSIncognito,
|
||||
"/set device name " *> (SetLocalDeviceName <$> textP),
|
||||
"/list remote hosts" $> ListRemoteHosts,
|
||||
@@ -6796,12 +6803,14 @@ chatCommandP =
|
||||
{ directMessages = Just DirectMessagesGroupPreference {enable = FEOn, role = Nothing},
|
||||
history = Just HistoryGroupPreference {enable = FEOn}
|
||||
}
|
||||
pure GroupProfile {displayName = gName, fullName = "", shortDescr, description = Nothing, image = Nothing, publicGroup = Nothing, groupPreferences, memberAdmission = Nothing}
|
||||
pure GroupProfile {displayName = gName, fullName = "", shortDescr, description = Nothing, image = Nothing, publicGroup = Nothing, groupPreferences, memberAdmission = Nothing, chatLayout = Nothing}
|
||||
channelProfile = do
|
||||
p@GroupProfile {groupPreferences = prefs_} <- groupProfile
|
||||
let prefs = (fromMaybe emptyGroupPrefs prefs_) {support = Just SupportGroupPreference {enable = FEOff}} :: GroupPreferences
|
||||
pure p {groupPreferences = Just prefs}
|
||||
memberCriteriaP = ("all" $> Just MCAll) <|> ("off" $> Nothing)
|
||||
chatLayoutP = ("classic" $> CLClassic) <|> ("terminal" $> CLTerminal)
|
||||
chatLayoutP_ = ("default" $> Nothing) <|> (Just <$> chatLayoutP)
|
||||
shortDescrP = do
|
||||
descr <- A.takeWhile1 isSpace *> (T.dropWhileEnd isSpace <$> textP) <|> pure ""
|
||||
pure $ if T.null descr then Nothing else Just $ T.take 160 descr
|
||||
@@ -6922,10 +6931,12 @@ chatCommandP =
|
||||
dbEncryptionConfig currentKey newKey = DBEncryptionConfig {currentKey, newKey, keepKey = Just False}
|
||||
#endif
|
||||
-- TODO [short links] parser for address settings
|
||||
autoAcceptP = ifM onOffP (businessAA <|> addressAA) (pure $ AddressSettings False Nothing Nothing)
|
||||
autoAcceptP = ifM onOffP (businessAA <|> addressAA) (toSettings False Nothing <$> layoutP <*> pure Nothing)
|
||||
where
|
||||
addressAA = AddressSettings False <$> (Just . AutoAccept <$> (" incognito=" *> onOffP <|> pure False)) <*> autoReply
|
||||
businessAA = " business" *> (AddressSettings True (Just $ AutoAccept False) <$> autoReply)
|
||||
addressAA = toSettings False <$> (Just . AutoAccept <$> (" incognito=" *> onOffP <|> pure False)) <*> layoutP <*> autoReply
|
||||
businessAA = " business" *> (toSettings True (Just $ AutoAccept False) <$> layoutP <*> autoReply)
|
||||
toSettings business autoAccept chatLayout_ autoReply_ = (AddressSettings business autoAccept Nothing autoReply_, chatLayout_)
|
||||
layoutP = optional (" layout=" *> chatLayoutP_)
|
||||
autoReply = optional (A.space *> msgContentP)
|
||||
rcCtrlAddressP = RCCtrlAddress <$> ("addr=" *> strP) <*> (" iface=" *> (quotedP <|> text1P))
|
||||
quotedP = safeDecodeUtf8 <$> (A.char '"' *> A.takeTill (== '"') <* A.char '"')
|
||||
|
||||
@@ -78,6 +78,7 @@ import Simplex.Chat.Types
|
||||
import Simplex.Chat.Types.MemberRelations
|
||||
import Simplex.Chat.Types.Preferences
|
||||
import Simplex.Chat.Types.Shared
|
||||
import Simplex.Chat.Types.UITheme (ChatLayout)
|
||||
import Simplex.Chat.Util (encryptFile, shuffle)
|
||||
import Simplex.FileTransfer.Description (FileDescriptionURI (..), ValidFileDescription)
|
||||
import qualified Simplex.FileTransfer.Description as FD
|
||||
@@ -1125,10 +1126,11 @@ acceptGroupJoinSendRejectAsync
|
||||
agentAcceptContactAsync cmdId acId False cReqInvId msg PQSupportOff subMode
|
||||
pure m
|
||||
|
||||
acceptBusinessJoinRequestAsync :: User -> Int64 -> GroupInfoKeys -> GroupMember -> UserContactRequest -> CM (GroupInfo, GroupMember)
|
||||
acceptBusinessJoinRequestAsync :: User -> Int64 -> Maybe ChatLayout -> GroupInfoKeys -> GroupMember -> UserContactRequest -> CM (GroupInfo, GroupMember)
|
||||
acceptBusinessJoinRequestAsync
|
||||
user
|
||||
uclId
|
||||
chatLayout
|
||||
(GIK gInfo@GroupInfo {membership = GroupMember {memberRole = userRole, memberId = userMemberId}} gks)
|
||||
clientMember@GroupMember {groupMemberId, memberId}
|
||||
UserContactRequest {agentInvitationId = AgentInvId cReqInvId, cReqChatVRange, xContactId} = do
|
||||
@@ -1143,7 +1145,7 @@ acceptBusinessJoinRequestAsync
|
||||
fromMemberName = displayName,
|
||||
fromMemberKey = Just $ groupMemberKey gks,
|
||||
invitedMember = MemberIdRole memberId GRMember,
|
||||
groupProfile = businessGroupProfile userProfile groupPreferences,
|
||||
groupProfile = businessGroupProfile userProfile groupPreferences chatLayout,
|
||||
accepted = Just GAAccepted,
|
||||
-- This refers to the "title member" that defines the group name and profile.
|
||||
-- This coincides with fromMember to be current user when accepting the connecting user,
|
||||
@@ -1213,9 +1215,9 @@ rejectRelayInvitationAsync user uclId cxt groupRelayInv presHeader_ invId reqCha
|
||||
createJoiningMemberConnection db user uclId (cmdId, acId) chatV reqChatVRange groupMemberId subMode
|
||||
agentAcceptContactAsync cmdId acId False invId msg PQSupportOff subMode
|
||||
|
||||
businessGroupProfile :: Profile -> GroupPreferences -> GroupProfile
|
||||
businessGroupProfile Profile {displayName, fullName, shortDescr, description, image} groupPreferences =
|
||||
GroupProfile {displayName, fullName, description, shortDescr, image, publicGroup = Nothing, groupPreferences = Just groupPreferences, memberAdmission = Nothing}
|
||||
businessGroupProfile :: Profile -> GroupPreferences -> Maybe ChatLayout -> GroupProfile
|
||||
businessGroupProfile Profile {displayName, fullName, shortDescr, description, image} groupPreferences chatLayout =
|
||||
GroupProfile {displayName, fullName, description, shortDescr, image, publicGroup = Nothing, groupPreferences = Just groupPreferences, memberAdmission = Nothing, chatLayout}
|
||||
|
||||
introduceToModerators :: StoreCxt -> User -> GroupInfoKeys -> GroupMember -> CM ()
|
||||
introduceToModerators cxt user gInfo@(GIK g@GroupInfo {groupId} _) m@GroupMember {memberRole, memberId} = do
|
||||
|
||||
@@ -1497,7 +1497,7 @@ processAgentMessageConn cxt user@User {userId} entity gks_ corrId agentConnId ag
|
||||
-- ##### Contact requests (regular and business contacts) #####
|
||||
Nothing -> do
|
||||
let UserContactLink {connLinkContact = CCLink connReq _, shortLinkDataSet, addressSettings} = ucl
|
||||
AddressSettings {autoAccept} = addressSettings
|
||||
AddressSettings {autoAccept, chatLayout} = addressSettings
|
||||
isSimplexTeam = sameConnReqContact connReq adminContactReq
|
||||
gVar <- asks random
|
||||
withStore (\db -> createOrUpdateContactRequest db gVar cxt user uclId ucl isSimplexTeam invId chatVRange (Just reqHeader) p xContactId_ welcomeMsgId_ requestMsg_ reqPQSup rejectionSupported) >>= \case
|
||||
@@ -1540,7 +1540,7 @@ processAgentMessageConn cxt user@User {userId} entity gks_ corrId agentConnId ag
|
||||
ct' <- acceptContactRequestAsync user uclId ct ucr incognitoProfile
|
||||
toView $ CEvtAcceptingContactRequest user ct'
|
||||
Just (REBusinessChat g@(GIK gInfo _) clientMember) -> do
|
||||
(_gInfo', _clientMember') <- acceptBusinessJoinRequestAsync user uclId g clientMember ucr
|
||||
(_gInfo', _clientMember') <- acceptBusinessJoinRequestAsync user uclId chatLayout g clientMember ucr
|
||||
let cd = CDGroupRcv gInfo Nothing clientMember
|
||||
void $ case prevUcr_ of
|
||||
Just UserContactRequest {requestSharedMsgId = prevSharedMsgId_} ->
|
||||
|
||||
@@ -56,6 +56,7 @@ import Simplex.Chat.Options.DB (FromField (..), ToField (..))
|
||||
import Simplex.Chat.Types
|
||||
import Simplex.Chat.Types.Preferences
|
||||
import Simplex.Chat.Types.Shared
|
||||
import Simplex.Chat.Types.UITheme (ChatLayout)
|
||||
import qualified Simplex.FileTransfer.Description as FD
|
||||
import Simplex.Messaging.Agent.Store.DB (blobFieldDecoder, fromTextField_)
|
||||
import Simplex.Messaging.Compression (Compressed, compress1, decompress1, decompressedSize)
|
||||
@@ -1612,6 +1613,7 @@ data ContactShortLinkData = ContactShortLinkData
|
||||
{ profile :: Profile,
|
||||
message :: Maybe MsgContent,
|
||||
business :: Bool,
|
||||
chatLayout :: Maybe ChatLayout,
|
||||
-- set by the receiving client for the UI: the link profile's badge, verified and crypto-free.
|
||||
-- never part of the published link data (the link carries the proof inside profile).
|
||||
localBadge :: Maybe LocalBadge
|
||||
|
||||
@@ -122,7 +122,7 @@ getConnectionEntityKeys db cxt user@User {userId, userContactId} agentConnId = d
|
||||
c.contact_profile_id, c.local_display_name, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, c.contact_used, c.contact_status, c.enable_ntfs, c.send_rcpts, c.favorite,
|
||||
p.preferences, p.preferences_json, c.user_preferences, c.created_at, c.updated_at, c.chat_ts, c.conn_full_link_to_connect, c.conn_short_link_to_connect, c.welcome_shared_msg_id, c.request_shared_msg_id, c.contact_request_id, cr2.rejection_supported,
|
||||
c.contact_group_member_id, c.contact_grp_inv_sent, c.grp_direct_inv_link, c.grp_direct_inv_from_group_id, c.grp_direct_inv_from_group_member_id, c.grp_direct_inv_from_member_conn_id, c.grp_direct_inv_started_connection,
|
||||
c.ui_themes, c.chat_deleted, c.custom_data, c.chat_item_ttl,
|
||||
c.ui_themes, c.chat_deleted, c.custom_data, c.chat_item_ttl, c.chat_layout,
|
||||
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx,
|
||||
p.contact_domain, p.contact_domain_proof, p.contact_domain_verified
|
||||
FROM contacts c
|
||||
@@ -132,7 +132,7 @@ getConnectionEntityKeys db cxt user@User {userId, userContactId} agentConnId = d
|
||||
|]
|
||||
(userId, contactId, CSActive)
|
||||
toContact' :: UTCTime -> Int64 -> Connection -> [ChatTagId] -> ContactRow' -> Contact
|
||||
toContact' currentTs contactId conn chatTags ((profileId, localDisplayName, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, encodedPrefs, receivedPrefs, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, rejectionSupported_, contactGroupMemberId, BI contactGrpInvSent) :. groupDirectInvRow :. (uiThemes, BI chatDeleted, customData, chatItemTTL) :. badgeRow :. domainRow) =
|
||||
toContact' currentTs contactId conn chatTags ((profileId, localDisplayName, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, encodedPrefs, receivedPrefs, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, rejectionSupported_, contactGroupMemberId, BI contactGrpInvSent) :. groupDirectInvRow :. (uiThemes, BI chatDeleted, customData, chatItemTTL, chatLayout) :. badgeRow :. domainRow) =
|
||||
let preferences = chatPrefsFromRow encodedPrefs receivedPrefs
|
||||
profile = LocalProfile {profileId, displayName, fullName, shortDescr, description, image, contactLink, contactDomain = rowToContactDomain domainRow, contactDomainVerified = rowToDomainVerified domainRow, peerType, localBadge = rowToBadge currentTs badgeRow, preferences, localAlias}
|
||||
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
|
||||
@@ -141,7 +141,7 @@ getConnectionEntityKeys db cxt user@User {userId, userContactId} agentConnId = d
|
||||
preparedContact = toPreparedContact preparedContactRow
|
||||
contactRequest = UserContactRequestRef <$> contactRequestId <*> (unBI <$> rejectionSupported_)
|
||||
groupDirectInv = toGroupDirectInvitation groupDirectInvRow
|
||||
in Contact {contactId, localDisplayName, profile, activeConn, contactUsed, contactStatus, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt, chatTs, preparedContact, contactRequestId, contactRequest, contactGroupMemberId, contactGrpInvSent, groupDirectInv, chatTags, chatItemTTL, uiThemes, chatDeleted, customData}
|
||||
in Contact {contactId, localDisplayName, profile, activeConn, contactUsed, contactStatus, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt, chatTs, preparedContact, contactRequestId, contactRequest, contactGroupMemberId, contactGrpInvSent, groupDirectInv, chatTags, chatItemTTL, uiThemes, chatLayout, chatDeleted, customData}
|
||||
getGroupAndMember_ :: Int64 -> Connection -> ExceptT StoreError IO ((GroupInfo, GroupKeysRow), GroupMember)
|
||||
getGroupAndMember_ groupMemberId c = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
@@ -155,7 +155,7 @@ getConnectionEntityKeys db cxt user@User {userId, userContactId} agentConnId = d
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id,
|
||||
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, gp.member_admission,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, gp.member_admission, gp.chat_layout,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
|
||||
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
|
||||
g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
|
||||
@@ -120,7 +120,7 @@ createOrUpdateContactRequest
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
||||
cp.preferences, cp.preferences_json, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
|
||||
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.chat_layout,
|
||||
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx, cp.contact_domain, cp.contact_domain_proof, cp.contact_domain_verified,
|
||||
-- Connection
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
|
||||
|
||||
@@ -324,7 +324,7 @@ getContactByConnReqHash db cxt user@User {userId} cReqHash1 cReqHash2 = do
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
||||
cp.preferences, cp.preferences_json, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
|
||||
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.chat_layout,
|
||||
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx,
|
||||
cp.contact_domain, cp.contact_domain_proof, cp.contact_domain_verified,
|
||||
-- Connection
|
||||
@@ -404,12 +404,12 @@ createIncognitoProfile db User {userId} p = do
|
||||
createdAt <- getCurrentTime
|
||||
createIncognitoProfile_ db userId createdAt p
|
||||
|
||||
createPreparedContact :: DB.Connection -> StoreCxt -> User -> Maybe ProofPresHeader -> Profile -> ACreatedConnLink -> Maybe SharedMsgId -> Maybe Bool -> ExceptT StoreError IO Contact
|
||||
createPreparedContact db cxt user presHeader_ p connLinkToConnect welcomeSharedMsgId verified_ = do
|
||||
createPreparedContact :: DB.Connection -> StoreCxt -> User -> Maybe ProofPresHeader -> Profile -> ACreatedConnLink -> Maybe SharedMsgId -> Maybe ChatLayout -> Maybe Bool -> ExceptT StoreError IO Contact
|
||||
createPreparedContact db cxt user presHeader_ p connLinkToConnect welcomeSharedMsgId chatLayout verified_ = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
let prepared = Just (connLinkToConnect, welcomeSharedMsgId)
|
||||
ctUserPreferences = newContactUserPrefs user p
|
||||
ct <- getContact db cxt user =<< createContact_ db cxt user presHeader_ p ctUserPreferences prepared "" currentTs
|
||||
ct <- getContact db cxt user =<< createContact_ db cxt user presHeader_ p ctUserPreferences prepared chatLayout "" currentTs
|
||||
liftIO $ maybe (pure ct) (setContactDomainVerified db user ct) verified_
|
||||
|
||||
updatePreparedContactUser :: DB.Connection -> StoreCxt -> User -> Contact -> User -> ExceptT StoreError IO Contact
|
||||
@@ -454,7 +454,7 @@ createDirectContact :: DB.Connection -> StoreCxt -> User -> Connection -> Maybe
|
||||
createDirectContact db cxt user Connection {connId, localAlias} presHeader_ p = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
let ctUserPreferences = newContactUserPrefs user p
|
||||
contactId <- createContact_ db cxt user presHeader_ p ctUserPreferences Nothing localAlias currentTs
|
||||
contactId <- createContact_ db cxt user presHeader_ p ctUserPreferences Nothing Nothing localAlias currentTs
|
||||
liftIO $ DB.execute db "UPDATE connections SET contact_id = ?, updated_at = ? WHERE connection_id = ?" (contactId, currentTs, connId)
|
||||
getContact db cxt user contactId
|
||||
|
||||
@@ -927,6 +927,7 @@ createContactFromRequest db user@User {userId, profile = LocalProfile {preferenc
|
||||
chatTags = [],
|
||||
chatItemTTL = Nothing,
|
||||
uiThemes = Nothing,
|
||||
chatLayout = Nothing,
|
||||
chatDeleted = False,
|
||||
customData = Nothing
|
||||
}
|
||||
@@ -976,7 +977,7 @@ getContact_ db cxt user@User {userId} contactId deleted = do
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
||||
cp.preferences, cp.preferences_json, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
|
||||
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.chat_layout,
|
||||
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx,
|
||||
cp.contact_domain, cp.contact_domain_proof, cp.contact_domain_verified,
|
||||
-- Connection
|
||||
|
||||
@@ -382,7 +382,7 @@ setGroupLinkShortLink db gLnk@GroupLink {userContactLinkId, connLinkContact = CC
|
||||
-- | creates completely new group with a single member - the current user
|
||||
createNewGroup :: DB.Connection -> StoreCxt -> User -> GroupProfile -> Maybe Profile -> MemberId -> GroupKeys -> Maybe Int64 -> ExceptT StoreError IO GroupInfo
|
||||
createNewGroup db cxt user@User {userId} groupProfile incognitoProfile memberId groupKeys publicMemberCount_ = ExceptT $ do
|
||||
let GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup, groupPreferences, memberAdmission} = groupProfile
|
||||
let GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup, groupPreferences, memberAdmission, chatLayout} = groupProfile
|
||||
(groupType_, groupLink_, publicGroupId_) = case publicGroup of
|
||||
Just PublicGroupProfile {groupType, groupLink, publicGroupId} -> (Just groupType, Just groupLink, Just publicGroupId)
|
||||
Nothing -> (Nothing, Nothing, Nothing)
|
||||
@@ -406,11 +406,11 @@ createNewGroup db cxt user@User {userId} groupProfile incognitoProfile memberId
|
||||
(display_name, full_name, short_descr, description, image,
|
||||
group_type, group_link, public_group_id,
|
||||
group_web_page, group_domain, domain_web_page, allow_embedding, group_domain_proof,
|
||||
user_id, member_admission, created_at, updated_at, preferences, preferences_json)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
user_id, member_admission, chat_layout, created_at, updated_at, preferences, preferences_json)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
((displayName, fullName, shortDescr, description, image, groupType_, groupLink_, publicGroupId_) :. publicGroupAccessRow publicGroup
|
||||
:. (userId, memberAdmission, currentTs, currentTs) :. prefsToRow groupPreferences)
|
||||
:. (userId, memberAdmission, chatLayout, currentTs, currentTs) :. prefsToRow groupPreferences)
|
||||
profileId <- insertedRowId db
|
||||
DB.execute
|
||||
db
|
||||
@@ -480,7 +480,7 @@ createGroupInvitation db cxt user@User {userId} inviter peerChatVRange GroupInvi
|
||||
DB.query db "SELECT group_id FROM groups WHERE inv_queue_info = ? AND user_id = ? LIMIT 1" (connRequest, userId)
|
||||
createGroupInvitation_ :: ExceptT StoreError IO (GroupInfoKeys, GroupMemberId)
|
||||
createGroupInvitation_ = do
|
||||
let GroupProfile {displayName, fullName, shortDescr, description, image, groupPreferences, memberAdmission} = groupProfile
|
||||
let GroupProfile {displayName, fullName, shortDescr, description, image, groupPreferences, memberAdmission, chatLayout} = groupProfile
|
||||
fullGroupPreferences = mergeGroupPreferences groupPreferences
|
||||
ExceptT $
|
||||
withLocalDisplayName db userId displayName $ \localDisplayName -> runExceptT $ do
|
||||
@@ -488,8 +488,8 @@ createGroupInvitation db cxt user@User {userId} inviter peerChatVRange GroupInvi
|
||||
groupId <- liftIO $ do
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, user_id, member_admission, created_at, updated_at, preferences, preferences_json) VALUES (?,?,?,?,?,?,?,?,?,?,?)"
|
||||
((displayName, fullName, shortDescr, description, image, userId, memberAdmission, currentTs, currentTs) :. prefsToRow groupPreferences)
|
||||
"INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, user_id, member_admission, chat_layout, created_at, updated_at, preferences, preferences_json) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"
|
||||
((displayName, fullName, shortDescr, description, image, userId, memberAdmission, chatLayout, currentTs, currentTs) :. prefsToRow groupPreferences)
|
||||
profileId <- insertedRowId db
|
||||
DB.execute
|
||||
db
|
||||
@@ -927,7 +927,7 @@ createGroupViaLink'
|
||||
|
||||
createGroup_ :: DB.Connection -> UserId -> GroupProfile -> Maybe (CreatedLinkContact, Maybe SharedMsgId) -> Maybe BusinessChatInfo -> Bool -> Maybe RelayStatus -> Maybe Int64 -> Maybe C.PrivateKeyEd25519 -> UTCTime -> ExceptT StoreError IO (GroupId, Text)
|
||||
createGroup_ db userId groupProfile prepared business useRelays relayOwnStatus publicMemberCount_ memberPrivKey_ currentTs = ExceptT $ do
|
||||
let GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup, groupPreferences, memberAdmission} = groupProfile
|
||||
let GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup, groupPreferences, memberAdmission, chatLayout} = groupProfile
|
||||
(groupType_, groupLink_, publicGroupId_) = case publicGroup of
|
||||
Just PublicGroupProfile {groupType, groupLink, publicGroupId} -> (Just groupType, Just groupLink, Just publicGroupId)
|
||||
Nothing -> (Nothing, Nothing, Nothing)
|
||||
@@ -940,11 +940,11 @@ createGroup_ db userId groupProfile prepared business useRelays relayOwnStatus p
|
||||
(display_name, full_name, short_descr, description, image,
|
||||
group_type, group_link, public_group_id,
|
||||
group_web_page, group_domain, domain_web_page, allow_embedding, group_domain_proof,
|
||||
user_id, member_admission, created_at, updated_at, preferences, preferences_json)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
user_id, member_admission, chat_layout, created_at, updated_at, preferences, preferences_json)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
((displayName, fullName, shortDescr, description, image, groupType_, groupLink_, publicGroupId_) :. publicGroupAccessRow publicGroup
|
||||
:. (userId, memberAdmission, currentTs, currentTs) :. prefsToRow groupPreferences)
|
||||
:. (userId, memberAdmission, chatLayout, currentTs, currentTs) :. prefsToRow groupPreferences)
|
||||
profileId <- insertedRowId db
|
||||
DB.execute
|
||||
db
|
||||
@@ -1957,7 +1957,8 @@ createRelayRequestGroup db cxt user@User {userId} GroupRelayInvitation {fromMemb
|
||||
image = Nothing,
|
||||
publicGroup = Nothing,
|
||||
groupPreferences = Nothing,
|
||||
memberAdmission = Nothing
|
||||
memberAdmission = Nothing,
|
||||
chatLayout = Nothing
|
||||
}
|
||||
(_, memberPrivKey) <- atomically $ C.generateKeyPair (drg cxt)
|
||||
(groupId, _groupLDN) <- createGroup_ db userId placeholderProfile Nothing Nothing True (Just relayStatus) Nothing (Just memberPrivKey) currentTs
|
||||
@@ -2726,7 +2727,7 @@ createMemberConnection_ db userId groupMemberId agentConnId chatV peerChatVRange
|
||||
createConnection_ db userId ConnMember (Just groupMemberId) agentConnId ConnNew chatV peerChatVRange viaContact Nothing Nothing connLevel currentTs subMode PQSupportOff
|
||||
|
||||
updateGroupProfile :: DB.Connection -> User -> GroupInfo -> GroupProfile -> ExceptT StoreError IO GroupInfo
|
||||
updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName, groupProfile = GroupProfile {displayName, publicGroup = oldPublicGroup}} p'@GroupProfile {displayName = newName, fullName, shortDescr, description, image, publicGroup, groupPreferences, memberAdmission}
|
||||
updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName, groupProfile = GroupProfile {displayName, publicGroup = oldPublicGroup}} p'@GroupProfile {displayName = newName, fullName, shortDescr, description, image, publicGroup, groupPreferences, memberAdmission, chatLayout}
|
||||
| displayName == newName = liftIO $ do
|
||||
currentTs <- getCurrentTime
|
||||
updateGroupProfile_ currentTs
|
||||
@@ -2762,7 +2763,7 @@ updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName,
|
||||
display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?,
|
||||
group_type = ?, group_link = ?,
|
||||
group_web_page = ?, group_domain = CASE WHEN ? THEN ? ELSE group_domain END, domain_web_page = ?, allow_embedding = ?, group_domain_proof = ?,
|
||||
member_admission = ?, updated_at = ?
|
||||
member_admission = ?, chat_layout = ?, updated_at = ?
|
||||
WHERE group_profile_id IN (
|
||||
SELECT group_profile_id
|
||||
FROM groups
|
||||
@@ -2772,7 +2773,7 @@ updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName,
|
||||
( prefsToRow groupPreferences
|
||||
:. (newName, fullName, shortDescr, description, image, groupType_, groupLink_)
|
||||
:. (groupWebPage_, isJust publicGroup, groupDomain_, domainWebPage_, allowEmbedding_, groupDomainProof_)
|
||||
:. (memberAdmission, currentTs, userId, groupId)
|
||||
:. (memberAdmission, chatLayout, currentTs, userId, groupId)
|
||||
)
|
||||
updateGroup_ ldn currentTs = do
|
||||
DB.execute
|
||||
@@ -2836,17 +2837,17 @@ getGroupProfileById db groupId =
|
||||
SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image,
|
||||
gp.group_type, gp.group_link, gp.public_group_id,
|
||||
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
|
||||
gp.preferences, gp.preferences_json, gp.member_admission
|
||||
gp.preferences, gp.preferences_json, gp.member_admission, gp.chat_layout
|
||||
FROM group_profiles gp
|
||||
JOIN groups g ON gp.group_profile_id = g.group_profile_id
|
||||
WHERE g.group_id = ?
|
||||
|]
|
||||
(Only groupId)
|
||||
where
|
||||
toGroupProfile ((displayName, fullName, shortDescr, description, image, groupType_, groupLink_, publicGroupId_) :. accessRow :. (encodedPrefs, receivedPrefs, memberAdmission)) =
|
||||
toGroupProfile ((displayName, fullName, shortDescr, description, image, groupType_, groupLink_, publicGroupId_) :. accessRow :. (encodedPrefs, receivedPrefs, memberAdmission, chatLayout)) =
|
||||
let groupPreferences = groupPrefsFromRow encodedPrefs receivedPrefs
|
||||
publicGroupAccess = toPublicGroupAccess accessRow
|
||||
in GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup = toPublicGroupProfile groupType_ groupLink_ publicGroupId_ publicGroupAccess, groupPreferences, memberAdmission}
|
||||
in GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup = toPublicGroupProfile groupType_ groupLink_ publicGroupId_ publicGroupAccess, groupPreferences, memberAdmission, chatLayout}
|
||||
|
||||
getGroupInfoByUserContactLinkConnReq :: DB.Connection -> StoreCxt -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe GroupInfo)
|
||||
getGroupInfoByUserContactLinkConnReq db cxt user@User {userId} (cReqSchema1, cReqSchema2) = do
|
||||
@@ -3312,7 +3313,7 @@ createMemberContact
|
||||
quotaErrCounter = 0
|
||||
}
|
||||
mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito ctConn
|
||||
pure Contact {contactId, localDisplayName, profile = memberProfile, activeConn = Just ctConn, contactUsed = True, contactStatus = CSActive, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, createdAt = currentTs, updatedAt = currentTs, chatTs = Just currentTs, preparedContact = Nothing, contactRequestId = Nothing, contactRequest = Nothing, contactGroupMemberId = Just groupMemberId, contactGrpInvSent = False, groupDirectInv = Nothing, chatTags = [], chatItemTTL = Nothing, uiThemes = Nothing, chatDeleted = False, customData = Nothing}
|
||||
pure Contact {contactId, localDisplayName, profile = memberProfile, activeConn = Just ctConn, contactUsed = True, contactStatus = CSActive, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, createdAt = currentTs, updatedAt = currentTs, chatTs = Just currentTs, preparedContact = Nothing, contactRequestId = Nothing, contactRequest = Nothing, contactGroupMemberId = Just groupMemberId, contactGrpInvSent = False, groupDirectInv = Nothing, chatTags = [], chatItemTTL = Nothing, uiThemes = Nothing, chatLayout = Nothing, chatDeleted = False, customData = Nothing}
|
||||
|
||||
getMemberContact :: DB.Connection -> StoreCxt -> User -> ContactId -> ExceptT StoreError IO (GroupInfo, GroupMember, Contact, ConnReqInvitation)
|
||||
getMemberContact db cxt user contactId = do
|
||||
|
||||
@@ -57,6 +57,7 @@ import Simplex.Chat.Store.Postgres.Migrations.M20260925_badge_bindings
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20261010_invite_member_via_group
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20261012_delivery_job_notification
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20261016_badge_store_receipts
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20261019_chat_layout
|
||||
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
|
||||
|
||||
schemaMigrations :: [(String, Text, Maybe Text)]
|
||||
@@ -113,7 +114,8 @@ schemaMigrations =
|
||||
("20260925_badge_bindings", m20260925_badge_bindings, Just down_m20260925_badge_bindings),
|
||||
("20261010_invite_member_via_group", m20261010_invite_member_via_group, Just down_m20261010_invite_member_via_group),
|
||||
("20261012_delivery_job_notification", m20261012_delivery_job_notification, Just down_m20261012_delivery_job_notification),
|
||||
("20261016_badge_store_receipts", m20261016_badge_store_receipts, Just down_m20261016_badge_store_receipts)
|
||||
("20261016_badge_store_receipts", m20261016_badge_store_receipts, Just down_m20261016_badge_store_receipts),
|
||||
("20261019_chat_layout", m20261019_chat_layout, Just down_m20261019_chat_layout)
|
||||
]
|
||||
|
||||
-- | The list of migrations in ascending order by date
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.Postgres.Migrations.M20261019_chat_layout where
|
||||
|
||||
import Data.Text (Text)
|
||||
import Text.RawString.QQ (r)
|
||||
|
||||
m20261019_chat_layout :: Text
|
||||
m20261019_chat_layout =
|
||||
[r|
|
||||
ALTER TABLE contacts ADD COLUMN chat_layout TEXT;
|
||||
ALTER TABLE group_profiles ADD COLUMN chat_layout TEXT;
|
||||
ALTER TABLE user_contact_links ADD COLUMN chat_layout TEXT;
|
||||
|]
|
||||
|
||||
down_m20261019_chat_layout :: Text
|
||||
down_m20261019_chat_layout =
|
||||
[r|
|
||||
ALTER TABLE contacts DROP COLUMN chat_layout;
|
||||
ALTER TABLE group_profiles DROP COLUMN chat_layout;
|
||||
ALTER TABLE user_contact_links DROP COLUMN chat_layout;
|
||||
|]
|
||||
@@ -795,7 +795,8 @@ CREATE TABLE test_chat_schema.contacts (
|
||||
grp_direct_inv_from_group_id bigint,
|
||||
grp_direct_inv_from_group_member_id bigint,
|
||||
grp_direct_inv_from_member_conn_id bigint,
|
||||
grp_direct_inv_started_connection smallint DEFAULT 0 NOT NULL
|
||||
grp_direct_inv_started_connection smallint DEFAULT 0 NOT NULL,
|
||||
chat_layout text
|
||||
);
|
||||
|
||||
|
||||
@@ -1092,7 +1093,8 @@ CREATE TABLE test_chat_schema.group_profiles (
|
||||
domain_web_page bigint,
|
||||
allow_embedding bigint,
|
||||
group_domain_proof text,
|
||||
preferences_json text
|
||||
preferences_json text,
|
||||
chat_layout text
|
||||
);
|
||||
|
||||
|
||||
@@ -1727,7 +1729,8 @@ CREATE TABLE test_chat_schema.user_contact_links (
|
||||
short_link_contact bytea,
|
||||
short_link_data_set smallint DEFAULT 0 NOT NULL,
|
||||
short_link_large_data_set smallint DEFAULT 0 NOT NULL,
|
||||
link_priv_sig_key bytea
|
||||
link_priv_sig_key bytea,
|
||||
chat_layout text
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -512,6 +512,7 @@ data GroupLinkInfo = GroupLinkInfo
|
||||
data AddressSettings = AddressSettings
|
||||
{ businessAddress :: Bool, -- possibly, it can be wrapped together with acceptIncognito, or AutoAccept made sum type
|
||||
autoAccept :: Maybe AutoAccept, -- accept automatically
|
||||
chatLayout :: Maybe ChatLayout,
|
||||
autoReply :: Maybe MsgContent -- included in short link information, sent on acceptance in case wasn't shown during connection
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
@@ -527,11 +528,11 @@ $(J.deriveJSON defaultJSON ''AddressSettings)
|
||||
|
||||
$(J.deriveJSON defaultJSON ''UserContactLink)
|
||||
|
||||
toUserContactLink :: (Int64, ConnReqContact, Maybe ShortLinkContact, BoolInt, BoolInt, BoolInt, BoolInt, BoolInt, Maybe MsgContent) -> UserContactLink
|
||||
toUserContactLink (userContactLinkId, connReq, shortLink, BI shortLinkDataSet, BI slLargeDataSet, BI businessAddress, BI autoAccept', BI acceptIncognito, autoReply) =
|
||||
toUserContactLink :: (Int64, ConnReqContact, Maybe ShortLinkContact, BoolInt, BoolInt, BoolInt, BoolInt, BoolInt, Maybe MsgContent, Maybe ChatLayout) -> UserContactLink
|
||||
toUserContactLink (userContactLinkId, connReq, shortLink, BI shortLinkDataSet, BI slLargeDataSet, BI businessAddress, BI autoAccept', BI acceptIncognito, autoReply, chatLayout) =
|
||||
UserContactLink userContactLinkId (CCLink connReq shortLink) shortLinkDataSet (BoolDef slLargeDataSet) $
|
||||
let autoAccept = if autoAccept' then Just AutoAccept {acceptIncognito} else Nothing
|
||||
in AddressSettings {businessAddress, autoAccept, autoReply}
|
||||
in AddressSettings {businessAddress, autoAccept, chatLayout, autoReply}
|
||||
|
||||
getUserAddress :: DB.Connection -> User -> ExceptT StoreError IO UserContactLink
|
||||
getUserAddress db User {userId} =
|
||||
@@ -544,7 +545,7 @@ getUserContactLinkById db userId userContactLinkId =
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, short_link_large_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content, group_id, group_link_member_role
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, short_link_large_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content, chat_layout, group_id, group_link_member_role
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND user_contact_link_id = ?
|
||||
|]
|
||||
@@ -586,7 +587,7 @@ getUserContactLinkViaTarget db User {userId, profile = LocalProfile {contactDoma
|
||||
userContactLinkQuery :: Query
|
||||
userContactLinkQuery =
|
||||
[sql|
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, short_link_large_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, short_link_large_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content, chat_layout
|
||||
FROM user_contact_links
|
||||
|]
|
||||
|
||||
@@ -637,15 +638,15 @@ getContactWithoutConnViaShortAddress db cxt user@User {userId} shortLink = do
|
||||
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getContact db cxt user) ctId_
|
||||
|
||||
updateUserAddressSettings :: DB.Connection -> Int64 -> AddressSettings -> IO ()
|
||||
updateUserAddressSettings db userContactLinkId AddressSettings {businessAddress, autoAccept, autoReply} =
|
||||
updateUserAddressSettings db userContactLinkId AddressSettings {businessAddress, autoAccept, chatLayout, autoReply} =
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE user_contact_links
|
||||
SET auto_accept = ?, auto_accept_incognito = ?, business_address = ?, auto_reply_msg_content = ?
|
||||
SET auto_accept = ?, auto_accept_incognito = ?, business_address = ?, auto_reply_msg_content = ?, chat_layout = ?
|
||||
WHERE user_contact_link_id = ?
|
||||
|]
|
||||
(autoAcceptValues :. (BI businessAddress, autoReply, userContactLinkId))
|
||||
(autoAcceptValues :. (BI businessAddress, autoReply, chatLayout, userContactLinkId))
|
||||
where
|
||||
autoAcceptValues = case autoAccept of
|
||||
Just AutoAccept {acceptIncognito} -> (BI True, BI acceptIncognito)
|
||||
|
||||
@@ -181,6 +181,7 @@ import Simplex.Chat.Store.SQLite.Migrations.M20261010_invite_member_via_group
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20261012_delivery_job_notification
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20261016_badge_store_receipts
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20261018_autoincrement_ids
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20261019_chat_layout
|
||||
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
|
||||
|
||||
schemaMigrations :: [(String, Query, Maybe Query)]
|
||||
@@ -361,7 +362,8 @@ schemaMigrations =
|
||||
("20261010_invite_member_via_group", m20261010_invite_member_via_group, Just down_m20261010_invite_member_via_group),
|
||||
("20261012_delivery_job_notification", m20261012_delivery_job_notification, Just down_m20261012_delivery_job_notification),
|
||||
("20261016_badge_store_receipts", m20261016_badge_store_receipts, Just down_m20261016_badge_store_receipts),
|
||||
("20261018_autoincrement_ids", m20261018_autoincrement_ids, Just down_m20261018_autoincrement_ids)
|
||||
("20261018_autoincrement_ids", m20261018_autoincrement_ids, Just down_m20261018_autoincrement_ids),
|
||||
("20261019_chat_layout", m20261019_chat_layout, Just down_m20261019_chat_layout)
|
||||
]
|
||||
|
||||
-- | The list of migrations in ascending order by date
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20261019_chat_layout where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20261019_chat_layout :: Query
|
||||
m20261019_chat_layout =
|
||||
[sql|
|
||||
ALTER TABLE contacts ADD COLUMN chat_layout TEXT;
|
||||
ALTER TABLE group_profiles ADD COLUMN chat_layout TEXT;
|
||||
ALTER TABLE user_contact_links ADD COLUMN chat_layout TEXT;
|
||||
|]
|
||||
|
||||
down_m20261019_chat_layout :: Query
|
||||
down_m20261019_chat_layout =
|
||||
[sql|
|
||||
ALTER TABLE contacts DROP COLUMN chat_layout;
|
||||
ALTER TABLE group_profiles DROP COLUMN chat_layout;
|
||||
ALTER TABLE user_contact_links DROP COLUMN chat_layout;
|
||||
|]
|
||||
@@ -1535,10 +1535,6 @@ Query: UPDATE ratchets SET ratchet_state = ? WHERE conn_id = ?
|
||||
Plan:
|
||||
SEARCH ratchets USING PRIMARY KEY (conn_id=?)
|
||||
|
||||
Query: UPDATE rcv_file_chunk_replicas SET delay = ?, retries = retries + 1, updated_at = ? WHERE rcv_file_chunk_replica_id = ?
|
||||
Plan:
|
||||
SEARCH rcv_file_chunk_replicas USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query: UPDATE rcv_file_chunk_replicas SET received = 1, updated_at = ? WHERE rcv_file_chunk_replica_id = ?
|
||||
Plan:
|
||||
SEARCH rcv_file_chunk_replicas USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
@@ -130,7 +130,7 @@ Query:
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
||||
cp.preferences, cp.preferences_json, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
|
||||
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.chat_layout,
|
||||
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx, cp.contact_domain, cp.contact_domain_proof, cp.contact_domain_verified,
|
||||
-- Connection
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
|
||||
@@ -153,7 +153,7 @@ Query:
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id,
|
||||
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, gp.member_admission,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, gp.member_admission, gp.chat_layout,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
|
||||
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
|
||||
g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
@@ -546,7 +546,7 @@ Query:
|
||||
display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?,
|
||||
group_type = ?, group_link = ?,
|
||||
group_web_page = ?, group_domain = CASE WHEN ? THEN ? ELSE group_domain END, domain_web_page = ?, allow_embedding = ?, group_domain_proof = ?,
|
||||
member_admission = ?, updated_at = ?
|
||||
member_admission = ?, chat_layout = ?, updated_at = ?
|
||||
WHERE group_profile_id IN (
|
||||
SELECT group_profile_id
|
||||
FROM groups
|
||||
@@ -758,7 +758,7 @@ Query:
|
||||
c.contact_profile_id, c.local_display_name, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, c.contact_used, c.contact_status, c.enable_ntfs, c.send_rcpts, c.favorite,
|
||||
p.preferences, p.preferences_json, c.user_preferences, c.created_at, c.updated_at, c.chat_ts, c.conn_full_link_to_connect, c.conn_short_link_to_connect, c.welcome_shared_msg_id, c.request_shared_msg_id, c.contact_request_id, cr2.rejection_supported,
|
||||
c.contact_group_member_id, c.contact_grp_inv_sent, c.grp_direct_inv_link, c.grp_direct_inv_from_group_id, c.grp_direct_inv_from_group_member_id, c.grp_direct_inv_from_member_conn_id, c.grp_direct_inv_started_connection,
|
||||
c.ui_themes, c.chat_deleted, c.custom_data, c.chat_item_ttl,
|
||||
c.ui_themes, c.chat_deleted, c.custom_data, c.chat_item_ttl, c.chat_layout,
|
||||
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx,
|
||||
p.contact_domain, p.contact_domain_proof, p.contact_domain_verified
|
||||
FROM contacts c
|
||||
@@ -1372,8 +1372,8 @@ Query:
|
||||
(display_name, full_name, short_descr, description, image,
|
||||
group_type, group_link, public_group_id,
|
||||
group_web_page, group_domain, domain_web_page, allow_embedding, group_domain_proof,
|
||||
user_id, member_admission, created_at, updated_at, preferences, preferences_json)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
user_id, member_admission, chat_layout, created_at, updated_at, preferences, preferences_json)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
|
||||
@@ -1520,7 +1520,7 @@ Query:
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
||||
cp.preferences, cp.preferences_json, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
|
||||
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.chat_layout,
|
||||
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx,
|
||||
cp.contact_domain, cp.contact_domain_proof, cp.contact_domain_verified,
|
||||
-- Connection
|
||||
@@ -2121,7 +2121,7 @@ Query:
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
||||
cp.preferences, cp.preferences_json, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
|
||||
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.chat_layout,
|
||||
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx,
|
||||
cp.contact_domain, cp.contact_domain_proof, cp.contact_domain_verified,
|
||||
-- Connection
|
||||
@@ -4003,7 +4003,7 @@ Query:
|
||||
SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image,
|
||||
gp.group_type, gp.group_link, gp.public_group_id,
|
||||
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
|
||||
gp.preferences, gp.preferences_json, gp.member_admission
|
||||
gp.preferences, gp.preferences_json, gp.member_admission, gp.chat_layout
|
||||
FROM group_profiles gp
|
||||
JOIN groups g ON gp.group_profile_id = g.group_profile_id
|
||||
WHERE g.group_id = ?
|
||||
@@ -4317,7 +4317,7 @@ Plan:
|
||||
SEARCH usage_conditions USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, short_link_large_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content, group_id, group_link_member_role
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, short_link_large_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content, chat_layout, group_id, group_link_member_role
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND user_contact_link_id = ?
|
||||
|
||||
@@ -5468,11 +5468,11 @@ Query:
|
||||
badge_proof = ?, badge_pres_header = ?, badge_expiry = ?, badge_type = ?, badge_verified = ?, badge_extra = ?, badge_master_key = ?, badge_signature = ?, badge_key_idx = ?,
|
||||
contact_domain = ?, contact_domain_proof = ?
|
||||
WHERE user_id = ? AND contact_profile_id = ?
|
||||
|
||||
|
||||
Plan:
|
||||
SEARCH contact_profiles USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
Query:
|
||||
UPDATE contact_profiles
|
||||
SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, contact_link = ?, chat_peer_type = ?, preferences = NULL, preferences_json = NULL, updated_at = ?,
|
||||
badge_proof = ?, badge_pres_header = ?, badge_expiry = ?, badge_type = ?, badge_verified = ?, badge_extra = ?, badge_master_key = ?, badge_signature = ?, badge_key_idx = ?,
|
||||
@@ -5780,7 +5780,7 @@ SEARCH server_operators USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
UPDATE user_contact_links
|
||||
SET auto_accept = ?, auto_accept_incognito = ?, business_address = ?, auto_reply_msg_content = ?
|
||||
SET auto_accept = ?, auto_accept_incognito = ?, business_address = ?, auto_reply_msg_content = ?, chat_layout = ?
|
||||
WHERE user_contact_link_id = ?
|
||||
|
||||
Plan:
|
||||
@@ -5856,7 +5856,7 @@ Query:
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id,
|
||||
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, gp.member_admission,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, gp.member_admission, gp.chat_layout,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
|
||||
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
|
||||
g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
@@ -5894,7 +5894,7 @@ Query:
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id,
|
||||
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, gp.member_admission,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, gp.member_admission, gp.chat_layout,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
|
||||
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
|
||||
g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
@@ -5925,7 +5925,7 @@ Query:
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id,
|
||||
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, gp.member_admission,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, gp.member_admission, gp.chat_layout,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
|
||||
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
|
||||
g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
@@ -6699,21 +6699,21 @@ Plan:
|
||||
SCAN usage_conditions
|
||||
|
||||
Query:
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, short_link_large_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, short_link_large_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content, chat_layout
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND conn_req_contact IN (?,?)
|
||||
Plan:
|
||||
SEARCH user_contact_links USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=?)
|
||||
|
||||
Query:
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, short_link_large_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, short_link_large_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content, chat_layout
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND local_display_name = '' AND group_id IS NULL
|
||||
Plan:
|
||||
SEARCH user_contact_links USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=? AND local_display_name=?)
|
||||
|
||||
Query:
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, short_link_large_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, short_link_large_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content, chat_layout
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND short_link_contact = ?
|
||||
Plan:
|
||||
@@ -7375,7 +7375,7 @@ Query: INSERT INTO contacts (contact_profile_id, local_display_name, user_id, is
|
||||
Plan:
|
||||
SEARCH users USING COVERING INDEX sqlite_autoindex_users_1 (contact_id=?)
|
||||
|
||||
Query: INSERT INTO contacts (contact_profile_id, user_preferences, local_display_name, user_id, created_at, updated_at, chat_ts, contact_used, conn_full_link_to_connect, conn_short_link_to_connect, welcome_shared_msg_id) VALUES (?,?,?,?,?,?,?,?,?,?,?)
|
||||
Query: INSERT INTO contacts (contact_profile_id, user_preferences, local_display_name, user_id, created_at, updated_at, chat_ts, contact_used, conn_full_link_to_connect, conn_short_link_to_connect, welcome_shared_msg_id, chat_layout) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
Plan:
|
||||
SEARCH users USING COVERING INDEX sqlite_autoindex_users_1 (contact_id=?)
|
||||
|
||||
@@ -7406,7 +7406,7 @@ Plan:
|
||||
Query: INSERT INTO files (user_id, group_id, file_name, file_size, chunk_size, file_inline, ci_file_status, protocol, file_type, shared_msg_id, roster_transfer_id, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
Plan:
|
||||
|
||||
Query: INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, user_id, member_admission, created_at, updated_at, preferences, preferences_json) VALUES (?,?,?,?,?,?,?,?,?,?,?)
|
||||
Query: INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, user_id, member_admission, chat_layout, created_at, updated_at, preferences, preferences_json) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
Plan:
|
||||
|
||||
Query: INSERT INTO group_profiles (display_name, full_name, short_descr, image, user_id, created_at, updated_at, preferences, preferences_json) VALUES (?,?,?,?,?,?,?,?,?)
|
||||
|
||||
@@ -109,6 +109,7 @@ CREATE TABLE contacts(
|
||||
grp_direct_inv_from_group_member_id INTEGER REFERENCES group_members(group_member_id) ON DELETE SET NULL,
|
||||
grp_direct_inv_from_member_conn_id INTEGER REFERENCES connections(connection_id) ON DELETE SET NULL,
|
||||
grp_direct_inv_started_connection INTEGER NOT NULL DEFAULT 0,
|
||||
chat_layout TEXT,
|
||||
FOREIGN KEY(user_id, local_display_name)
|
||||
REFERENCES display_names(user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
@@ -149,7 +150,8 @@ CREATE TABLE group_profiles(
|
||||
domain_web_page INTEGER,
|
||||
allow_embedding INTEGER,
|
||||
group_domain_proof TEXT,
|
||||
preferences_json TEXT
|
||||
preferences_json TEXT,
|
||||
chat_layout TEXT
|
||||
) STRICT;
|
||||
CREATE TABLE groups(
|
||||
group_id INTEGER PRIMARY KEY AUTOINCREMENT, -- local group ID
|
||||
@@ -417,6 +419,7 @@ CREATE TABLE user_contact_links(
|
||||
short_link_data_set INTEGER NOT NULL DEFAULT 0,
|
||||
short_link_large_data_set INTEGER NOT NULL DEFAULT 0,
|
||||
link_priv_sig_key BLOB,
|
||||
chat_layout TEXT,
|
||||
UNIQUE(user_id, local_display_name)
|
||||
) STRICT;
|
||||
CREATE TABLE contact_requests(
|
||||
|
||||
@@ -414,10 +414,10 @@ setCommandConnId db User {userId} cmdId connId = do
|
||||
createContact :: DB.Connection -> StoreCxt -> User -> Profile -> ExceptT StoreError IO ()
|
||||
createContact db cxt user profile = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
void $ createContact_ db cxt user Nothing profile emptyChatPrefs Nothing "" currentTs
|
||||
void $ createContact_ db cxt user Nothing profile emptyChatPrefs Nothing Nothing "" currentTs
|
||||
|
||||
createContact_ :: DB.Connection -> StoreCxt -> User -> Maybe ProofPresHeader -> Profile -> Preferences -> Maybe (ACreatedConnLink, Maybe SharedMsgId) -> LocalAlias -> UTCTime -> ExceptT StoreError IO ContactId
|
||||
createContact_ db cxt User {userId} presHeader_ p@Profile {displayName, fullName, shortDescr, description, image, contactLink, contactDomain, peerType, preferences} ctUserPreferences prepared localAlias currentTs =
|
||||
createContact_ :: DB.Connection -> StoreCxt -> User -> Maybe ProofPresHeader -> Profile -> Preferences -> Maybe (ACreatedConnLink, Maybe SharedMsgId) -> Maybe ChatLayout -> LocalAlias -> UTCTime -> ExceptT StoreError IO ContactId
|
||||
createContact_ db cxt User {userId} presHeader_ p@Profile {displayName, fullName, shortDescr, description, image, contactLink, contactDomain, peerType, preferences} ctUserPreferences prepared chatLayout localAlias currentTs =
|
||||
ExceptT . withLocalDisplayName db userId displayName $ \ldn -> do
|
||||
(Profile {badge}, badgeVerified) <- profileBadgeVerified presHeader_ (badgeKeys cxt) Nothing p
|
||||
DB.execute
|
||||
@@ -427,8 +427,8 @@ createContact_ db cxt User {userId} presHeader_ p@Profile {displayName, fullName
|
||||
profileId <- insertedRowId db
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO contacts (contact_profile_id, user_preferences, local_display_name, user_id, created_at, updated_at, chat_ts, contact_used, conn_full_link_to_connect, conn_short_link_to_connect, welcome_shared_msg_id) VALUES (?,?,?,?,?,?,?,?,?,?,?)"
|
||||
((profileId, ctUserPreferences, ldn, userId, currentTs, currentTs, currentTs, BI True) :. toPreparedContactRow prepared)
|
||||
"INSERT INTO contacts (contact_profile_id, user_preferences, local_display_name, user_id, created_at, updated_at, chat_ts, contact_used, conn_full_link_to_connect, conn_short_link_to_connect, welcome_shared_msg_id, chat_layout) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"
|
||||
((profileId, ctUserPreferences, ldn, userId, currentTs, currentTs, currentTs, BI True) :. toPreparedContactRow prepared :. Only chatLayout)
|
||||
contactId <- insertedRowId db
|
||||
pure $ Right contactId
|
||||
|
||||
@@ -490,14 +490,14 @@ type PreparedContactRow = (Maybe AConnectionRequestUri, Maybe AConnShortLink, Ma
|
||||
|
||||
type GroupDirectInvitationRow = (Maybe ConnReqInvitation, Maybe GroupId, Maybe GroupMemberId, Maybe Int64, BoolInt)
|
||||
|
||||
type ContactRow' = (ProfileId, ContactName, ContactName, Text, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, BoolInt, ContactStatus) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Text, Maybe Text, Preferences, UTCTime, UTCTime, Maybe UTCTime) :. PreparedContactRow :. (Maybe Int64, Maybe BoolInt, Maybe GroupMemberId, BoolInt) :. GroupDirectInvitationRow :. (Maybe UIThemeEntityOverrides, BoolInt, Maybe CustomData, Maybe Int64) :. BadgeRow :. ContactDomainRow
|
||||
type ContactRow' = (ProfileId, ContactName, ContactName, Text, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, BoolInt, ContactStatus) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Text, Maybe Text, Preferences, UTCTime, UTCTime, Maybe UTCTime) :. PreparedContactRow :. (Maybe Int64, Maybe BoolInt, Maybe GroupMemberId, BoolInt) :. GroupDirectInvitationRow :. (Maybe UIThemeEntityOverrides, BoolInt, Maybe CustomData, Maybe Int64, Maybe ChatLayout) :. BadgeRow :. ContactDomainRow
|
||||
|
||||
type ContactRow = Only ContactId :. ContactRow'
|
||||
|
||||
type ContactDomainRow = (Maybe SimplexDomain, Maybe SimplexDomainProof, Maybe BoolInt)
|
||||
|
||||
toContact :: UTCTime -> StoreCxt -> User -> [ChatTagId] -> ContactRow :. MaybeConnectionRow -> Contact
|
||||
toContact now cxt user chatTags ((Only contactId :. (profileId, localDisplayName, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, encodedPrefs, receivedPrefs, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, rejectionSupported_, contactGroupMemberId, BI contactGrpInvSent) :. groupDirectInvRow :. (uiThemes, BI chatDeleted, customData, chatItemTTL) :. badgeRow :. domainRow) :. connRow) =
|
||||
toContact now cxt user chatTags ((Only contactId :. (profileId, localDisplayName, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, encodedPrefs, receivedPrefs, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, rejectionSupported_, contactGroupMemberId, BI contactGrpInvSent) :. groupDirectInvRow :. (uiThemes, BI chatDeleted, customData, chatItemTTL, chatLayout) :. badgeRow :. domainRow) :. connRow) =
|
||||
let preferences = chatPrefsFromRow encodedPrefs receivedPrefs
|
||||
profile = LocalProfile {profileId, displayName, fullName, shortDescr, description, image, contactLink, contactDomain = rowToContactDomain domainRow, contactDomainVerified = rowToDomainVerified domainRow, peerType, localBadge = rowToBadge now badgeRow, preferences, localAlias}
|
||||
activeConn = toMaybeConnection cxt connRow
|
||||
@@ -507,7 +507,7 @@ toContact now cxt user chatTags ((Only contactId :. (profileId, localDisplayName
|
||||
preparedContact = toPreparedContact preparedContactRow
|
||||
contactRequest = UserContactRequestRef <$> contactRequestId <*> (unBI <$> rejectionSupported_)
|
||||
groupDirectInv = toGroupDirectInvitation groupDirectInvRow
|
||||
in Contact {contactId, localDisplayName, profile, activeConn, contactUsed, contactStatus, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt, chatTs, preparedContact, contactRequestId, contactRequest, contactGroupMemberId, contactGrpInvSent, groupDirectInv, chatTags, chatItemTTL, uiThemes, chatDeleted, customData}
|
||||
in Contact {contactId, localDisplayName, profile, activeConn, contactUsed, contactStatus, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt, chatTs, preparedContact, contactRequestId, contactRequest, contactGroupMemberId, contactGrpInvSent, groupDirectInv, chatTags, chatItemTTL, uiThemes, chatLayout, chatDeleted, customData}
|
||||
|
||||
rowToContactDomain :: ContactDomainRow -> Maybe SimplexDomainClaim
|
||||
rowToContactDomain (domain_, domainProof_, _) = (`SimplexDomainClaim` domainProof_) . StrJSON <$> domain_
|
||||
@@ -696,7 +696,7 @@ type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe Member
|
||||
|
||||
type GroupKeysRow = (Maybe C.PrivateKeyEd25519, Maybe C.PublicKeyEd25519, Maybe C.PrivateKeyEd25519)
|
||||
|
||||
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData, Maybe GroupType, Maybe ShortLinkContact, Maybe B64UrlByteString) :. PublicGroupAccessRow :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Text, Maybe Text, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (BoolInt, Maybe RelayStatus, Maybe UIThemeEntityOverrides, Int64, Maybe Int64, Maybe VersionRoster, Maybe CustomData, Maybe Int64, Int, Maybe ConnReqContact, Maybe BoolInt) :. GroupKeysRow :. GroupMemberRow
|
||||
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData, Maybe GroupType, Maybe ShortLinkContact, Maybe B64UrlByteString) :. PublicGroupAccessRow :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Text, Maybe Text, Maybe GroupMemberAdmission, Maybe ChatLayout) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (BoolInt, Maybe RelayStatus, Maybe UIThemeEntityOverrides, Int64, Maybe Int64, Maybe VersionRoster, Maybe CustomData, Maybe Int64, Int, Maybe ConnReqContact, Maybe BoolInt) :. GroupKeysRow :. GroupMemberRow
|
||||
|
||||
type PublicGroupAccessRow = (Maybe Text, Maybe SimplexDomain, Maybe BoolInt, Maybe BoolInt, Maybe SimplexDomainProof)
|
||||
|
||||
@@ -705,13 +705,13 @@ type GroupMemberRow = (GroupMemberId, GroupId, Int64, MemberId, VersionChat, Ver
|
||||
type ProfileRow = (ProfileId, ContactName, Text, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, Maybe Text, Maybe Text) :. BadgeRow :. ContactDomainRow
|
||||
|
||||
toGroupInfo :: UTCTime -> StoreCxt -> Int64 -> [ChatTagId] -> GroupInfoRow -> (GroupInfo, GroupKeysRow)
|
||||
toGroupInfo now cxt userContactId chatTags ((groupId, localDisplayName, displayName, fullName, shortDescr, localAlias, description, image, groupType_, groupLink_, publicGroupId_) :. accessRow :. (enableNtfs_, sendRcpts, BI favorite, encodedPrefs, receivedPrefs, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. preparedGroupRow :. businessRow :. (BI useRelays, relayOwnStatus, uiThemes, currentMembers, publicMemberCount, rosterVersion, customData, chatItemTTL, membersRequireAttention, viaGroupLinkUri, groupDomainVerified) :. groupKeysRow :. userMemberRow) =
|
||||
toGroupInfo now cxt userContactId chatTags ((groupId, localDisplayName, displayName, fullName, shortDescr, localAlias, description, image, groupType_, groupLink_, publicGroupId_) :. accessRow :. (enableNtfs_, sendRcpts, BI favorite, encodedPrefs, receivedPrefs, memberAdmission, chatLayout) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. preparedGroupRow :. businessRow :. (BI useRelays, relayOwnStatus, uiThemes, currentMembers, publicMemberCount, rosterVersion, customData, chatItemTTL, membersRequireAttention, viaGroupLinkUri, groupDomainVerified) :. groupKeysRow :. userMemberRow) =
|
||||
let membership = (toGroupMember now userContactId userMemberRow) {memberChatVRange = vr cxt}
|
||||
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
|
||||
groupPreferences = groupPrefsFromRow encodedPrefs receivedPrefs
|
||||
fullGroupPreferences = mergeGroupPreferences groupPreferences
|
||||
publicGroup = toPublicGroupProfile groupType_ groupLink_ publicGroupId_ (toPublicGroupAccess accessRow)
|
||||
groupProfile = GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup, groupPreferences, memberAdmission}
|
||||
groupProfile = GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup, groupPreferences, memberAdmission, chatLayout}
|
||||
businessChat = toBusinessChatInfo (toPublicGroupAccess accessRow >>= groupDomainClaim) businessRow
|
||||
preparedGroup = toPreparedGroup preparedGroupRow
|
||||
groupSummary = GroupSummary {currentMembers, publicMemberCount}
|
||||
@@ -843,7 +843,7 @@ groupInfoQueryFields =
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id,
|
||||
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, gp.member_admission,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, gp.member_admission, gp.chat_layout,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
|
||||
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
|
||||
g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
|
||||
@@ -220,6 +220,7 @@ data Contact = Contact
|
||||
chatTags :: [ChatTagId],
|
||||
chatItemTTL :: Maybe Int64,
|
||||
uiThemes :: Maybe UIThemeEntityOverrides,
|
||||
chatLayout :: Maybe ChatLayout,
|
||||
chatDeleted :: Bool,
|
||||
customData :: Maybe CustomData
|
||||
}
|
||||
@@ -919,7 +920,8 @@ data GroupProfile = GroupProfile
|
||||
image :: Maybe ImageData,
|
||||
publicGroup :: Maybe PublicGroupProfile,
|
||||
groupPreferences :: Maybe GroupPreferences,
|
||||
memberAdmission :: Maybe GroupMemberAdmission
|
||||
memberAdmission :: Maybe GroupMemberAdmission,
|
||||
chatLayout :: Maybe ChatLayout
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
|
||||
@@ -32,7 +32,8 @@ data UIColorMode = UCMLight | UCMDark
|
||||
|
||||
data UIThemeEntityOverrides = UIThemeEntityOverrides
|
||||
{ light :: Maybe UIThemeEntityOverride,
|
||||
dark :: Maybe UIThemeEntityOverride
|
||||
dark :: Maybe UIThemeEntityOverride,
|
||||
chatLayout :: Maybe ChatLayout
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
@@ -43,6 +44,30 @@ data UIThemeEntityOverride = UIThemeEntityOverride
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
data ChatLayout = CLClassic | CLTerminal | CLUnknown Text
|
||||
deriving (Eq, Show)
|
||||
|
||||
instance FromJSON ChatLayout where
|
||||
parseJSON = textParseJSON "ChatLayout"
|
||||
|
||||
instance ToJSON ChatLayout where
|
||||
toJSON = J.String . textEncode
|
||||
toEncoding = JE.text . textEncode
|
||||
|
||||
instance FromField ChatLayout where fromField = fromTextField_ textDecode
|
||||
|
||||
instance ToField ChatLayout where toField = toField . textEncode
|
||||
|
||||
instance TextEncoding ChatLayout where
|
||||
textDecode s = Just $ case s of
|
||||
"classic" -> CLClassic
|
||||
"terminal" -> CLTerminal
|
||||
tag -> CLUnknown tag
|
||||
textEncode = \case
|
||||
CLClassic -> "classic"
|
||||
CLTerminal -> "terminal"
|
||||
CLUnknown tag -> tag
|
||||
|
||||
data DarkColorScheme = DCSDark | DCSBlack | DCSSimplex
|
||||
deriving (Eq, Ord, Show)
|
||||
|
||||
@@ -172,4 +197,4 @@ instance ToField UIThemeEntityOverrides where
|
||||
toField = toField . encodeJSON
|
||||
|
||||
instance FromField UIThemeEntityOverrides where
|
||||
fromField = fromTextField_ $ Just . fromMaybe (UIThemeEntityOverrides Nothing Nothing) . decodeJSON
|
||||
fromField = fromTextField_ $ Just . fromMaybe (UIThemeEntityOverrides Nothing Nothing Nothing) . decodeJSON
|
||||
@@ -1190,16 +1190,17 @@ simplexDomainLine nameType (Just SimplexDomainClaim {domain, proof}) status = ca
|
||||
|
||||
-- TODO [short links] show all settings
|
||||
viewAddressSettings :: AddressSettings -> [StyledString]
|
||||
viewAddressSettings AddressSettings {businessAddress, autoAccept, autoReply} = case autoAccept of
|
||||
viewAddressSettings AddressSettings {businessAddress, autoAccept, chatLayout, autoReply} = case autoAccept of
|
||||
Just AutoAccept {acceptIncognito} ->
|
||||
("auto_accept on" <> aaInfo)
|
||||
: maybe [] ((["auto reply:"] <>) . ttyMsgContent) autoReply
|
||||
: viewChatLayout chatLayout
|
||||
<> maybe [] ((["auto reply:"] <>) . ttyMsgContent) autoReply
|
||||
where
|
||||
aaInfo
|
||||
| businessAddress = ", business"
|
||||
| acceptIncognito = ", incognito"
|
||||
| otherwise = ""
|
||||
_ -> ["auto_accept off"]
|
||||
_ -> "auto_accept off" : viewChatLayout chatLayout
|
||||
|
||||
groupLink_ :: Bool -> StyledString -> GroupInfo -> GroupLink -> [StyledString]
|
||||
groupLink_ showFullLinks intro g GroupLink {connLinkContact = CCLink cReq shortLink, acceptMemberRole} =
|
||||
@@ -1893,7 +1894,7 @@ dayTime :: UTCTime -> Text
|
||||
dayTime = T.pack . formatTime defaultTimeLocale "%Y-%m-%d %H:%M"
|
||||
|
||||
viewContactInfo :: Contact -> Maybe ConnectionStats -> Maybe Profile -> [StyledString]
|
||||
viewContactInfo ct@Contact {contactId, profile = LocalProfile {localAlias, contactLink, localBadge, contactDomain, contactDomainVerified, description}, activeConn, uiThemes, customData} stats incognitoProfile =
|
||||
viewContactInfo ct@Contact {contactId, profile = LocalProfile {localAlias, contactLink, localBadge, contactDomain, contactDomainVerified, description}, activeConn, uiThemes, chatLayout, customData} stats incognitoProfile =
|
||||
["contact ID: " <> sShow contactId]
|
||||
<> viewContactBadge localBadge
|
||||
<> maybe [] ((bold' "description:" :) . map plain . T.lines) description
|
||||
@@ -1909,6 +1910,7 @@ viewContactInfo ct@Contact {contactId, profile = LocalProfile {localAlias, conta
|
||||
<> ["quantum resistant end-to-end encryption" | contactPQEnabled ct == CR.PQEncOn]
|
||||
<> maybe [] (\ac -> [viewPeerChatVRange (peerChatVRange ac)]) activeConn
|
||||
<> viewUITheme uiThemes
|
||||
<> viewChatLayout chatLayout
|
||||
<> viewCustomData customData
|
||||
|
||||
viewGroupInfo :: GroupInfo -> [StyledString]
|
||||
@@ -1931,6 +1933,9 @@ viewGroupInfo gInfo@GroupInfo {groupId, businessChat, groupDomainVerified, group
|
||||
viewUITheme :: Maybe UIThemeEntityOverrides -> [StyledString]
|
||||
viewUITheme = maybe [] (\uiThemes -> ["UI themes: " <> viewJSON uiThemes])
|
||||
|
||||
viewChatLayout :: Maybe ChatLayout -> [StyledString]
|
||||
viewChatLayout = maybe [] (\chatLayout -> ["chat layout: " <> plain (textEncode chatLayout)])
|
||||
|
||||
viewCustomData :: Maybe CustomData -> [StyledString]
|
||||
viewCustomData = maybe [] (\(CustomData v) -> ["custom data: " <> viewJSON (J.Object v)])
|
||||
|
||||
@@ -2111,10 +2116,10 @@ countactUserPrefText cup = case cup of
|
||||
|
||||
viewGroupUpdated :: GroupInfo -> GroupInfo -> Maybe GroupMember -> Maybe MsgSigStatus -> [StyledString]
|
||||
viewGroupUpdated
|
||||
GroupInfo {localDisplayName = n, groupProfile = GroupProfile {fullName, shortDescr, description, image, groupPreferences = gps, memberAdmission = ma, publicGroup = pg}}
|
||||
g'@GroupInfo {localDisplayName = n', groupProfile = GroupProfile {fullName = fullName', shortDescr = shortDescr', description = description', image = image', groupPreferences = gps', memberAdmission = ma', publicGroup = pg'}}
|
||||
GroupInfo {localDisplayName = n, groupProfile = GroupProfile {fullName, shortDescr, description, image, groupPreferences = gps, memberAdmission = ma, chatLayout = layout, publicGroup = pg}}
|
||||
g'@GroupInfo {localDisplayName = n', groupProfile = GroupProfile {fullName = fullName', shortDescr = shortDescr', description = description', image = image', groupPreferences = gps', memberAdmission = ma', chatLayout = layout', publicGroup = pg'}}
|
||||
m signed = do
|
||||
let update = groupProfileUpdated <> groupPrefsUpdated <> memberAdmissionUpdated <> publicGroupAccessUpdated
|
||||
let update = groupProfileUpdated <> groupPrefsUpdated <> memberAdmissionUpdated <> chatLayoutUpdated <> publicGroupAccessUpdated
|
||||
if null update
|
||||
then []
|
||||
else memberUpdated <> update
|
||||
@@ -2139,6 +2144,9 @@ viewGroupUpdated
|
||||
memberAdmissionUpdated
|
||||
| ma == ma' = []
|
||||
| otherwise = ["changed member admission rules"]
|
||||
chatLayoutUpdated
|
||||
| layout == layout' = []
|
||||
| otherwise = ["chat layout " <> maybe "removed" (("changed to: " <>) . plain . textEncode) layout']
|
||||
publicGroupAccessUpdated
|
||||
| access == access' = []
|
||||
| otherwise = ["updated public group access:" <> viewAccess access']
|
||||
@@ -2153,11 +2161,12 @@ viewGroupUpdated
|
||||
<> (if allowEmbedding then " embed=on" else "")
|
||||
|
||||
viewGroupProfile :: GroupInfo -> [StyledString]
|
||||
viewGroupProfile g@GroupInfo {groupProfile = GroupProfile {shortDescr, description, image, groupPreferences = gps}} =
|
||||
viewGroupProfile g@GroupInfo {groupProfile = GroupProfile {shortDescr, description, image, groupPreferences = gps, chatLayout}} =
|
||||
[ttyFullGroup g]
|
||||
<> maybe [] (\sd -> ["description: " <> plain sd]) shortDescr
|
||||
<> maybe [] (const ["has profile image"]) image
|
||||
<> maybe [] ((bold' "welcome message:" :) . map plain . T.lines) description
|
||||
<> viewChatLayout chatLayout
|
||||
<> (bold' "group preferences:" : map viewPref (if useRelays' g then channelGroupFeatures else regularGroupFeatures))
|
||||
where
|
||||
viewPref (AGF f) = plain $ groupPreferenceText (pref gps)
|
||||
|
||||
Reference in new issue
Block a user