diff --git a/bots/api/TYPES.md b/bots/api/TYPES.md index c2303e9e6d..7a68fa92a9 100644 --- a/bots/api/TYPES.md +++ b/bots/api/TYPES.md @@ -51,6 +51,7 @@ This file is generated automatically. - [Color](#color) - [CommandError](#commanderror) - [CommandErrorType](#commanderrortype) +- [CommentsGroupPreference](#commentsgrouppreference) - [ComposedMessage](#composedmessage) - [ConnStatus](#connstatus) - [ConnType](#conntype) @@ -1480,6 +1481,15 @@ LARGE: - type: "LARGE" +--- + +## CommentsGroupPreference + +**Record type**: +- enable: [GroupFeatureEnabled](#groupfeatureenabled) +- duration: int? + + --- ## ComposedMessage @@ -2070,6 +2080,7 @@ Phone: - reports: [GroupPreference](#grouppreference) - history: [GroupPreference](#grouppreference) - sessions: [RoleGroupPreference](#rolegrouppreference) +- comments: [CommentsGroupPreference](#commentsgrouppreference) - commands: [[ChatBotCommand](#chatbotcommand)] @@ -2160,6 +2171,7 @@ MemberSupport: - "reports" - "history" - "sessions" +- "comments" --- @@ -2376,6 +2388,7 @@ Known: - reports: [GroupPreference](#grouppreference)? - history: [GroupPreference](#grouppreference)? - sessions: [RoleGroupPreference](#rolegrouppreference)? +- comments: [CommentsGroupPreference](#commentsgrouppreference)? - commands: [[ChatBotCommand](#chatbotcommand)]? diff --git a/bots/src/API/Docs/Types.hs b/bots/src/API/Docs/Types.hs index 37fc6121ce..e55e037243 100644 --- a/bots/src/API/Docs/Types.hs +++ b/bots/src/API/Docs/Types.hs @@ -237,6 +237,7 @@ chatTypesDocsData = (sti @Color, STEnum, "", [], "", ""), (sti @CommandError, STUnion, "", [], "", ""), (sti @CommandErrorType, STUnion, "", [], "", ""), + (sti @CommentsGroupPreference, STRecord, "", [], "", ""), (sti @ComposedMessage, STRecord, "", [], "", ""), (sti @Connection, STRecord, "", [], "", ""), (sti @ConnectionEntity, STUnion, "", [], "", ""), @@ -435,6 +436,7 @@ deriving instance Generic ClientNotice deriving instance Generic Color deriving instance Generic CommandError deriving instance Generic CommandErrorType +deriving instance Generic CommentsGroupPreference deriving instance Generic ComposedMessage deriving instance Generic Connection deriving instance Generic ConnectionEntity diff --git a/packages/simplex-chat-client/types/typescript/src/types.ts b/packages/simplex-chat-client/types/typescript/src/types.ts index 34b34ccbd5..ef232a6461 100644 --- a/packages/simplex-chat-client/types/typescript/src/types.ts +++ b/packages/simplex-chat-client/types/typescript/src/types.ts @@ -1714,6 +1714,11 @@ export namespace CommandErrorType { } } +export interface CommentsGroupPreference { + enable: GroupFeatureEnabled + duration?: number // int +} + export interface ComposedMessage { fileSource?: CryptoFile quotedItemId?: number // int64 @@ -2420,6 +2425,7 @@ export interface FullGroupPreferences { reports: GroupPreference history: GroupPreference sessions: RoleGroupPreference + comments: CommentsGroupPreference commands: ChatBotCommand[] } @@ -2492,6 +2498,7 @@ export enum GroupFeature { Reports = "reports", History = "history", Sessions = "sessions", + Comments = "comments", } export enum GroupFeatureEnabled { @@ -2669,6 +2676,7 @@ export interface GroupPreferences { reports?: GroupPreference history?: GroupPreference sessions?: RoleGroupPreference + comments?: CommentsGroupPreference commands?: ChatBotCommand[] } diff --git a/src/Simplex/Chat/Types/Preferences.hs b/src/Simplex/Chat/Types/Preferences.hs index d8c6f10b3a..c02d2b8433 100644 --- a/src/Simplex/Chat/Types/Preferences.hs +++ b/src/Simplex/Chat/Types/Preferences.hs @@ -177,6 +177,7 @@ data GroupFeature | GFReports | GFHistory | GFSessions + | GFComments deriving (Show) data SGroupFeature (f :: GroupFeature) where @@ -190,6 +191,7 @@ data SGroupFeature (f :: GroupFeature) where SGFReports :: SGroupFeature 'GFReports SGFHistory :: SGroupFeature 'GFHistory SGFSessions :: SGroupFeature 'GFSessions + SGFComments :: SGroupFeature 'GFComments deriving instance Show (SGroupFeature f) @@ -217,6 +219,7 @@ groupFeatureNameText = \case GFReports -> "Member reports" GFHistory -> "Recent history" GFSessions -> "Chat sessions" + GFComments -> "Comments" groupFeatureNameText' :: SGroupFeature f -> Text groupFeatureNameText' = groupFeatureNameText . toGroupFeature @@ -230,6 +233,11 @@ groupFeatureMemberAllowed' feature role prefs = let pref = getGroupPreference feature prefs in getField @"enable" pref == FEOn && maybe True (role >=) (getField @"role" pref) +-- TODO: some preferences are channel-only (e.g., comments) and should not generate +-- UI items or be configurable in regular groups. Currently they are simply excluded +-- from this list. When more channel-only or group-only preferences are added, +-- consider adding a scope property to GroupFeatureI (e.g., GFScopeAll | GFScopeChannel | GFScopeGroup) +-- and filtering at the call sites in createGroupFeatureItems_ / createGroupFeatureChangedItems. allGroupFeatures :: [AGroupFeature] allGroupFeatures = [ AGF SGFTimedMessages, @@ -244,7 +252,7 @@ allGroupFeatures = ] groupPrefSel :: SGroupFeature f -> GroupPreferences -> Maybe (GroupFeaturePreference f) -groupPrefSel f GroupPreferences {timedMessages, directMessages, fullDelete, reactions, voice, files, simplexLinks, reports, history, sessions} = case f of +groupPrefSel f GroupPreferences {timedMessages, directMessages, fullDelete, reactions, voice, files, simplexLinks, reports, history, sessions, comments} = case f of SGFTimedMessages -> timedMessages SGFDirectMessages -> directMessages SGFFullDelete -> fullDelete @@ -255,6 +263,7 @@ groupPrefSel f GroupPreferences {timedMessages, directMessages, fullDelete, reac SGFReports -> reports SGFHistory -> history SGFSessions -> sessions + SGFComments -> comments toGroupFeature :: SGroupFeature f -> GroupFeature toGroupFeature = \case @@ -268,6 +277,7 @@ toGroupFeature = \case SGFReports -> GFReports SGFHistory -> GFHistory SGFSessions -> GFSessions + SGFComments -> GFComments class GroupPreferenceI p where getGroupPreference :: SGroupFeature f -> p -> GroupFeaturePreference f @@ -279,7 +289,7 @@ instance GroupPreferenceI (Maybe GroupPreferences) where getGroupPreference pt prefs = fromMaybe (getGroupPreference pt defaultGroupPrefs) (groupPrefSel pt =<< prefs) instance GroupPreferenceI FullGroupPreferences where - getGroupPreference f FullGroupPreferences {timedMessages, directMessages, fullDelete, reactions, voice, files, simplexLinks, reports, history, sessions} = case f of + getGroupPreference f FullGroupPreferences {timedMessages, directMessages, fullDelete, reactions, voice, files, simplexLinks, reports, history, sessions, comments} = case f of SGFTimedMessages -> timedMessages SGFDirectMessages -> directMessages SGFFullDelete -> fullDelete @@ -290,6 +300,7 @@ instance GroupPreferenceI FullGroupPreferences where SGFReports -> reports SGFHistory -> history SGFSessions -> sessions + SGFComments -> comments {-# INLINE getGroupPreference #-} -- collection of optional group preferences @@ -304,6 +315,7 @@ data GroupPreferences = GroupPreferences reports :: Maybe ReportsGroupPreference, history :: Maybe HistoryGroupPreference, sessions :: Maybe SessionsGroupPreference, + comments :: Maybe CommentsGroupPreference, commands :: Maybe [ChatBotCommand] } deriving (Eq, Show) @@ -354,6 +366,7 @@ setGroupPreference_ f pref prefs = SGFReports -> prefs {reports = pref} SGFHistory -> prefs {history = pref} SGFSessions -> prefs {sessions = pref} + SGFComments -> prefs {comments = pref} setGroupTimedMessagesPreference :: TimedMessagesGroupPreference -> Maybe GroupPreferences -> GroupPreferences setGroupTimedMessagesPreference pref prefs_ = @@ -396,6 +409,7 @@ data FullGroupPreferences = FullGroupPreferences reports :: ReportsGroupPreference, history :: HistoryGroupPreference, sessions :: SessionsGroupPreference, + comments :: CommentsGroupPreference, commands :: ListDef ChatBotCommand } deriving (Eq, Show) @@ -465,11 +479,12 @@ defaultGroupPrefs = reports = ReportsGroupPreference {enable = FEOn}, history = HistoryGroupPreference {enable = FEOff}, sessions = SessionsGroupPreference {enable = FEOff, role = Nothing}, + comments = CommentsGroupPreference {enable = FEOff, duration = Nothing}, commands = ListDef [] } emptyGroupPrefs :: GroupPreferences -emptyGroupPrefs = GroupPreferences Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing +emptyGroupPrefs = GroupPreferences Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing businessGroupPrefs :: Preferences -> GroupPreferences businessGroupPrefs Preferences {timedMessages, fullDelete, reactions, voice, files, sessions, commands} = @@ -501,6 +516,7 @@ defaultBusinessGroupPrefs = reports = Just $ ReportsGroupPreference FEOff, history = Just $ HistoryGroupPreference FEOn, sessions = Just $ SessionsGroupPreference FEOn Nothing, + comments = Just $ CommentsGroupPreference FEOff Nothing, commands = Nothing } @@ -635,6 +651,14 @@ data SessionsGroupPreference = SessionsGroupPreference {enable :: GroupFeatureEnabled, role :: Maybe GroupMemberRole} deriving (Eq, Show) +-- Channel comments. ``duration` is time in seconds since post creation +-- after which a channel post stops accepting new comments; `Nothing` means accept comments indefinitely. +data CommentsGroupPreference = CommentsGroupPreference + { enable :: GroupFeatureEnabled, + duration :: Maybe Int + } + deriving (Eq, Show) + class (Eq (GroupFeaturePreference f), HasField "enable" (GroupFeaturePreference f) GroupFeatureEnabled) => GroupFeatureI f where type GroupFeaturePreference (f :: GroupFeature) = p | p -> f sGroupFeature :: SGroupFeature f @@ -678,6 +702,9 @@ instance HasField "enable" HistoryGroupPreference GroupFeatureEnabled where instance HasField "enable" SessionsGroupPreference GroupFeatureEnabled where hasField p@SessionsGroupPreference {enable} = (\e -> p {enable = e}, enable) +instance HasField "enable" CommentsGroupPreference GroupFeatureEnabled where + hasField p@CommentsGroupPreference {enable} = (\e -> p {enable = e}, enable) + instance GroupFeatureI 'GFTimedMessages where type GroupFeaturePreference 'GFTimedMessages = TimedMessagesGroupPreference sGroupFeature = SGFTimedMessages @@ -738,6 +765,12 @@ instance GroupFeatureI 'GFSessions where groupPrefParam _ = Nothing groupPrefRole SessionsGroupPreference {role} = role +instance GroupFeatureI 'GFComments where + type GroupFeaturePreference 'GFComments = CommentsGroupPreference + sGroupFeature = SGFComments + groupPrefParam CommentsGroupPreference {duration} = duration + groupPrefRole _ = Nothing + instance GroupFeatureNoRoleI 'GFTimedMessages instance GroupFeatureNoRoleI 'GFFullDelete @@ -748,6 +781,8 @@ instance GroupFeatureNoRoleI 'GFReports instance GroupFeatureNoRoleI 'GFHistory +instance GroupFeatureNoRoleI 'GFComments + instance HasField "role" DirectMessagesGroupPreference (Maybe GroupMemberRole) where hasField p@DirectMessagesGroupPreference {role} = (\r -> p {role = r}, role) @@ -788,6 +823,7 @@ groupPrefStateText feature pref param role = groupParamText_ :: GroupFeature -> Maybe Int -> Text groupParamText_ feature param = case feature of GFTimedMessages -> maybe "" (\p -> " (" <> timedTTLText p <> ")") param + GFComments -> maybe "" (\p -> " (close after " <> timedTTLText p <> ")") param _ -> "" groupPreferenceText :: forall f. GroupFeatureI f => GroupFeaturePreference f -> Text @@ -938,6 +974,7 @@ mergeGroupPreferences groupPreferences = reports = pref SGFReports, history = pref SGFHistory, sessions = pref SGFSessions, + comments = pref SGFComments, commands = ListDef $ fromMaybe [] $ groupPreferences >>= commands_ } where @@ -957,6 +994,7 @@ toGroupPreferences groupPreferences@FullGroupPreferences {commands = ListDef cmd reports = pref SGFReports, history = pref SGFHistory, sessions = pref SGFSessions, + comments = pref SGFComments, commands = Just cmds } where @@ -1091,6 +1129,12 @@ instance FromJSON SessionsGroupPreference where parseJSON v = $(J.mkParseJSON defaultJSON ''SessionsGroupPreference) v omittedField = Just SessionsGroupPreference {enable = FEOff, role = Nothing} +$(J.deriveToJSON defaultJSON ''CommentsGroupPreference) + +instance FromJSON CommentsGroupPreference where + parseJSON v = $(J.mkParseJSON defaultJSON ''CommentsGroupPreference) v + omittedField = Just CommentsGroupPreference {enable = FEOff, duration = Nothing} + $(J.deriveJSON defaultJSON ''GroupPreferences) instance ToField GroupPreferences where diff --git a/tests/ProtocolTests.hs b/tests/ProtocolTests.hs index 1b708a2ffa..6d9ee54e6c 100644 --- a/tests/ProtocolTests.hs +++ b/tests/ProtocolTests.hs @@ -101,7 +101,7 @@ testChatPreferences :: Maybe Preferences testChatPreferences = Just Preferences {voice = Just VoicePreference {allow = FAYes}, files = Nothing, fullDelete = Nothing, timedMessages = Nothing, calls = Nothing, reactions = Just ReactionsPreference {allow = FAYes}, sessions = Nothing, commands = Nothing} testGroupPreferences :: Maybe GroupPreferences -testGroupPreferences = Just GroupPreferences {timedMessages = Nothing, directMessages = Nothing, reactions = Just ReactionsGroupPreference {enable = FEOn}, voice = Just VoiceGroupPreference {enable = FEOn, role = Nothing}, files = Nothing, fullDelete = Nothing, simplexLinks = Nothing, history = Nothing, reports = Nothing, sessions = Nothing, commands = Nothing} +testGroupPreferences = Just GroupPreferences {timedMessages = Nothing, directMessages = Nothing, reactions = Just ReactionsGroupPreference {enable = FEOn}, voice = Just VoiceGroupPreference {enable = FEOn, role = Nothing}, files = Nothing, fullDelete = Nothing, simplexLinks = Nothing, history = Nothing, reports = Nothing, sessions = Nothing, comments = Nothing, commands = Nothing} testProfile :: Profile testProfile = Profile {displayName = "alice", fullName = "Alice", shortDescr = Nothing, image = Just (ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII="), peerType = Nothing, contactLink = Nothing, preferences = testChatPreferences}