core: chat items showing preference changes (#1399)
This commit is contained in:
committed by
GitHub
parent
c7f70f0ed0
commit
2b09fb425d
@@ -9,7 +9,7 @@
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
func infoRow(_ title: LocalizedStringKey, _ value: String) -> some View {
|
||||
func infoRow<S>(_ title: S, _ value: String) -> some View where S: StringProtocol {
|
||||
HStack {
|
||||
Text(title)
|
||||
Spacer()
|
||||
|
||||
41
apps/ios/Shared/Views/Chat/ChatItem/CIChatFeatureView.swift
Normal file
41
apps/ios/Shared/Views/Chat/ChatItem/CIChatFeatureView.swift
Normal file
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// CIChatFeatureView.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by Evgeny on 21/11/2022.
|
||||
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
struct CIChatFeatureView: View {
|
||||
var chatItem: ChatItem
|
||||
var feature: Feature
|
||||
var enabled: FeatureEnabled?
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .bottom, spacing: 0) {
|
||||
Image(systemName: feature.icon + ".fill")
|
||||
.foregroundColor(iconColor)
|
||||
chatEventText(chatItem)
|
||||
}
|
||||
.padding(.leading, 6)
|
||||
.padding(.bottom, 6)
|
||||
.textSelection(.disabled)
|
||||
}
|
||||
|
||||
private var iconColor: Color {
|
||||
if let enabled = enabled {
|
||||
return enabled.forUser ? .green : enabled.forContact ? .yellow : .secondary
|
||||
}
|
||||
return .red
|
||||
}
|
||||
}
|
||||
|
||||
struct CIChatFeatureView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let enabled = FeatureEnabled(forUser: false, forContact: false)
|
||||
CIChatFeatureView(chatItem: ChatItem.getChatFeatureSample(.fullDelete, enabled), feature: .fullDelete, enabled: enabled)
|
||||
}
|
||||
}
|
||||
@@ -20,27 +20,27 @@ struct CIEventView: View {
|
||||
.foregroundColor(.secondary)
|
||||
.fontWeight(.light)
|
||||
+ Text(" ")
|
||||
+ eventText()
|
||||
+ chatEventText(chatItem)
|
||||
} else {
|
||||
eventText()
|
||||
chatEventText(chatItem)
|
||||
}
|
||||
}
|
||||
.padding(.leading, 6)
|
||||
.padding(.bottom, 6)
|
||||
.textSelection(.disabled)
|
||||
}
|
||||
}
|
||||
|
||||
func eventText() -> Text {
|
||||
Text(chatItem.content.text)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.fontWeight(.light)
|
||||
+ Text(" ")
|
||||
+ chatItem.timestampText
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.secondary)
|
||||
.fontWeight(.light)
|
||||
}
|
||||
func chatEventText(_ ci: ChatItem) -> Text {
|
||||
Text(ci.content.text)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.fontWeight(.light)
|
||||
+ Text(" ")
|
||||
+ ci.timestampText
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.secondary)
|
||||
.fontWeight(.light)
|
||||
}
|
||||
|
||||
struct CIEventView_Previews: PreviewProvider {
|
||||
|
||||
@@ -31,6 +31,9 @@ struct ChatItemView: View {
|
||||
case .sndGroupEvent: eventItemView()
|
||||
case .rcvConnEvent: eventItemView()
|
||||
case .sndConnEvent: eventItemView()
|
||||
case let .rcvChatFeature(feature, enabled): chatFeatureView(feature, enabled)
|
||||
case let .sndChatFeature(feature, enabled): chatFeatureView(feature, enabled)
|
||||
case let .rcvChatFeatureRejected(feature): chatFeatureView(feature, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +60,10 @@ struct ChatItemView: View {
|
||||
private func eventItemView() -> some View {
|
||||
CIEventView(chatItem: chatItem)
|
||||
}
|
||||
|
||||
private func chatFeatureView(_ feature: Feature, _ enabled: FeatureEnabled?) -> some View {
|
||||
CIChatFeatureView(chatItem: chatItem, feature: feature, enabled: enabled)
|
||||
}
|
||||
}
|
||||
|
||||
struct ChatItemView_Previews: PreviewProvider {
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
5C55A923283CEDE600C4E99E /* SoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C55A922283CEDE600C4E99E /* SoundPlayer.swift */; };
|
||||
5C55A92E283D0FDE00C4E99E /* sounds in Resources */ = {isa = PBXBuildFile; fileRef = 5C55A92D283D0FDE00C4E99E /* sounds */; };
|
||||
5C577F7D27C83AA10006112D /* MarkdownHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C577F7C27C83AA10006112D /* MarkdownHelp.swift */; };
|
||||
5C58BCD6292BEBE600AF9E4F /* CIChatFeatureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C58BCD5292BEBE600AF9E4F /* CIChatFeatureView.swift */; };
|
||||
5C58BCD0292AA93700AF9E4F /* libHSsimplex-chat-4.2.1-HrC8bBnv4qm8Sq6Eue57W1-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C58BCCB292AA93700AF9E4F /* libHSsimplex-chat-4.2.1-HrC8bBnv4qm8Sq6Eue57W1-ghc8.10.7.a */; };
|
||||
5C58BCD1292AA93700AF9E4F /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C58BCCC292AA93700AF9E4F /* libgmp.a */; };
|
||||
5C58BCD2292AA93700AF9E4F /* libHSsimplex-chat-4.2.1-HrC8bBnv4qm8Sq6Eue57W1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C58BCCD292AA93700AF9E4F /* libHSsimplex-chat-4.2.1-HrC8bBnv4qm8Sq6Eue57W1.a */; };
|
||||
@@ -247,6 +248,7 @@
|
||||
5C55A922283CEDE600C4E99E /* SoundPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundPlayer.swift; sourceTree = "<group>"; };
|
||||
5C55A92D283D0FDE00C4E99E /* sounds */ = {isa = PBXFileReference; lastKnownFileType = folder; path = sounds; sourceTree = "<group>"; };
|
||||
5C577F7C27C83AA10006112D /* MarkdownHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownHelp.swift; sourceTree = "<group>"; };
|
||||
5C58BCD5292BEBE600AF9E4F /* CIChatFeatureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIChatFeatureView.swift; sourceTree = "<group>"; };
|
||||
5C58BCCB292AA93700AF9E4F /* libHSsimplex-chat-4.2.1-HrC8bBnv4qm8Sq6Eue57W1-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.2.1-HrC8bBnv4qm8Sq6Eue57W1-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||
5C58BCCC292AA93700AF9E4F /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5C58BCCD292AA93700AF9E4F /* libHSsimplex-chat-4.2.1-HrC8bBnv4qm8Sq6Eue57W1.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.2.1-HrC8bBnv4qm8Sq6Eue57W1.a"; sourceTree = "<group>"; };
|
||||
@@ -672,6 +674,7 @@
|
||||
5C3F1D552842B68D00EC8A82 /* IntegrityErrorItemView.swift */,
|
||||
64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */,
|
||||
6440C9FF288857A10062C672 /* CIEventView.swift */,
|
||||
5C58BCD5292BEBE600AF9E4F /* CIChatFeatureView.swift */,
|
||||
);
|
||||
path = ChatItem;
|
||||
sourceTree = "<group>";
|
||||
@@ -960,6 +963,7 @@
|
||||
5CFE0921282EEAF60002594B /* ZoomableScrollView.swift in Sources */,
|
||||
5C3A88CE27DF50170060F1C2 /* DetermineWidth.swift in Sources */,
|
||||
5C7505A527B679EE00BE3227 /* NavLinkPlain.swift in Sources */,
|
||||
5C58BCD6292BEBE600AF9E4F /* CIChatFeatureView.swift in Sources */,
|
||||
5CB346E72868D76D001FD2EF /* NotificationsView.swift in Sources */,
|
||||
647F090E288EA27B00644C40 /* GroupMemberInfoView.swift in Sources */,
|
||||
646BB38E283FDB6D001CE359 /* LocalAuthenticationUtils.swift in Sources */,
|
||||
|
||||
@@ -213,6 +213,13 @@ public struct FeatureEnabled: Decodable {
|
||||
default: return FeatureEnabled(forUser: true, forContact: true)
|
||||
}
|
||||
}
|
||||
|
||||
public var text: String {
|
||||
(forUser && forContact) ? NSLocalizedString("enabled", comment: "enabled status")
|
||||
: forUser ? NSLocalizedString("enabled for you", comment: "enabled status")
|
||||
: forContact ? NSLocalizedString("enabled for contact", comment: "enabled status")
|
||||
: NSLocalizedString("off", comment: "enabled status")
|
||||
}
|
||||
}
|
||||
|
||||
public enum ContactUserPref: Decodable {
|
||||
@@ -220,7 +227,7 @@ public enum ContactUserPref: Decodable {
|
||||
case user(preference: Preference) // global user default is used
|
||||
}
|
||||
|
||||
public enum Feature {
|
||||
public enum Feature: String, Decodable {
|
||||
case fullDelete
|
||||
case voice
|
||||
|
||||
@@ -228,10 +235,10 @@ public enum Feature {
|
||||
|
||||
public var id: Self { self }
|
||||
|
||||
public var text: LocalizedStringKey {
|
||||
public var text: String {
|
||||
switch self {
|
||||
case .fullDelete: return "Full deletion"
|
||||
case .voice: return "Voice messages"
|
||||
case .fullDelete: return NSLocalizedString("Full deletion", comment: "chat feature")
|
||||
case .voice: return NSLocalizedString("Voice messages", comment: "chat feature")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1364,6 +1371,17 @@ public struct ChatItem: Identifiable, Decodable {
|
||||
file: nil
|
||||
)
|
||||
}
|
||||
|
||||
public static func getChatFeatureSample(_ feature: Feature, _ enabled: FeatureEnabled) -> ChatItem {
|
||||
let content = CIContent.rcvChatFeature(feature: feature, enabled: enabled)
|
||||
return ChatItem(
|
||||
chatDir: .directRcv,
|
||||
meta: CIMeta.getSample(1, .now, content.text, .rcvRead, false, false, false),
|
||||
content: content,
|
||||
quotedItem: nil,
|
||||
file: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public enum CIDirection: Decodable {
|
||||
@@ -1465,6 +1483,9 @@ public enum CIContent: Decodable, ItemContent {
|
||||
case sndGroupEvent(sndGroupEvent: SndGroupEvent)
|
||||
case rcvConnEvent(rcvConnEvent: RcvConnEvent)
|
||||
case sndConnEvent(sndConnEvent: SndConnEvent)
|
||||
case rcvChatFeature(feature: Feature, enabled: FeatureEnabled)
|
||||
case sndChatFeature(feature: Feature, enabled: FeatureEnabled)
|
||||
case rcvChatFeatureRejected(feature: Feature)
|
||||
|
||||
public var text: String {
|
||||
get {
|
||||
@@ -1482,6 +1503,9 @@ public enum CIContent: Decodable, ItemContent {
|
||||
case let .sndGroupEvent(sndGroupEvent): return sndGroupEvent.text
|
||||
case let .rcvConnEvent(rcvConnEvent): return rcvConnEvent.text
|
||||
case let .sndConnEvent(sndConnEvent): return sndConnEvent.text
|
||||
case let .rcvChatFeature(feature, enabled): return "\(feature.text): \(enabled.text)"
|
||||
case let .sndChatFeature(feature, enabled): return "\(feature.text): \(enabled.text)"
|
||||
case let .rcvChatFeatureRejected(feature): return String.localizedStringWithFormat("%@: received, prohibited", feature.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1750,6 +1750,7 @@ processAgentMessage (Just user@User {userId}) corrId agentConnId agentMessage =
|
||||
-- [incognito] print incognito profile used for this contact
|
||||
incognitoProfile <- forM customUserProfileId $ \profileId -> withStore (\db -> getProfileById db userId profileId)
|
||||
toView $ CRContactConnected ct (fmap fromLocalProfile incognitoProfile)
|
||||
createFeatureEnabledItems ct
|
||||
setActive $ ActiveC c
|
||||
showToast (c <> "> ") "connected"
|
||||
forM_ groupLinkId $ \_ -> probeMatchingContacts ct $ contactConnIncognito ct
|
||||
@@ -2483,6 +2484,23 @@ processAgentMessage (Just user@User {userId}) corrId agentConnId agentMessage =
|
||||
xInfo c@Contact {profile = p} p' = unless (fromLocalProfile p == p') $ do
|
||||
c' <- withStore $ \db -> updateContactProfile db user c p'
|
||||
toView $ CRContactUpdated c c'
|
||||
createFeatureChangedItems c'
|
||||
where
|
||||
createFeatureChangedItems c' = unless (preferences' c == preferences' c') $ do
|
||||
let cups = contactUserPreferences' user c
|
||||
cups' = contactUserPreferences' user c'
|
||||
forM_ allChatFeatures $ \f -> do
|
||||
let ContactUserPreference {enabled} = getContactUserPreference f cups
|
||||
ContactUserPreference {enabled = enabled'} = getContactUserPreference f cups'
|
||||
unless (enabled == enabled') $
|
||||
createInternalChatItem (CDDirectRcv c') (CIRcvChatFeature f enabled') Nothing
|
||||
|
||||
createFeatureEnabledItems :: Contact -> m ()
|
||||
createFeatureEnabledItems ct = do
|
||||
let cups = contactUserPreferences' user ct
|
||||
forM_ allChatFeatures $ \f -> do
|
||||
let ContactUserPreference {enabled} = getContactUserPreference f cups
|
||||
createInternalChatItem (CDDirectRcv ct) (CIRcvChatFeature f enabled) Nothing
|
||||
|
||||
xInfoProbe :: Contact -> Probe -> m ()
|
||||
xInfoProbe c2 probe =
|
||||
|
||||
@@ -558,6 +558,9 @@ data CIContent (d :: MsgDirection) where
|
||||
CISndGroupEvent :: SndGroupEvent -> CIContent 'MDSnd
|
||||
CIRcvConnEvent :: RcvConnEvent -> CIContent 'MDRcv
|
||||
CISndConnEvent :: SndConnEvent -> CIContent 'MDSnd
|
||||
CIRcvChatFeature :: ChatFeature -> PrefEnabled -> CIContent 'MDRcv
|
||||
CISndChatFeature :: ChatFeature -> PrefEnabled -> CIContent 'MDSnd
|
||||
CIRcvChatFeatureRejected :: ChatFeature -> CIContent 'MDRcv
|
||||
-- ^ This type is used both in API and in DB, so we use different JSON encodings for the database and for the API
|
||||
-- ! ^ Nested sum types also have to use different encodings for database and API
|
||||
-- ! ^ to avoid breaking cross-platform compatibility, see RcvGroupEvent and SndGroupEvent
|
||||
@@ -709,6 +712,9 @@ ciContentToText = \case
|
||||
CISndGroupEvent event -> sndGroupEventToText event
|
||||
CIRcvConnEvent event -> rcvConnEventToText event
|
||||
CISndConnEvent event -> sndConnEventToText event
|
||||
CIRcvChatFeature feature enabled -> chatFeatureToText feature <> ": " <> prefEnabledToText enabled
|
||||
CISndChatFeature feature enabled -> chatFeatureToText feature <> ": " <> prefEnabledToText enabled
|
||||
CIRcvChatFeatureRejected feature -> chatFeatureToText feature <> ": received, prohibited"
|
||||
|
||||
msgIntegrityError :: MsgErrorType -> Text
|
||||
msgIntegrityError = \case
|
||||
@@ -759,6 +765,9 @@ data JSONCIContent
|
||||
| JCISndGroupEvent {sndGroupEvent :: SndGroupEvent}
|
||||
| JCIRcvConnEvent {rcvConnEvent :: RcvConnEvent}
|
||||
| JCISndConnEvent {sndConnEvent :: SndConnEvent}
|
||||
| JCIRcvChatFeature {feature :: ChatFeature, enabled :: PrefEnabled}
|
||||
| JCISndChatFeature {feature :: ChatFeature, enabled :: PrefEnabled}
|
||||
| JCIRcvChatFeatureRejected {feature :: ChatFeature}
|
||||
deriving (Generic)
|
||||
|
||||
instance FromJSON JSONCIContent where
|
||||
@@ -783,6 +792,9 @@ jsonCIContent = \case
|
||||
CISndGroupEvent sndGroupEvent -> JCISndGroupEvent {sndGroupEvent}
|
||||
CIRcvConnEvent rcvConnEvent -> JCIRcvConnEvent {rcvConnEvent}
|
||||
CISndConnEvent sndConnEvent -> JCISndConnEvent {sndConnEvent}
|
||||
CIRcvChatFeature feature enabled -> JCIRcvChatFeature {feature, enabled}
|
||||
CISndChatFeature feature enabled -> JCISndChatFeature {feature, enabled}
|
||||
CIRcvChatFeatureRejected feature -> JCIRcvChatFeatureRejected {feature}
|
||||
|
||||
aciContentJSON :: JSONCIContent -> ACIContent
|
||||
aciContentJSON = \case
|
||||
@@ -799,6 +811,9 @@ aciContentJSON = \case
|
||||
JCISndGroupEvent {sndGroupEvent} -> ACIContent SMDSnd $ CISndGroupEvent sndGroupEvent
|
||||
JCIRcvConnEvent {rcvConnEvent} -> ACIContent SMDRcv $ CIRcvConnEvent rcvConnEvent
|
||||
JCISndConnEvent {sndConnEvent} -> ACIContent SMDSnd $ CISndConnEvent sndConnEvent
|
||||
JCIRcvChatFeature {feature, enabled} -> ACIContent SMDRcv $ CIRcvChatFeature feature enabled
|
||||
JCISndChatFeature {feature, enabled} -> ACIContent SMDSnd $ CISndChatFeature feature enabled
|
||||
JCIRcvChatFeatureRejected {feature} -> ACIContent SMDRcv $ CIRcvChatFeatureRejected feature
|
||||
|
||||
-- platform independent
|
||||
data DBJSONCIContent
|
||||
@@ -815,6 +830,9 @@ data DBJSONCIContent
|
||||
| DBJCISndGroupEvent {sndGroupEvent :: DBSndGroupEvent}
|
||||
| DBJCIRcvConnEvent {rcvConnEvent :: DBRcvConnEvent}
|
||||
| DBJCISndConnEvent {sndConnEvent :: DBSndConnEvent}
|
||||
| DBJCIRcvChatFeature {feature :: ChatFeature, enabled :: PrefEnabled}
|
||||
| DBJCISndChatFeature {feature :: ChatFeature, enabled :: PrefEnabled}
|
||||
| DBJCIRcvChatFeatureRejected {feature :: ChatFeature}
|
||||
deriving (Generic)
|
||||
|
||||
instance FromJSON DBJSONCIContent where
|
||||
@@ -839,6 +857,9 @@ dbJsonCIContent = \case
|
||||
CISndGroupEvent sge -> DBJCISndGroupEvent $ SGE sge
|
||||
CIRcvConnEvent rce -> DBJCIRcvConnEvent $ RCE rce
|
||||
CISndConnEvent sce -> DBJCISndConnEvent $ SCE sce
|
||||
CIRcvChatFeature feature enabled -> DBJCIRcvChatFeature {feature, enabled}
|
||||
CISndChatFeature feature enabled -> DBJCISndChatFeature {feature, enabled}
|
||||
CIRcvChatFeatureRejected feature -> DBJCIRcvChatFeatureRejected {feature}
|
||||
|
||||
aciContentDBJSON :: DBJSONCIContent -> ACIContent
|
||||
aciContentDBJSON = \case
|
||||
@@ -855,6 +876,9 @@ aciContentDBJSON = \case
|
||||
DBJCISndGroupEvent (SGE sge) -> ACIContent SMDSnd $ CISndGroupEvent sge
|
||||
DBJCIRcvConnEvent (RCE rce) -> ACIContent SMDRcv $ CIRcvConnEvent rce
|
||||
DBJCISndConnEvent (SCE sce) -> ACIContent SMDSnd $ CISndConnEvent sce
|
||||
DBJCIRcvChatFeature {feature, enabled} -> ACIContent SMDRcv $ CIRcvChatFeature feature enabled
|
||||
DBJCISndChatFeature {feature, enabled} -> ACIContent SMDSnd $ CISndChatFeature feature enabled
|
||||
DBJCIRcvChatFeatureRejected {feature} -> ACIContent SMDRcv $ CIRcvChatFeatureRejected feature
|
||||
|
||||
data CICallStatus
|
||||
= CISCallPending
|
||||
|
||||
@@ -43,7 +43,7 @@ import Database.SQLite.Simple.ToField (ToField (..))
|
||||
import GHC.Generics (Generic)
|
||||
import Simplex.Messaging.Agent.Protocol (ACommandTag (..), ACorrId, AParty (..), ConnId, ConnectionMode (..), ConnectionRequestUri, InvitationId)
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.Parsers (dropPrefix, fromTextField_, sumTypeJSON, taggedObjectJSON)
|
||||
import Simplex.Messaging.Parsers (dropPrefix, enumJSON, fromTextField_, sumTypeJSON, taggedObjectJSON)
|
||||
import Simplex.Messaging.Protocol (SMPServerWithAuth)
|
||||
import Simplex.Messaging.Util (safeDecodeUtf8, (<$?>))
|
||||
|
||||
@@ -51,16 +51,27 @@ class IsContact a where
|
||||
contactId' :: a -> ContactId
|
||||
profile' :: a -> LocalProfile
|
||||
localDisplayName' :: a -> ContactName
|
||||
preferences' :: a -> Maybe Preferences
|
||||
|
||||
instance IsContact User where
|
||||
contactId' = userContactId
|
||||
{-# INLINE contactId' #-}
|
||||
profile' = profile
|
||||
{-# INLINE profile' #-}
|
||||
localDisplayName' = localDisplayName
|
||||
{-# INLINE localDisplayName' #-}
|
||||
preferences' User {profile = LocalProfile {preferences}} = preferences
|
||||
{-# INLINE preferences' #-}
|
||||
|
||||
instance IsContact Contact where
|
||||
contactId' = contactId
|
||||
{-# INLINE contactId' #-}
|
||||
profile' = profile
|
||||
{-# INLINE profile' #-}
|
||||
localDisplayName' = localDisplayName
|
||||
{-# INLINE localDisplayName' #-}
|
||||
preferences' Contact {profile = LocalProfile {preferences}} = preferences
|
||||
{-# INLINE preferences' #-}
|
||||
|
||||
data User = User
|
||||
{ userId :: UserId,
|
||||
@@ -239,6 +250,19 @@ data ChatFeature
|
||||
= CFFullDelete
|
||||
| -- | CFReceipts
|
||||
CFVoice
|
||||
deriving (Show, Generic)
|
||||
|
||||
chatFeatureToText :: ChatFeature -> Text
|
||||
chatFeatureToText = \case
|
||||
CFFullDelete -> "Full deletion"
|
||||
CFVoice -> "Voice messages"
|
||||
|
||||
instance ToJSON ChatFeature where
|
||||
toEncoding = J.genericToEncoding . enumJSON $ dropPrefix "CF"
|
||||
toJSON = J.genericToJSON . enumJSON $ dropPrefix "CF"
|
||||
|
||||
instance FromJSON ChatFeature where
|
||||
parseJSON = J.genericParseJSON . enumJSON $ dropPrefix "CF"
|
||||
|
||||
allChatFeatures :: [ChatFeature]
|
||||
allChatFeatures =
|
||||
@@ -259,17 +283,6 @@ chatPrefName = \case
|
||||
-- CFReceipts -> "delivery receipts"
|
||||
CFVoice -> "voice messages"
|
||||
|
||||
class HasPreferences p where
|
||||
preferences' :: p -> Maybe Preferences
|
||||
|
||||
instance HasPreferences User where
|
||||
preferences' User {profile = LocalProfile {preferences}} = preferences
|
||||
{-# INLINE preferences' #-}
|
||||
|
||||
instance HasPreferences Contact where
|
||||
preferences' Contact {profile = LocalProfile {preferences}} = preferences
|
||||
{-# INLINE preferences' #-}
|
||||
|
||||
class PreferenceI p where
|
||||
getPreference :: ChatFeature -> p -> Preference
|
||||
|
||||
@@ -515,7 +528,7 @@ mergeGroupPreferences groupPreferences =
|
||||
pref pt = fromMaybe (getGroupPreference pt defaultGroupPrefs) (groupPreferences >>= groupPrefSel pt)
|
||||
|
||||
data PrefEnabled = PrefEnabled {forUser :: Bool, forContact :: Bool}
|
||||
deriving (Eq, Show, Generic)
|
||||
deriving (Eq, Show, Generic, FromJSON)
|
||||
|
||||
instance ToJSON PrefEnabled where
|
||||
toJSON = J.genericToJSON J.defaultOptions
|
||||
@@ -529,6 +542,17 @@ prefEnabled Preference {allow = user} Preference {allow = contact} = case (user,
|
||||
(FANo, _) -> PrefEnabled False False
|
||||
_ -> PrefEnabled True True
|
||||
|
||||
prefEnabledToText :: PrefEnabled -> Text
|
||||
prefEnabledToText = \case
|
||||
PrefEnabled True True -> "enabled"
|
||||
PrefEnabled False False -> "off"
|
||||
PrefEnabled {forUser = True, forContact = False} -> "enabled for you"
|
||||
PrefEnabled {forUser = False, forContact = True} -> "enabled for contact"
|
||||
|
||||
contactUserPreferences' :: User -> Contact -> ContactUserPreferences
|
||||
contactUserPreferences' user ct =
|
||||
contactUserPreferences user (userPreferences ct) (preferences' ct) (contactConnIncognito ct)
|
||||
|
||||
contactUserPreferences :: User -> Preferences -> Maybe Preferences -> Bool -> ContactUserPreferences
|
||||
contactUserPreferences user userPreferences contactPreferences connectedIncognito =
|
||||
ContactUserPreferences
|
||||
|
||||
@@ -745,7 +745,7 @@ viewContactPreferences user ct ct' cups =
|
||||
viewContactPref :: FullPreferences -> FullPreferences -> Maybe Preferences -> ContactUserPreferences -> ChatFeature -> Maybe StyledString
|
||||
viewContactPref userPrefs userPrefs' ctPrefs cups pt
|
||||
| userPref == userPref' && ctPref == contactPreference = Nothing
|
||||
| otherwise = Just $ plain (chatPrefName pt) <> ": " <> viewPrefEnabled enabled <> " (you allow: " <> viewCountactUserPref userPreference <> ", contact allows: " <> viewPreference contactPreference <> ")"
|
||||
| otherwise = Just $ plain (chatPrefName pt) <> ": " <> plain (prefEnabledToText enabled) <> " (you allow: " <> viewCountactUserPref userPreference <> ", contact allows: " <> viewPreference contactPreference <> ")"
|
||||
where
|
||||
userPref = getPreference pt userPrefs
|
||||
userPref' = getPreference pt userPrefs'
|
||||
@@ -776,13 +776,6 @@ viewCountactUserPref = \case
|
||||
CUPUser p -> "default (" <> viewPreference p <> ")"
|
||||
CUPContact p -> viewPreference p
|
||||
|
||||
viewPrefEnabled :: PrefEnabled -> StyledString
|
||||
viewPrefEnabled = \case
|
||||
PrefEnabled True True -> "enabled"
|
||||
PrefEnabled False False -> "off"
|
||||
PrefEnabled {forUser = True, forContact = False} -> "enabled for you"
|
||||
PrefEnabled {forUser = False, forContact = True} -> "enabled for contact"
|
||||
|
||||
viewGroupUpdated :: GroupInfo -> GroupInfo -> Maybe GroupMember -> [StyledString]
|
||||
viewGroupUpdated
|
||||
GroupInfo {localDisplayName = n, groupProfile = GroupProfile {fullName, image, groupPreferences = gps}}
|
||||
|
||||
@@ -260,23 +260,23 @@ testAddContact = versionTestMatrix2 runTestAddContact
|
||||
bob #$> ("/clear alice", id, "alice: all messages are removed locally ONLY")
|
||||
bob #$> ("/_get chat @2 count=100", chat, [])
|
||||
chatsEmpty alice bob = do
|
||||
alice @@@ [("@bob", "")]
|
||||
alice #$> ("/_get chat @2 count=100", chat, [])
|
||||
bob @@@ [("@alice", "")]
|
||||
bob #$> ("/_get chat @2 count=100", chat, [])
|
||||
alice @@@ [("@bob", "Voice messages: enabled")]
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures)
|
||||
bob @@@ [("@alice", "Voice messages: enabled")]
|
||||
bob #$> ("/_get chat @2 count=100", chat, chatFeatures)
|
||||
chatsOneMessage alice bob = do
|
||||
alice @@@ [("@bob", "hello there 🙂")]
|
||||
alice #$> ("/_get chat @2 count=100", chat, [(1, "hello there 🙂")])
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "hello there 🙂")])
|
||||
bob @@@ [("@alice", "hello there 🙂")]
|
||||
bob #$> ("/_get chat @2 count=100", chat, [(0, "hello there 🙂")])
|
||||
bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "hello there 🙂")])
|
||||
chatsManyMessages alice bob = do
|
||||
alice @@@ [("@bob", "how are you?")]
|
||||
alice #$> ("/_get chat @2 count=100", chat, [(1, "hello there 🙂"), (0, "hello there"), (0, "how are you?")])
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "hello there 🙂"), (0, "hello there"), (0, "how are you?")])
|
||||
bob @@@ [("@alice", "how are you?")]
|
||||
bob #$> ("/_get chat @2 count=100", chat, [(0, "hello there 🙂"), (1, "hello there"), (1, "how are you?")])
|
||||
bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "hello there 🙂"), (1, "hello there"), (1, "how are you?")])
|
||||
-- pagination
|
||||
alice #$> ("/_get chat @2 after=1 count=100", chat, [(0, "hello there"), (0, "how are you?")])
|
||||
alice #$> ("/_get chat @2 before=2 count=100", chat, [(1, "hello there 🙂")])
|
||||
alice #$> ("/_get chat @2 after=" <> itemId 1 <> " count=100", chat, [(0, "hello there"), (0, "how are you?")])
|
||||
alice #$> ("/_get chat @2 before=" <> itemId 2 <> " count=100", chat, chatFeatures <> [(1, "hello there 🙂")])
|
||||
-- search
|
||||
alice #$> ("/_get chat @2 count=100 search=ello ther", chat, [(1, "hello there 🙂"), (0, "hello there")])
|
||||
-- read messages
|
||||
@@ -327,14 +327,14 @@ testDirectMessageUpdate =
|
||||
alice <# "bob> > hello 🙂"
|
||||
alice <## " hi alice"
|
||||
|
||||
alice #$> ("/_get chat @2 count=100", chat', [((1, "hello 🙂"), Nothing), ((0, "hi alice"), Just (1, "hello 🙂"))])
|
||||
bob #$> ("/_get chat @2 count=100", chat', [((0, "hello 🙂"), Nothing), ((1, "hi alice"), Just (0, "hello 🙂"))])
|
||||
alice #$> ("/_get chat @2 count=100", chat', chatFeatures' <> [((1, "hello 🙂"), Nothing), ((0, "hi alice"), Just (1, "hello 🙂"))])
|
||||
bob #$> ("/_get chat @2 count=100", chat', chatFeatures' <> [((0, "hello 🙂"), Nothing), ((1, "hi alice"), Just (0, "hello 🙂"))])
|
||||
|
||||
alice #$> ("/_update item @2 1 text hey 👋", id, "message updated")
|
||||
alice #$> ("/_update item @2 " <> itemId 1 <> " text hey 👋", id, "message updated")
|
||||
bob <# "alice> [edited] hey 👋"
|
||||
|
||||
alice #$> ("/_get chat @2 count=100", chat', [((1, "hey 👋"), Nothing), ((0, "hi alice"), Just (1, "hello 🙂"))])
|
||||
bob #$> ("/_get chat @2 count=100", chat', [((0, "hey 👋"), Nothing), ((1, "hi alice"), Just (0, "hello 🙂"))])
|
||||
alice #$> ("/_get chat @2 count=100", chat', chatFeatures' <> [((1, "hey 👋"), Nothing), ((0, "hi alice"), Just (1, "hello 🙂"))])
|
||||
bob #$> ("/_get chat @2 count=100", chat', chatFeatures' <> [((0, "hey 👋"), Nothing), ((1, "hi alice"), Just (0, "hello 🙂"))])
|
||||
|
||||
-- msg id 3
|
||||
bob `send` "> @alice (hey) hey alice"
|
||||
@@ -343,27 +343,27 @@ testDirectMessageUpdate =
|
||||
alice <# "bob> > hey 👋"
|
||||
alice <## " hey alice"
|
||||
|
||||
alice #$> ("/_get chat @2 count=100", chat', [((1, "hey 👋"), Nothing), ((0, "hi alice"), Just (1, "hello 🙂")), ((0, "hey alice"), Just (1, "hey 👋"))])
|
||||
bob #$> ("/_get chat @2 count=100", chat', [((0, "hey 👋"), Nothing), ((1, "hi alice"), Just (0, "hello 🙂")), ((1, "hey alice"), Just (0, "hey 👋"))])
|
||||
alice #$> ("/_get chat @2 count=100", chat', chatFeatures' <> [((1, "hey 👋"), Nothing), ((0, "hi alice"), Just (1, "hello 🙂")), ((0, "hey alice"), Just (1, "hey 👋"))])
|
||||
bob #$> ("/_get chat @2 count=100", chat', chatFeatures' <> [((0, "hey 👋"), Nothing), ((1, "hi alice"), Just (0, "hello 🙂")), ((1, "hey alice"), Just (0, "hey 👋"))])
|
||||
|
||||
alice #$> ("/_update item @2 1 text greetings 🤝", id, "message updated")
|
||||
alice #$> ("/_update item @2 " <> itemId 1 <> " text greetings 🤝", id, "message updated")
|
||||
bob <# "alice> [edited] greetings 🤝"
|
||||
|
||||
alice #$> ("/_update item @2 2 text updating bob's message", id, "cannot update this item")
|
||||
alice #$> ("/_update item @2 " <> itemId 2 <> " text updating bob's message", id, "cannot update this item")
|
||||
|
||||
alice #$> ("/_get chat @2 count=100", chat', [((1, "greetings 🤝"), Nothing), ((0, "hi alice"), Just (1, "hello 🙂")), ((0, "hey alice"), Just (1, "hey 👋"))])
|
||||
bob #$> ("/_get chat @2 count=100", chat', [((0, "greetings 🤝"), Nothing), ((1, "hi alice"), Just (0, "hello 🙂")), ((1, "hey alice"), Just (0, "hey 👋"))])
|
||||
alice #$> ("/_get chat @2 count=100", chat', chatFeatures' <> [((1, "greetings 🤝"), Nothing), ((0, "hi alice"), Just (1, "hello 🙂")), ((0, "hey alice"), Just (1, "hey 👋"))])
|
||||
bob #$> ("/_get chat @2 count=100", chat', chatFeatures' <> [((0, "greetings 🤝"), Nothing), ((1, "hi alice"), Just (0, "hello 🙂")), ((1, "hey alice"), Just (0, "hey 👋"))])
|
||||
|
||||
bob #$> ("/_update item @2 2 text hey Alice", id, "message updated")
|
||||
bob #$> ("/_update item @2 " <> itemId 2 <> " text hey Alice", id, "message updated")
|
||||
alice <# "bob> [edited] > hello 🙂"
|
||||
alice <## " hey Alice"
|
||||
|
||||
bob #$> ("/_update item @2 3 text greetings Alice", id, "message updated")
|
||||
bob #$> ("/_update item @2 " <> itemId 3 <> " text greetings Alice", id, "message updated")
|
||||
alice <# "bob> [edited] > hey 👋"
|
||||
alice <## " greetings Alice"
|
||||
|
||||
alice #$> ("/_get chat @2 count=100", chat', [((1, "greetings 🤝"), Nothing), ((0, "hey Alice"), Just (1, "hello 🙂")), ((0, "greetings Alice"), Just (1, "hey 👋"))])
|
||||
bob #$> ("/_get chat @2 count=100", chat', [((0, "greetings 🤝"), Nothing), ((1, "hey Alice"), Just (0, "hello 🙂")), ((1, "greetings Alice"), Just (0, "hey 👋"))])
|
||||
alice #$> ("/_get chat @2 count=100", chat', chatFeatures' <> [((1, "greetings 🤝"), Nothing), ((0, "hey Alice"), Just (1, "hello 🙂")), ((0, "greetings Alice"), Just (1, "hey 👋"))])
|
||||
bob #$> ("/_get chat @2 count=100", chat', chatFeatures' <> [((0, "greetings 🤝"), Nothing), ((1, "hey Alice"), Just (0, "hello 🙂")), ((1, "greetings Alice"), Just (0, "hey 👋"))])
|
||||
|
||||
testDirectMessageDelete :: IO ()
|
||||
testDirectMessageDelete =
|
||||
@@ -383,28 +383,28 @@ testDirectMessageDelete =
|
||||
alice <## " hey alic"
|
||||
|
||||
-- alice: deletes msg ids 1,2
|
||||
alice #$> ("/_delete item @2 1 internal", id, "message deleted")
|
||||
alice #$> ("/_delete item @2 2 internal", id, "message deleted")
|
||||
alice #$> ("/_delete item @2 " <> itemId 1 <> " internal", id, "message deleted")
|
||||
alice #$> ("/_delete item @2 " <> itemId 2 <> " internal", id, "message deleted")
|
||||
|
||||
alice @@@ [("@bob", "")]
|
||||
alice #$> ("/_get chat @2 count=100", chat, [])
|
||||
alice @@@ [("@bob", "Voice messages: enabled")]
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures)
|
||||
|
||||
-- alice: msg id 1
|
||||
bob #$> ("/_update item @2 2 text hey alice", id, "message updated")
|
||||
bob #$> ("/_update item @2 " <> itemId 2 <> " text hey alice", id, "message updated")
|
||||
alice <# "bob> [edited] hey alice"
|
||||
alice @@@ [("@bob", "hey alice")]
|
||||
alice #$> ("/_get chat @2 count=100", chat, [(0, "hey alice")])
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "hey alice")])
|
||||
|
||||
-- bob: deletes msg id 2
|
||||
bob #$> ("/_delete item @2 2 broadcast", id, "message deleted")
|
||||
bob #$> ("/_delete item @2 " <> itemId 2 <> " broadcast", id, "message deleted")
|
||||
alice <# "bob> [deleted] hey alice"
|
||||
alice @@@ [("@bob", "this item is deleted (broadcast)")]
|
||||
alice #$> ("/_get chat @2 count=100", chat, [(0, "this item is deleted (broadcast)")])
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "this item is deleted (broadcast)")])
|
||||
|
||||
-- alice: deletes msg id 1 that was broadcast deleted by bob
|
||||
alice #$> ("/_delete item @2 1 internal", id, "message deleted")
|
||||
alice @@@ [("@bob", "")]
|
||||
alice #$> ("/_get chat @2 count=100", chat, [])
|
||||
alice #$> ("/_delete item @2 " <> itemId 1 <> " internal", id, "message deleted")
|
||||
alice @@@ [("@bob", "Voice messages: enabled")]
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures)
|
||||
|
||||
-- alice: msg id 1, bob: msg id 2 (quoting message alice deleted locally)
|
||||
bob `send` "> @alice (hello 🙂) do you receive my messages?"
|
||||
@@ -413,24 +413,24 @@ testDirectMessageDelete =
|
||||
alice <# "bob> > hello 🙂"
|
||||
alice <## " do you receive my messages?"
|
||||
alice @@@ [("@bob", "do you receive my messages?")]
|
||||
alice #$> ("/_get chat @2 count=100", chat', [((0, "do you receive my messages?"), Just (1, "hello 🙂"))])
|
||||
alice #$> ("/_delete item @2 1 broadcast", id, "cannot delete this item")
|
||||
alice #$> ("/_get chat @2 count=100", chat', chatFeatures' <> [((0, "do you receive my messages?"), Just (1, "hello 🙂"))])
|
||||
alice #$> ("/_delete item @2 " <> itemId 1 <> " broadcast", id, "cannot delete this item")
|
||||
|
||||
-- alice: msg id 2, bob: msg id 3
|
||||
bob #> "@alice how are you?"
|
||||
alice <# "bob> how are you?"
|
||||
|
||||
-- alice: deletes msg id 2
|
||||
alice #$> ("/_delete item @2 2 internal", id, "message deleted")
|
||||
alice #$> ("/_delete item @2 " <> itemId 2 <> " internal", id, "message deleted")
|
||||
|
||||
-- bob: deletes msg id 3 (that alice deleted locally)
|
||||
bob #$> ("/_delete item @2 3 broadcast", id, "message deleted")
|
||||
bob #$> ("/_delete item @2 " <> itemId 3 <> " broadcast", id, "message deleted")
|
||||
alice <## "bob> [deleted - original message not found]"
|
||||
|
||||
alice @@@ [("@bob", "do you receive my messages?")]
|
||||
alice #$> ("/_get chat @2 count=100", chat', [((0, "do you receive my messages?"), Just (1, "hello 🙂"))])
|
||||
alice #$> ("/_get chat @2 count=100", chat', chatFeatures' <> [((0, "do you receive my messages?"), Just (1, "hello 🙂"))])
|
||||
bob @@@ [("@alice", "do you receive my messages?")]
|
||||
bob #$> ("/_get chat @2 count=100", chat', [((0, "hello 🙂"), Nothing), ((1, "do you receive my messages?"), Just (0, "hello 🙂"))])
|
||||
bob #$> ("/_get chat @2 count=100", chat', chatFeatures' <> [((0, "hello 🙂"), Nothing), ((1, "do you receive my messages?"), Just (0, "hello 🙂"))])
|
||||
|
||||
testGroup :: Spec
|
||||
testGroup = versionTestMatrix3 runTestGroup
|
||||
@@ -557,8 +557,8 @@ testGroupShared alice bob cath checkMessages = do
|
||||
alice #$> ("/_get chat #1 count=100", chat, [(0, "connected"), (0, "connected"), (1, "hello"), (0, "hi there"), (0, "hey team")])
|
||||
-- "before" and "after" define a chat item id across all chats,
|
||||
-- so we take into account group event items as well as sent group invitations in direct chats
|
||||
alice #$> ("/_get chat #1 after=5 count=100", chat, [(0, "hi there"), (0, "hey team")])
|
||||
alice #$> ("/_get chat #1 before=7 count=100", chat, [(0, "connected"), (0, "connected"), (1, "hello"), (0, "hi there")])
|
||||
alice #$> ("/_get chat #1 after=" <> groupItemId 2 5 <> " count=100", chat, [(0, "hi there"), (0, "hey team")])
|
||||
alice #$> ("/_get chat #1 before=" <> groupItemId 2 7 <> " count=100", chat, [(0, "connected"), (0, "connected"), (1, "hello"), (0, "hi there")])
|
||||
alice #$> ("/_get chat #1 count=100 search=team", chat, [(0, "hey team")])
|
||||
bob @@@ [("@cath", "hey"), ("#team", "hey team"), ("@alice", "received invitation to join group team as admin")]
|
||||
bob #$> ("/_get chat #1 count=100", chat, [(0, "connected"), (0, "added cath (Catherine)"), (0, "connected"), (0, "hello"), (1, "hi there"), (0, "hey team")])
|
||||
@@ -683,7 +683,7 @@ testGroup2 =
|
||||
<##? [ "dan> hi",
|
||||
"@dan hey"
|
||||
]
|
||||
alice ##> "/t 12"
|
||||
alice ##> "/t 18"
|
||||
alice
|
||||
<##? [ "@bob sent invitation to join group club as admin",
|
||||
"@cath sent invitation to join group club as admin",
|
||||
@@ -696,7 +696,13 @@ testGroup2 =
|
||||
"#club cath> hey",
|
||||
"#club dan> how is it going?",
|
||||
"dan> hi",
|
||||
"@dan hey"
|
||||
"@dan hey",
|
||||
"dan> Full deletion: off",
|
||||
"dan> Voice messages: enabled",
|
||||
"bob> Full deletion: off",
|
||||
"bob> Voice messages: enabled",
|
||||
"cath> Full deletion: off",
|
||||
"cath> Voice messages: enabled"
|
||||
]
|
||||
-- remove member
|
||||
cath ##> "/rm club dan"
|
||||
@@ -1157,7 +1163,7 @@ testGroupMessageUpdate =
|
||||
(bob <# "#team alice> hello!")
|
||||
(cath <# "#team alice> hello!")
|
||||
|
||||
alice #$> ("/_update item #1 5 text hey 👋", id, "message updated")
|
||||
alice #$> ("/_update item #1 " <> groupItemId 2 5 <> " text hey 👋", id, "message updated")
|
||||
concurrently_
|
||||
(bob <# "#team alice> [edited] hey 👋")
|
||||
(cath <# "#team alice> [edited] hey 👋")
|
||||
@@ -1185,12 +1191,12 @@ testGroupMessageUpdate =
|
||||
bob #$> ("/_get chat #1 count=2", chat', [((0, "hey 👋"), Nothing), ((1, "hi alice"), Just (0, "hey 👋"))])
|
||||
cath #$> ("/_get chat #1 count=2", chat', [((0, "hey 👋"), Nothing), ((0, "hi alice"), Just (0, "hey 👋"))])
|
||||
|
||||
alice #$> ("/_update item #1 5 text greetings 🤝", id, "message updated")
|
||||
alice #$> ("/_update item #1 " <> groupItemId 2 5 <> " text greetings 🤝", id, "message updated")
|
||||
concurrently_
|
||||
(bob <# "#team alice> [edited] greetings 🤝")
|
||||
(cath <# "#team alice> [edited] greetings 🤝")
|
||||
|
||||
alice #$> ("/_update item #1 6 text updating bob's message", id, "cannot update this item")
|
||||
alice #$> ("/_update item #1 " <> groupItemId 2 6 <> " text updating bob's message", id, "cannot update this item")
|
||||
|
||||
threadDelay 1000000
|
||||
cath `send` "> #team @alice (greetings) greetings!"
|
||||
@@ -1223,7 +1229,7 @@ testGroupMessageDelete =
|
||||
(cath <# "#team alice> hello!")
|
||||
|
||||
-- alice: deletes msg id 5
|
||||
alice #$> ("/_delete item #1 5 internal", id, "message deleted")
|
||||
alice #$> ("/_delete item #1 " <> groupItemId 2 5 <> " internal", id, "message deleted")
|
||||
|
||||
alice #$> ("/_get chat #1 count=1", chat, [(0, "connected")])
|
||||
bob #$> ("/_get chat #1 count=1", chat, [(0, "hello!")])
|
||||
@@ -1249,14 +1255,14 @@ testGroupMessageDelete =
|
||||
cath #$> ("/_get chat #1 count=2", chat', [((0, "hello!"), Nothing), ((0, "hi alic"), Just (0, "hello!"))])
|
||||
|
||||
-- alice: deletes msg id 5
|
||||
alice #$> ("/_delete item #1 5 internal", id, "message deleted")
|
||||
alice #$> ("/_delete item #1 " <> groupItemId 2 5 <> " internal", id, "message deleted")
|
||||
|
||||
alice #$> ("/_get chat #1 count=1", chat', [((0, "connected"), Nothing)])
|
||||
bob #$> ("/_get chat #1 count=2", chat', [((0, "hello!"), Nothing), ((1, "hi alic"), Just (0, "hello!"))])
|
||||
cath #$> ("/_get chat #1 count=2", chat', [((0, "hello!"), Nothing), ((0, "hi alic"), Just (0, "hello!"))])
|
||||
|
||||
-- alice: msg id 5
|
||||
bob #$> ("/_update item #1 6 text hi alice", id, "message updated")
|
||||
bob #$> ("/_update item #1 " <> groupItemId 1 6 <> " text hi alice", id, "message updated")
|
||||
concurrently_
|
||||
(alice <# "#team bob> [edited] hi alice")
|
||||
( do
|
||||
@@ -1275,13 +1281,13 @@ testGroupMessageDelete =
|
||||
(alice <# "#team cath> how are you?")
|
||||
(bob <# "#team cath> how are you?")
|
||||
|
||||
cath #$> ("/_delete item #1 6 broadcast", id, "message deleted")
|
||||
cath #$> ("/_delete item #1 " <> groupItemId 1 6 <> " broadcast", id, "message deleted")
|
||||
concurrently_
|
||||
(alice <# "#team cath> [deleted] how are you?")
|
||||
(bob <# "#team cath> [deleted] how are you?")
|
||||
|
||||
alice #$> ("/_delete item #1 5 broadcast", id, "cannot delete this item")
|
||||
alice #$> ("/_delete item #1 5 internal", id, "message deleted")
|
||||
alice #$> ("/_delete item #1 " <> groupItemId 2 5 <> " broadcast", id, "cannot delete this item")
|
||||
alice #$> ("/_delete item #1 " <> groupItemId 2 5 <> " internal", id, "message deleted")
|
||||
|
||||
alice #$> ("/_get chat #1 count=1", chat', [((0, "this item is deleted (broadcast)"), Nothing)])
|
||||
bob #$> ("/_get chat #1 count=3", chat', [((0, "hello!"), Nothing), ((1, "hi alice"), Just (0, "hello!")), ((0, "this item is deleted (broadcast)"), Nothing)])
|
||||
@@ -1839,8 +1845,8 @@ runTestMessageWithFile alice bob = do
|
||||
src <- B.readFile "./tests/fixtures/test.jpg"
|
||||
dest <- B.readFile "./tests/tmp/test.jpg"
|
||||
dest `shouldBe` src
|
||||
alice #$> ("/_get chat @2 count=100", chatF, [((1, "hi, sending a file"), Just "./tests/fixtures/test.jpg")])
|
||||
bob #$> ("/_get chat @2 count=100", chatF, [((0, "hi, sending a file"), Just "./tests/tmp/test.jpg")])
|
||||
alice #$> ("/_get chat @2 count=100", chatF, chatFeaturesF <> [((1, "hi, sending a file"), Just "./tests/fixtures/test.jpg")])
|
||||
bob #$> ("/_get chat @2 count=100", chatF, chatFeaturesF <> [((0, "hi, sending a file"), Just "./tests/tmp/test.jpg")])
|
||||
|
||||
testSendImage :: IO ()
|
||||
testSendImage =
|
||||
@@ -1863,8 +1869,8 @@ testSendImage =
|
||||
src <- B.readFile "./tests/fixtures/test.jpg"
|
||||
dest <- B.readFile "./tests/tmp/test.jpg"
|
||||
dest `shouldBe` src
|
||||
alice #$> ("/_get chat @2 count=100", chatF, [((1, ""), Just "./tests/fixtures/test.jpg")])
|
||||
bob #$> ("/_get chat @2 count=100", chatF, [((0, ""), Just "./tests/tmp/test.jpg")])
|
||||
alice #$> ("/_get chat @2 count=100", chatF, chatFeaturesF <> [((1, ""), Just "./tests/fixtures/test.jpg")])
|
||||
bob #$> ("/_get chat @2 count=100", chatF, chatFeaturesF <> [((0, ""), Just "./tests/tmp/test.jpg")])
|
||||
-- deleting contact without files folder set should not remove file
|
||||
bob ##> "/d alice"
|
||||
bob <## "alice: contact is deleted"
|
||||
@@ -1894,8 +1900,8 @@ testFilesFoldersSendImage =
|
||||
src <- B.readFile "./tests/fixtures/test.jpg"
|
||||
dest <- B.readFile "./tests/tmp/app_files/test.jpg"
|
||||
dest `shouldBe` src
|
||||
alice #$> ("/_get chat @2 count=100", chatF, [((1, ""), Just "test.jpg")])
|
||||
bob #$> ("/_get chat @2 count=100", chatF, [((0, ""), Just "test.jpg")])
|
||||
alice #$> ("/_get chat @2 count=100", chatF, chatFeaturesF <> [((1, ""), Just "test.jpg")])
|
||||
bob #$> ("/_get chat @2 count=100", chatF, chatFeaturesF <> [((0, ""), Just "test.jpg")])
|
||||
-- deleting contact with files folder set should remove file
|
||||
checkActionDeletesFile "./tests/tmp/app_files/test.jpg" $ do
|
||||
bob ##> "/d alice"
|
||||
@@ -1964,7 +1970,7 @@ testSendImageWithTextAndQuote =
|
||||
connectUsers alice bob
|
||||
bob #> "@alice hi alice"
|
||||
alice <# "bob> hi alice"
|
||||
alice ##> "/_send @2 json {\"filePath\": \"./tests/fixtures/test.jpg\", \"quotedItemId\": 1, \"msgContent\": {\"text\":\"hey bob\",\"type\":\"image\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\"}}"
|
||||
alice ##> ("/_send @2 json {\"filePath\": \"./tests/fixtures/test.jpg\", \"quotedItemId\": " <> itemId 1 <> ", \"msgContent\": {\"text\":\"hey bob\",\"type\":\"image\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\"}}")
|
||||
alice <# "@bob > hi alice"
|
||||
alice <## " hey bob"
|
||||
alice <# "/f @bob ./tests/fixtures/test.jpg"
|
||||
@@ -1983,12 +1989,12 @@ testSendImageWithTextAndQuote =
|
||||
(alice <## "completed sending file 1 (test.jpg) to bob")
|
||||
src <- B.readFile "./tests/fixtures/test.jpg"
|
||||
B.readFile "./tests/tmp/test.jpg" `shouldReturn` src
|
||||
alice #$> ("/_get chat @2 count=100", chat'', [((0, "hi alice"), Nothing, Nothing), ((1, "hey bob"), Just (0, "hi alice"), Just "./tests/fixtures/test.jpg")])
|
||||
alice #$> ("/_get chat @2 count=100", chat'', chatFeatures'' <> [((0, "hi alice"), Nothing, Nothing), ((1, "hey bob"), Just (0, "hi alice"), Just "./tests/fixtures/test.jpg")])
|
||||
alice @@@ [("@bob", "hey bob")]
|
||||
bob #$> ("/_get chat @2 count=100", chat'', [((1, "hi alice"), Nothing, Nothing), ((0, "hey bob"), Just (1, "hi alice"), Just "./tests/tmp/test.jpg")])
|
||||
bob #$> ("/_get chat @2 count=100", chat'', chatFeatures'' <> [((1, "hi alice"), Nothing, Nothing), ((0, "hey bob"), Just (1, "hi alice"), Just "./tests/tmp/test.jpg")])
|
||||
bob @@@ [("@alice", "hey bob")]
|
||||
-- quoting (file + text) with file uses quoted text
|
||||
bob ##> "/_send @2 json {\"filePath\": \"./tests/fixtures/test.txt\", \"quotedItemId\": 2, \"msgContent\": {\"text\":\"\",\"type\":\"file\"}}"
|
||||
bob ##> ("/_send @2 json {\"filePath\": \"./tests/fixtures/test.txt\", \"quotedItemId\": " <> itemId 2 <> ", \"msgContent\": {\"text\":\"\",\"type\":\"file\"}}")
|
||||
bob <# "@alice > hey bob"
|
||||
bob <## " test.txt"
|
||||
bob <# "/f @alice ./tests/fixtures/test.txt"
|
||||
@@ -2008,7 +2014,7 @@ testSendImageWithTextAndQuote =
|
||||
txtSrc <- B.readFile "./tests/fixtures/test.txt"
|
||||
B.readFile "./tests/tmp/test.txt" `shouldReturn` txtSrc
|
||||
-- quoting (file without text) with file uses file name
|
||||
alice ##> "/_send @2 json {\"filePath\": \"./tests/fixtures/test.jpg\", \"quotedItemId\": 3, \"msgContent\": {\"text\":\"\",\"type\":\"image\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\"}}"
|
||||
alice ##> ("/_send @2 json {\"filePath\": \"./tests/fixtures/test.jpg\", \"quotedItemId\": " <> itemId 3 <> ", \"msgContent\": {\"text\":\"\",\"type\":\"image\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\"}}")
|
||||
alice <# "@bob > test.txt"
|
||||
alice <## " test.jpg"
|
||||
alice <# "/f @bob ./tests/fixtures/test.jpg"
|
||||
@@ -2084,7 +2090,7 @@ testGroupSendImageWithTextAndQuote =
|
||||
(alice <# "#team bob> hi team")
|
||||
(cath <# "#team bob> hi team")
|
||||
threadDelay 1000000
|
||||
alice ##> "/_send #1 json {\"filePath\": \"./tests/fixtures/test.jpg\", \"quotedItemId\": 5, \"msgContent\": {\"text\":\"hey bob\",\"type\":\"image\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\"}}"
|
||||
alice ##> ("/_send #1 json {\"filePath\": \"./tests/fixtures/test.jpg\", \"quotedItemId\": " <> groupItemId 2 5 <> ", \"msgContent\": {\"text\":\"hey bob\",\"type\":\"image\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\"}}")
|
||||
alice <# "#team > bob hi team"
|
||||
alice <## " hey bob"
|
||||
alice <# "/f #team ./tests/fixtures/test.jpg"
|
||||
@@ -2145,7 +2151,7 @@ testUserContactLink = versionTestMatrix3 $ \alice bob cath -> do
|
||||
concurrently_
|
||||
(bob <## "alice (Alice): contact is connected")
|
||||
(alice <## "bob (Bob): contact is connected")
|
||||
alice @@@ [("@bob", "")]
|
||||
alice @@@ [("@bob", "Voice messages: enabled")]
|
||||
alice <##> bob
|
||||
|
||||
cath ##> ("/c " <> cLink)
|
||||
@@ -2156,7 +2162,7 @@ testUserContactLink = versionTestMatrix3 $ \alice bob cath -> do
|
||||
concurrently_
|
||||
(cath <## "alice (Alice): contact is connected")
|
||||
(alice <## "cath (Catherine): contact is connected")
|
||||
alice @@@ [("@cath", ""), ("@bob", "hey")]
|
||||
alice @@@ [("@cath", "Voice messages: enabled"), ("@bob", "hey")]
|
||||
alice <##> cath
|
||||
|
||||
testUserContactLinkAutoAccept :: IO ()
|
||||
@@ -2174,7 +2180,7 @@ testUserContactLinkAutoAccept =
|
||||
concurrently_
|
||||
(bob <## "alice (Alice): contact is connected")
|
||||
(alice <## "bob (Bob): contact is connected")
|
||||
alice @@@ [("@bob", "")]
|
||||
alice @@@ [("@bob", "Voice messages: enabled")]
|
||||
alice <##> bob
|
||||
|
||||
alice ##> "/auto_accept on"
|
||||
@@ -2186,7 +2192,7 @@ testUserContactLinkAutoAccept =
|
||||
concurrently_
|
||||
(cath <## "alice (Alice): contact is connected")
|
||||
(alice <## "cath (Catherine): contact is connected")
|
||||
alice @@@ [("@cath", ""), ("@bob", "hey")]
|
||||
alice @@@ [("@cath", "Voice messages: enabled"), ("@bob", "hey")]
|
||||
alice <##> cath
|
||||
|
||||
alice ##> "/auto_accept off"
|
||||
@@ -2200,7 +2206,7 @@ testUserContactLinkAutoAccept =
|
||||
concurrently_
|
||||
(dan <## "alice (Alice): contact is connected")
|
||||
(alice <## "dan (Daniel): contact is connected")
|
||||
alice @@@ [("@dan", ""), ("@cath", "hey"), ("@bob", "hey")]
|
||||
alice @@@ [("@dan", "Voice messages: enabled"), ("@cath", "hey"), ("@bob", "hey")]
|
||||
alice <##> dan
|
||||
|
||||
testDeduplicateContactRequests :: IO ()
|
||||
@@ -2229,8 +2235,8 @@ testDeduplicateContactRequests = testChat3 aliceProfile bobProfile cathProfile $
|
||||
|
||||
bob ##> ("/c " <> cLink)
|
||||
bob <## "alice (Alice): contact already exists"
|
||||
alice @@@ [("@bob", "")]
|
||||
bob @@@ [("@alice", ""), (":2", ""), (":1", "")]
|
||||
alice @@@ [("@bob", "Voice messages: enabled")]
|
||||
bob @@@ [("@alice", "Voice messages: enabled"), (":2", ""), (":1", "")]
|
||||
bob ##> "/_delete :1"
|
||||
bob <## "connection :1 deleted"
|
||||
bob ##> "/_delete :2"
|
||||
@@ -2244,8 +2250,8 @@ testDeduplicateContactRequests = testChat3 aliceProfile bobProfile cathProfile $
|
||||
bob <## "alice (Alice): contact already exists"
|
||||
|
||||
alice <##> bob
|
||||
alice #$> ("/_get chat @2 count=100", chat, [(1, "hi"), (0, "hey"), (1, "hi"), (0, "hey")])
|
||||
bob #$> ("/_get chat @2 count=100", chat, [(0, "hi"), (1, "hey"), (0, "hi"), (1, "hey")])
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "hi"), (0, "hey"), (1, "hi"), (0, "hey")])
|
||||
bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "hi"), (1, "hey"), (0, "hi"), (1, "hey")])
|
||||
|
||||
cath ##> ("/c " <> cLink)
|
||||
alice <#? cath
|
||||
@@ -2255,7 +2261,7 @@ testDeduplicateContactRequests = testChat3 aliceProfile bobProfile cathProfile $
|
||||
concurrently_
|
||||
(cath <## "alice (Alice): contact is connected")
|
||||
(alice <## "cath (Catherine): contact is connected")
|
||||
alice @@@ [("@cath", ""), ("@bob", "hey")]
|
||||
alice @@@ [("@cath", "Voice messages: enabled"), ("@bob", "hey")]
|
||||
alice <##> cath
|
||||
|
||||
testDeduplicateContactRequestsProfileChange :: IO ()
|
||||
@@ -2299,8 +2305,8 @@ testDeduplicateContactRequestsProfileChange = testChat3 aliceProfile bobProfile
|
||||
|
||||
bob ##> ("/c " <> cLink)
|
||||
bob <## "alice (Alice): contact already exists"
|
||||
alice @@@ [("@robert", "")]
|
||||
bob @@@ [("@alice", ""), (":3", ""), (":2", ""), (":1", "")]
|
||||
alice @@@ [("@robert", "Voice messages: enabled")]
|
||||
bob @@@ [("@alice", "Voice messages: enabled"), (":3", ""), (":2", ""), (":1", "")]
|
||||
bob ##> "/_delete :1"
|
||||
bob <## "connection :1 deleted"
|
||||
bob ##> "/_delete :2"
|
||||
@@ -2316,8 +2322,8 @@ testDeduplicateContactRequestsProfileChange = testChat3 aliceProfile bobProfile
|
||||
bob <## "alice (Alice): contact already exists"
|
||||
|
||||
alice <##> bob
|
||||
alice #$> ("/_get chat @2 count=100", chat, [(1, "hi"), (0, "hey"), (1, "hi"), (0, "hey")])
|
||||
bob #$> ("/_get chat @2 count=100", chat, [(0, "hi"), (1, "hey"), (0, "hi"), (1, "hey")])
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "hi"), (0, "hey"), (1, "hi"), (0, "hey")])
|
||||
bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "hi"), (1, "hey"), (0, "hi"), (1, "hey")])
|
||||
|
||||
cath ##> ("/c " <> cLink)
|
||||
alice <#? cath
|
||||
@@ -2327,7 +2333,7 @@ testDeduplicateContactRequestsProfileChange = testChat3 aliceProfile bobProfile
|
||||
concurrently_
|
||||
(cath <## "alice (Alice): contact is connected")
|
||||
(alice <## "cath (Catherine): contact is connected")
|
||||
alice @@@ [("@cath", ""), ("@robert", "hey")]
|
||||
alice @@@ [("@cath", "Voice messages: enabled"), ("@robert", "hey")]
|
||||
alice <##> cath
|
||||
|
||||
testRejectContactAndDeleteUserContact :: IO ()
|
||||
@@ -2827,7 +2833,7 @@ testSetConnectionAlias = testChat2 aliceProfile bobProfile $
|
||||
concurrently_
|
||||
(alice <## "bob (Bob): contact is connected")
|
||||
(bob <## "alice (Alice): contact is connected")
|
||||
alice @@@ [("@bob", "")]
|
||||
alice @@@ [("@bob", "Voice messages: enabled")]
|
||||
alice ##> "/cs"
|
||||
alice <## "bob (Bob) (alias: friend)"
|
||||
|
||||
@@ -3272,45 +3278,45 @@ testNegotiateCall =
|
||||
-- alice invite bob to call
|
||||
alice ##> ("/_call invite @2 " <> serialize testCallType)
|
||||
alice <## "ok"
|
||||
alice #$> ("/_get chat @2 count=100", chat, [(1, "outgoing call: calling...")])
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "outgoing call: calling...")])
|
||||
bob <## "alice wants to connect with you via WebRTC video call (e2e encrypted)"
|
||||
repeatM_ 3 $ getTermLine bob
|
||||
bob #$> ("/_get chat @2 count=100", chat, [(0, "incoming call: calling...")])
|
||||
bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "incoming call: calling...")])
|
||||
-- bob accepts call by sending WebRTC offer
|
||||
bob ##> ("/_call offer @2 " <> serialize testWebRTCCallOffer)
|
||||
bob <## "ok"
|
||||
bob #$> ("/_get chat @2 count=100", chat, [(0, "incoming call: accepted")])
|
||||
bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "incoming call: accepted")])
|
||||
alice <## "bob accepted your WebRTC video call (e2e encrypted)"
|
||||
repeatM_ 3 $ getTermLine alice
|
||||
alice <## "message updated" -- call chat item updated
|
||||
alice #$> ("/_get chat @2 count=100", chat, [(1, "outgoing call: accepted")])
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "outgoing call: accepted")])
|
||||
-- alice confirms call by sending WebRTC answer
|
||||
alice ##> ("/_call answer @2 " <> serialize testWebRTCSession)
|
||||
alice
|
||||
<### [ "ok",
|
||||
"message updated"
|
||||
]
|
||||
alice #$> ("/_get chat @2 count=100", chat, [(1, "outgoing call: connecting...")])
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "outgoing call: connecting...")])
|
||||
bob <## "alice continued the WebRTC call"
|
||||
repeatM_ 3 $ getTermLine bob
|
||||
bob #$> ("/_get chat @2 count=100", chat, [(0, "incoming call: connecting...")])
|
||||
bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "incoming call: connecting...")])
|
||||
-- participants can update calls as connected
|
||||
alice ##> "/_call status @2 connected"
|
||||
alice
|
||||
<### [ "ok",
|
||||
"message updated"
|
||||
]
|
||||
alice #$> ("/_get chat @2 count=100", chat, [(1, "outgoing call: in progress (00:00)")])
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "outgoing call: in progress (00:00)")])
|
||||
bob ##> "/_call status @2 connected"
|
||||
bob <## "ok"
|
||||
bob #$> ("/_get chat @2 count=100", chat, [(0, "incoming call: in progress (00:00)")])
|
||||
bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "incoming call: in progress (00:00)")])
|
||||
-- either party can end the call
|
||||
bob ##> "/_call end @2"
|
||||
bob <## "ok"
|
||||
bob #$> ("/_get chat @2 count=100", chat, [(0, "incoming call: ended (00:00)")])
|
||||
bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "incoming call: ended (00:00)")])
|
||||
alice <## "call with bob ended"
|
||||
alice <## "message updated"
|
||||
alice #$> ("/_get chat @2 count=100", chat, [(1, "outgoing call: ended (00:00)")])
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "outgoing call: ended (00:00)")])
|
||||
|
||||
testMaintenanceMode :: IO ()
|
||||
testMaintenanceMode = withTmpFiles $ do
|
||||
@@ -3501,11 +3507,11 @@ testSetChatItemTTL =
|
||||
bob <# "alice> 3"
|
||||
bob #> "@alice 4"
|
||||
alice <# "bob> 4"
|
||||
alice #$> ("/_get chat @2 count=100", chatF, [((1, "1"), Nothing), ((0, "2"), Nothing), ((1, ""), Just "test.jpg"), ((1, "3"), Nothing), ((0, "4"), Nothing)])
|
||||
alice #$> ("/_get chat @2 count=100", chatF, chatFeaturesF <> [((1, "1"), Nothing), ((0, "2"), Nothing), ((1, ""), Just "test.jpg"), ((1, "3"), Nothing), ((0, "4"), Nothing)])
|
||||
checkActionDeletesFile "./tests/tmp/app_files/test.jpg" $
|
||||
alice #$> ("/_ttl 2", id, "ok")
|
||||
alice #$> ("/_get chat @2 count=100", chat, [(1, "3"), (0, "4")]) -- when expiration is turned on, first cycle is synchronous
|
||||
bob #$> ("/_get chat @2 count=100", chat, [(0, "1"), (1, "2"), (0, ""), (0, "3"), (1, "4")])
|
||||
bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "1"), (1, "2"), (0, ""), (0, "3"), (1, "4")])
|
||||
alice #$> ("/ttl", id, "old messages are set to be deleted after: 2 second(s)")
|
||||
alice #$> ("/ttl week", id, "ok")
|
||||
alice #$> ("/ttl", id, "old messages are set to be deleted after: one week")
|
||||
@@ -3549,8 +3555,8 @@ testGroupLink =
|
||||
alice @@@ [("#team", "connected")]
|
||||
bob @@@ [("#team", "connected")]
|
||||
-- calling /_get chat api marks it as used and adds it to chat previews
|
||||
alice #$> ("/_get chat @2 count=100", chat, [])
|
||||
alice @@@ [("@bob", ""), ("#team", "connected")]
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures)
|
||||
alice @@@ [("@bob", "Voice messages: enabled"), ("#team", "connected")]
|
||||
alice <##> bob
|
||||
alice @@@ [("@bob", "hey"), ("#team", "connected")]
|
||||
|
||||
@@ -3830,8 +3836,8 @@ testSwitchContact =
|
||||
alice <## "bob: you started changing address"
|
||||
bob <## "alice changed address for you"
|
||||
alice <## "bob: you changed address"
|
||||
alice #$> ("/_get chat @2 count=100", chat, [(1, "started changing address..."), (1, "you changed address")])
|
||||
bob #$> ("/_get chat @2 count=100", chat, [(0, "started changing address for you..."), (0, "changed address for you")])
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "started changing address..."), (1, "you changed address")])
|
||||
bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "started changing address for you..."), (0, "changed address for you")])
|
||||
alice <##> bob
|
||||
|
||||
testSwitchGroupMember :: IO ()
|
||||
@@ -4021,6 +4027,24 @@ chatF = map (\(a, _, c) -> (a, c)) . chat''
|
||||
chat'' :: String -> [((Int, String), Maybe (Int, String), Maybe String)]
|
||||
chat'' = read
|
||||
|
||||
chatFeatures :: [(Int, String)]
|
||||
chatFeatures = map (\(a, _, _) -> a) chatFeatures''
|
||||
|
||||
chatFeatures' :: [((Int, String), Maybe (Int, String))]
|
||||
chatFeatures' = map (\(a, b, _) -> (a, b)) chatFeatures''
|
||||
|
||||
chatFeaturesF :: [((Int, String), Maybe String)]
|
||||
chatFeaturesF = map (\(a, _, c) -> (a, c)) chatFeatures''
|
||||
|
||||
chatFeatures'' :: [((Int, String), Maybe (Int, String), Maybe String)]
|
||||
chatFeatures'' = [((0, "Full deletion: off"), Nothing, Nothing), ((0, "Voice messages: enabled"), Nothing, Nothing)]
|
||||
|
||||
itemId :: Int -> String
|
||||
itemId i = show $ length chatFeatures + i
|
||||
|
||||
groupItemId :: Int -> Int -> String
|
||||
groupItemId n i = show $ (length chatFeatures) * n + i
|
||||
|
||||
(@@@) :: TestCC -> [(String, String)] -> Expectation
|
||||
(@@@) = getChats . map $ \(ldn, msg, _) -> (ldn, msg)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user