diff --git a/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift b/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift
index 6969ae325c..d860c8e842 100644
--- a/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift
+++ b/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift
@@ -143,6 +143,7 @@ struct ChatPreviewView: View {
}
case let .group(groupInfo):
switch (groupInfo.membership.memberStatus) {
+ case .memPendingApproval: inactiveIcon("questionmark.circle.fill")
case .memRejected: inactiveIcon()
case .memLeft: inactiveIcon()
case .memRemoved: inactiveIcon()
@@ -154,8 +155,8 @@ struct ChatPreviewView: View {
}
}
- @ViewBuilder private func inactiveIcon() -> some View {
- Image(systemName: "multiply.circle.fill")
+ private func inactiveIcon(_ icon: String = "multiply.circle.fill") -> some View {
+ Image(systemName: icon)
.foregroundColor(.secondary.opacity(0.65))
.background(Circle().foregroundColor(Color(uiColor: .systemBackground)))
}
diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift
index a601e60d5f..468bc2ea8f 100644
--- a/apps/ios/SimpleXChat/ChatTypes.swift
+++ b/apps/ios/SimpleXChat/ChatTypes.swift
@@ -2147,6 +2147,7 @@ public struct GroupMember: Identifiable, Decodable, Hashable {
case .memGroupDeleted: return false
case .memUnknown: return false
case .memInvited: return false
+ case .memPendingApproval: return true
case .memIntroduced: return false
case .memIntroInvited: return false
case .memAccepted: return false
@@ -2165,6 +2166,7 @@ public struct GroupMember: Identifiable, Decodable, Hashable {
case .memGroupDeleted: return false
case .memUnknown: return false
case .memInvited: return false
+ case .memPendingApproval: return false
case .memIntroduced: return true
case .memIntroInvited: return true
case .memAccepted: return true
@@ -2296,6 +2298,7 @@ public enum GroupMemberStatus: String, Decodable, Hashable {
case memGroupDeleted = "deleted"
case memUnknown = "unknown"
case memInvited = "invited"
+ case memPendingApproval = "pending_approval"
case memIntroduced = "introduced"
case memIntroInvited = "intro-inv"
case memAccepted = "accepted"
@@ -2312,6 +2315,7 @@ public enum GroupMemberStatus: String, Decodable, Hashable {
case .memGroupDeleted: return "group deleted"
case .memUnknown: return "unknown status"
case .memInvited: return "invited"
+ case .memPendingApproval: return "pending approval"
case .memIntroduced: return "connecting (introduced)"
case .memIntroInvited: return "connecting (introduction invitation)"
case .memAccepted: return "connecting (accepted)"
@@ -2330,6 +2334,7 @@ public enum GroupMemberStatus: String, Decodable, Hashable {
case .memGroupDeleted: return "group deleted"
case .memUnknown: return "unknown"
case .memInvited: return "invited"
+ case .memPendingApproval: return "pending"
case .memIntroduced: return "connecting"
case .memIntroInvited: return "connecting"
case .memAccepted: return "connecting"
diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt
index f784dcb9ed..7afcd69487 100644
--- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt
+++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt
@@ -1917,6 +1917,7 @@ data class GroupMember (
GroupMemberStatus.MemGroupDeleted -> false
GroupMemberStatus.MemUnknown -> false
GroupMemberStatus.MemInvited -> false
+ GroupMemberStatus.MemPendingApproval -> true
GroupMemberStatus.MemIntroduced -> false
GroupMemberStatus.MemIntroInvited -> false
GroupMemberStatus.MemAccepted -> false
@@ -1933,6 +1934,7 @@ data class GroupMember (
GroupMemberStatus.MemGroupDeleted -> false
GroupMemberStatus.MemUnknown -> false
GroupMemberStatus.MemInvited -> false
+ GroupMemberStatus.MemPendingApproval -> false
GroupMemberStatus.MemIntroduced -> true
GroupMemberStatus.MemIntroInvited -> true
GroupMemberStatus.MemAccepted -> true
@@ -2037,6 +2039,7 @@ enum class GroupMemberStatus {
@SerialName("deleted") MemGroupDeleted,
@SerialName("unknown") MemUnknown,
@SerialName("invited") MemInvited,
+ @SerialName("pending_approval") MemPendingApproval,
@SerialName("introduced") MemIntroduced,
@SerialName("intro-inv") MemIntroInvited,
@SerialName("accepted") MemAccepted,
@@ -2052,6 +2055,7 @@ enum class GroupMemberStatus {
MemGroupDeleted -> generalGetString(MR.strings.group_member_status_group_deleted)
MemUnknown -> generalGetString(MR.strings.group_member_status_unknown)
MemInvited -> generalGetString(MR.strings.group_member_status_invited)
+ MemPendingApproval -> generalGetString(MR.strings.group_member_status_pending_approval)
MemIntroduced -> generalGetString(MR.strings.group_member_status_introduced)
MemIntroInvited -> generalGetString(MR.strings.group_member_status_intro_invitation)
MemAccepted -> generalGetString(MR.strings.group_member_status_accepted)
@@ -2068,6 +2072,7 @@ enum class GroupMemberStatus {
MemGroupDeleted -> generalGetString(MR.strings.group_member_status_group_deleted)
MemUnknown -> generalGetString(MR.strings.group_member_status_unknown_short)
MemInvited -> generalGetString(MR.strings.group_member_status_invited)
+ MemPendingApproval -> generalGetString(MR.strings.group_member_status_pending_approval_short)
MemIntroduced -> generalGetString(MR.strings.group_member_status_connecting)
MemIntroInvited -> generalGetString(MR.strings.group_member_status_connecting)
MemAccepted -> generalGetString(MR.strings.group_member_status_connecting)
diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatPreviewView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatPreviewView.kt
index 93d512507a..3edefafa75 100644
--- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatPreviewView.kt
+++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatPreviewView.kt
@@ -52,9 +52,9 @@ fun ChatPreviewView(
val cInfo = chat.chatInfo
@Composable
- fun inactiveIcon() {
+ fun inactiveIcon(icon: ImageResource = MR.images.ic_cancel_filled) {
Icon(
- painterResource(MR.images.ic_cancel_filled),
+ painterResource(icon),
stringResource(MR.strings.icon_descr_group_inactive),
Modifier.size(18.sp.toDp()).background(MaterialTheme.colors.background, CircleShape),
tint = MaterialTheme.colors.secondary
@@ -70,6 +70,7 @@ fun ChatPreviewView(
}
is ChatInfo.Group ->
when (cInfo.groupInfo.membership.memberStatus) {
+ GroupMemberStatus.MemPendingApproval -> inactiveIcon(MR.images.ic_help_filled)
GroupMemberStatus.MemRejected -> inactiveIcon()
GroupMemberStatus.MemLeft -> inactiveIcon()
GroupMemberStatus.MemRemoved -> inactiveIcon()
diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml
index 83f085fe3a..3a2aee9623 100644
--- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml
+++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml
@@ -1632,6 +1632,8 @@
group deleted
unknown status
invited
+ pending approval
+ pending
connecting (introduced)
connecting (introduction invitation)
connecting (accepted)
diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_help_filled.svg b/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_help_filled.svg
new file mode 100644
index 0000000000..ba3d3a393a
--- /dev/null
+++ b/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_help_filled.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/Simplex/Chat/Library/Subscriber.hs b/src/Simplex/Chat/Library/Subscriber.hs
index 2e9e4f85b6..90dc5b8d1a 100644
--- a/src/Simplex/Chat/Library/Subscriber.hs
+++ b/src/Simplex/Chat/Library/Subscriber.hs
@@ -581,7 +581,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
let (UserContactLink {autoAccept}, gli_) = ucl
when (connChatVersion < batchSend2Version) $ sendAutoReply ct' autoAccept
-- TODO REMOVE LEGACY vvv
- forM_ gli_ $ \GroupLinkInfo {groupId, memberRole = gLinkMemRole, acceptance = _acceptance} -> do
+ forM_ gli_ $ \GroupLinkInfo {groupId, memberRole = gLinkMemRole} -> do
groupInfo <- withStore $ \db -> getGroupInfo db vr user groupId
subMode <- chatReadVar subscriptionMode
groupConnIds <- createAgentConnectionAsync user CFCreateConnGrpInv True SCMInvitation subMode
@@ -1205,10 +1205,10 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
incognitoProfile <- if acceptIncognito then Just . NewIncognito <$> liftIO generateRandomProfile else pure Nothing
ct <- acceptContactRequestAsync user cReq incognitoProfile reqPQSup
toView $ CRAcceptingContactRequest user ct
- Just gli@GroupLinkInfo {groupId, acceptance = gAcceptance, memberRole = gLinkMemRole} -> do
+ Just gli@GroupLinkInfo {groupId, memberRole = gLinkMemRole} -> do
gInfo <- withStore $ \db -> getGroupInfo db vr user groupId
acceptMember_ <- asks $ acceptMember . chatHooks . config
- maybe (pure $ Right (gAcceptance, gLinkMemRole)) (\am -> liftIO $ am gInfo gli p) acceptMember_ >>= \case
+ maybe (pure $ Right (GAAuto, gLinkMemRole)) (\am -> liftIO $ am gInfo gli p) acceptMember_ >>= \case
Right (acceptance, useRole)
| v < groupFastLinkJoinVersion ->
messageError "processUserContactRequest: chat version range incompatible for accepting group join request"
diff --git a/src/Simplex/Chat/Store/Postgres/Migrations/M20250227_member_acceptance.hs b/src/Simplex/Chat/Store/Postgres/Migrations/M20250227_member_acceptance.hs
index 68fa03868c..40d38ef4bc 100644
--- a/src/Simplex/Chat/Store/Postgres/Migrations/M20250227_member_acceptance.hs
+++ b/src/Simplex/Chat/Store/Postgres/Migrations/M20250227_member_acceptance.hs
@@ -10,8 +10,6 @@ m20250227_member_acceptance :: Text
m20250227_member_acceptance =
T.pack
[r|
-ALTER TABLE user_contact_links ADD COLUMN group_link_auto_accept TEXT;
-
DROP INDEX idx_chat_items_groups_history;
CREATE INDEX idx_chat_items_groups_history ON chat_items(
user_id,
@@ -28,8 +26,6 @@ down_m20250227_member_acceptance :: Text
down_m20250227_member_acceptance =
T.pack
[r|
-ALTER TABLE user_contact_links DROP COLUMN group_link_auto_accept;
-
DROP INDEX idx_chat_items_groups_history;
CREATE INDEX idx_chat_items_groups_history ON chat_items(
user_id,
diff --git a/src/Simplex/Chat/Store/Profiles.hs b/src/Simplex/Chat/Store/Profiles.hs
index 38045500ce..22d2a7b1f5 100644
--- a/src/Simplex/Chat/Store/Profiles.hs
+++ b/src/Simplex/Chat/Store/Profiles.hs
@@ -457,7 +457,6 @@ data UserContactLink = UserContactLink
data GroupLinkInfo = GroupLinkInfo
{ groupId :: GroupId,
- acceptance :: GroupAcceptance,
memberRole :: GroupMemberRole
}
deriving (Show)
@@ -498,14 +497,14 @@ getUserContactLinkById db userId userContactLinkId =
groupLinkInfoQuery :: Query
groupLinkInfoQuery =
[sql|
- SELECT conn_req_contact, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content, group_id, group_link_auto_accept, group_link_member_role
+ SELECT conn_req_contact, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content, group_id, group_link_member_role
FROM user_contact_links
WHERE user_id = ?
|]
-toGroupLinkInfo :: (Maybe GroupId, Maybe GroupAcceptance, Maybe GroupMemberRole) -> Maybe GroupLinkInfo
-toGroupLinkInfo (groupId_, acceptance_, mRole_) =
- (\groupId -> GroupLinkInfo {groupId, acceptance = fromMaybe GAAuto acceptance_, memberRole = fromMaybe GRMember mRole_})
+toGroupLinkInfo :: (Maybe GroupId, Maybe GroupMemberRole) -> Maybe GroupLinkInfo
+toGroupLinkInfo (groupId_, mRole_) =
+ (\groupId -> GroupLinkInfo {groupId, memberRole = fromMaybe GRMember mRole_})
<$> groupId_
getGroupLinkInfo :: DB.Connection -> UserId -> GroupId -> IO (Maybe GroupLinkInfo)
diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/M20250227_member_acceptance.hs b/src/Simplex/Chat/Store/SQLite/Migrations/M20250227_member_acceptance.hs
index 281b3063cd..6b3f287c7e 100644
--- a/src/Simplex/Chat/Store/SQLite/Migrations/M20250227_member_acceptance.hs
+++ b/src/Simplex/Chat/Store/SQLite/Migrations/M20250227_member_acceptance.hs
@@ -8,8 +8,6 @@ import Database.SQLite.Simple.QQ (sql)
m20250227_member_acceptance :: Query
m20250227_member_acceptance =
[sql|
-ALTER TABLE user_contact_links ADD COLUMN group_link_auto_accept TEXT;
-
DROP INDEX idx_chat_items_groups_history;
CREATE INDEX idx_chat_items_groups_history ON chat_items(
user_id,
@@ -25,8 +23,6 @@ CREATE INDEX idx_chat_items_groups_history ON chat_items(
down_m20250227_member_acceptance :: Query
down_m20250227_member_acceptance =
[sql|
-ALTER TABLE user_contact_links DROP COLUMN group_link_auto_accept;
-
DROP INDEX idx_chat_items_groups_history;
CREATE INDEX idx_chat_items_groups_history ON chat_items(
user_id,
diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt b/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt
index 063ca91faf..c72214fb36 100644
--- a/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt
+++ b/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt
@@ -2965,7 +2965,7 @@ Plan:
SEARCH user_contact_links USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=? AND local_display_name=?)
Query:
- SELECT conn_req_contact, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content, group_id, group_link_auto_accept, group_link_member_role
+ SELECT conn_req_contact, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content, group_id, group_link_member_role
FROM user_contact_links
WHERE user_id = ?
AND user_contact_link_id = ?
diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql b/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql
index d48806e953..45e91a46f8 100644
--- a/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql
+++ b/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql
@@ -316,7 +316,6 @@ CREATE TABLE user_contact_links(
group_link_id BLOB,
group_link_member_role TEXT NULL,
business_address INTEGER DEFAULT 0,
- group_link_auto_accept TEXT,
UNIQUE(user_id, local_display_name)
);
CREATE TABLE contact_requests(