mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-11-04 19:27:55 +00:00
revert ui
This commit is contained in:
1 parent
f39002a146
commit
b7fd247998
23 files changed
+258
-597
No files matched your search
@@ -82,7 +82,7 @@ enum ChatCommand: ChatCmdProtocol {
|
||||
case apiDeleteMemberSupportChat(groupId: Int64, groupMemberId: Int64)
|
||||
case apiMembersRole(groupId: Int64, memberIds: [Int64], memberRole: GroupMemberRole)
|
||||
case apiBlockMembersForAll(groupId: Int64, memberIds: [Int64], blocked: Bool)
|
||||
case apiRemoveMembers(groupId: Int64, memberIds: [Int64], withMessages: Bool, notice: LinkNotice?)
|
||||
case apiRemoveMembers(groupId: Int64, memberIds: [Int64], withMessages: Bool)
|
||||
case apiLeaveGroup(groupId: Int64)
|
||||
case apiListMembers(groupId: Int64)
|
||||
case apiUpdateGroupProfile(groupId: Int64, groupProfile: GroupProfile)
|
||||
@@ -142,7 +142,7 @@ enum ChatCommand: ChatCmdProtocol {
|
||||
case apiConnectPreparedGroup(groupId: Int64, incognito: Bool, msg: MsgContent?)
|
||||
case apiConnect(userId: Int64, incognito: Bool, connLink: CreatedConnLink)
|
||||
case apiConnectContactViaAddress(userId: Int64, incognito: Bool, contactId: Int64)
|
||||
case apiDeleteChat(type: ChatType, id: Int64, chatDeleteMode: ChatDeleteMode, notice: LinkNotice?)
|
||||
case apiDeleteChat(type: ChatType, id: Int64, chatDeleteMode: ChatDeleteMode)
|
||||
case apiClearChat(type: ChatType, id: Int64)
|
||||
case apiListContacts(userId: Int64)
|
||||
case apiUpdateProfile(userId: Int64, profile: Profile)
|
||||
@@ -265,7 +265,7 @@ enum ChatCommand: ChatCmdProtocol {
|
||||
let msgs = encodeJSON(composedMessages)
|
||||
return "/_create *\(noteFolderId) json \(msgs)"
|
||||
case let .apiReportMessage(groupId, chatItemId, reportReason, reportText):
|
||||
return "/_report #\(groupId) \(chatItemId) reason=\(reportReason.rawValue) \(reportText)"
|
||||
return "/_report #\(groupId) \(chatItemId) reason=\(reportReason) \(reportText)"
|
||||
case let .apiUpdateChatItem(type, id, scope, itemId, um, live): return "/_update item \(ref(type, id, scope: scope)) \(itemId) live=\(onOff(live)) \(um.cmdString)"
|
||||
case let .apiDeleteChatItem(type, id, scope, itemIds, mode): return "/_delete item \(ref(type, id, scope: scope)) \(itemIds.map({ "\($0)" }).joined(separator: ",")) \(mode.rawValue)"
|
||||
case let .apiDeleteMemberChatItem(groupId, itemIds): return "/_delete member item #\(groupId) \(itemIds.map({ "\($0)" }).joined(separator: ","))"
|
||||
@@ -301,7 +301,7 @@ enum ChatCommand: ChatCmdProtocol {
|
||||
case let .apiDeleteMemberSupportChat(groupId, groupMemberId): return "/_delete member chat #\(groupId) \(groupMemberId)"
|
||||
case let .apiMembersRole(groupId, memberIds, memberRole): return "/_member role #\(groupId) \(memberIds.map({ "\($0)" }).joined(separator: ",")) \(memberRole.rawValue)"
|
||||
case let .apiBlockMembersForAll(groupId, memberIds, blocked): return "/_block #\(groupId) \(memberIds.map({ "\($0)" }).joined(separator: ",")) blocked=\(onOff(blocked))"
|
||||
case let .apiRemoveMembers(groupId, memberIds, withMessages, notice): return "/_remove #\(groupId) \(memberIds.map({ "\($0)" }).joined(separator: ",")) messages=\(onOff(withMessages))\(noticeCmdString(notice))"
|
||||
case let .apiRemoveMembers(groupId, memberIds, withMessages): return "/_remove #\(groupId) \(memberIds.map({ "\($0)" }).joined(separator: ",")) messages=\(onOff(withMessages))"
|
||||
case let .apiLeaveGroup(groupId): return "/_leave #\(groupId)"
|
||||
case let .apiListMembers(groupId): return "/_members #\(groupId)"
|
||||
case let .apiUpdateGroupProfile(groupId, groupProfile): return "/_group_profile #\(groupId) \(encodeJSON(groupProfile))"
|
||||
@@ -373,7 +373,7 @@ enum ChatCommand: ChatCmdProtocol {
|
||||
case let .apiConnectPreparedGroup(groupId, incognito, mc): return "/_connect group #\(groupId) incognito=\(onOff(incognito))\(maybeContent(mc))"
|
||||
case let .apiConnect(userId, incognito, connLink): return "/_connect \(userId) incognito=\(onOff(incognito)) \(connLink.connFullLink) \(connLink.connShortLink ?? "")"
|
||||
case let .apiConnectContactViaAddress(userId, incognito, contactId): return "/_connect contact \(userId) incognito=\(onOff(incognito)) \(contactId)"
|
||||
case let .apiDeleteChat(type, id, chatDeleteMode, notice): return "/_delete \(ref(type, id, scope: nil)) \(chatDeleteMode.cmdString)\(noticeCmdString(notice))"
|
||||
case let .apiDeleteChat(type, id, chatDeleteMode): return "/_delete \(ref(type, id, scope: nil)) \(chatDeleteMode.cmdString)"
|
||||
case let .apiClearChat(type, id): return "/_clear chat \(ref(type, id, scope: nil))"
|
||||
case let .apiListContacts(userId): return "/_contacts \(userId)"
|
||||
case let .apiUpdateProfile(userId, profile): return "/_profile \(userId) \(encodeJSON(profile))"
|
||||
@@ -1522,7 +1522,6 @@ enum ContactAddressPlan: Decodable, Hashable {
|
||||
case connectingProhibit(contact: Contact)
|
||||
case known(contact: Contact)
|
||||
case contactViaAddress(contact: Contact)
|
||||
case linkNotice(expiresAt: Date?, reason: ReportReason?)
|
||||
}
|
||||
|
||||
public struct GroupShortLinkInfo: Decodable, Hashable {
|
||||
@@ -1539,7 +1538,6 @@ enum GroupLinkPlan: Decodable, Hashable {
|
||||
case known(groupInfo: GroupInfo)
|
||||
case noRelays(groupSLinkData_: GroupShortLinkData?)
|
||||
case updateRequired(groupSLinkData_: GroupShortLinkData?)
|
||||
case linkNotice(expiresAt: Date?, reason: ReportReason?)
|
||||
}
|
||||
|
||||
struct ChatTagData: Encodable {
|
||||
@@ -1556,15 +1554,6 @@ struct UpdatedMessage: Encodable {
|
||||
}
|
||||
}
|
||||
|
||||
struct LinkNotice: Encodable {
|
||||
var ttl: Int64?
|
||||
var reason: ReportReason?
|
||||
}
|
||||
|
||||
func noticeCmdString(_ notice: LinkNotice?) -> String {
|
||||
notice.map { " notice=\(encodeJSON($0))" } ?? ""
|
||||
}
|
||||
|
||||
enum ChatDeleteMode: Codable {
|
||||
case full(notify: Bool)
|
||||
case entity(notify: Bool)
|
||||
|
||||
@@ -1079,7 +1079,7 @@ func apiConnect(incognito: Bool, connLink: CreatedConnLink) async -> (ConnReqTyp
|
||||
return nil
|
||||
}
|
||||
|
||||
private func apiConnectResponseAlert<R>(_ r: APIResult<R>, _ noticeTarget: LinkNoticeTarget = .link) async {
|
||||
private func apiConnectResponseAlert<R>(_ r: APIResult<R>) async {
|
||||
await MainActor.run {
|
||||
switch r.unexpected {
|
||||
case .error(.invalidConnReq):
|
||||
@@ -1092,8 +1092,6 @@ private func apiConnectResponseAlert<R>(_ r: APIResult<R>, _ noticeTarget: LinkN
|
||||
NSLocalizedString("Unsupported connection link", comment: ""),
|
||||
message: NSLocalizedString("This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link.", comment: "")
|
||||
)
|
||||
case let .error(.linkNotice(expiresAt, reason)):
|
||||
showLinkNoticeAlert(noticeTarget, expiresAt: expiresAt, reason: reason)
|
||||
case let .error(.simplexDomainNotReady(domain, err)):
|
||||
switch err {
|
||||
case .noValidLink:
|
||||
@@ -1233,14 +1231,14 @@ func apiChangePreparedGroupUser(groupId: Int64, newUserId: Int64) async throws -
|
||||
func apiConnectPreparedContact(contactId: Int64, incognito: Bool, msg: MsgContent?) async -> Contact? {
|
||||
let r: APIResult<ChatResponse1>? = await chatApiSendCmdWithRetry(.apiConnectPreparedContact(contactId: contactId, incognito: incognito, msg: msg))
|
||||
if case let .result(.startedConnectionToContact(_, contact)) = r { return contact }
|
||||
if let r { await apiConnectResponseAlert(r, .address) }
|
||||
if let r { await apiConnectResponseAlert(r) }
|
||||
return nil
|
||||
}
|
||||
|
||||
func apiConnectPreparedGroup(groupId: Int64, isChannel: Bool, incognito: Bool, msg: MsgContent?) async -> (GroupInfo, [RelayConnectionResult])? {
|
||||
func apiConnectPreparedGroup(groupId: Int64, incognito: Bool, msg: MsgContent?) async -> (GroupInfo, [RelayConnectionResult])? {
|
||||
let r: APIResult<ChatResponse1>? = await chatApiSendCmdWithRetry(.apiConnectPreparedGroup(groupId: groupId, incognito: incognito, msg: msg))
|
||||
if case let .result(.startedConnectionToGroup(_, groupInfo, relayResults)) = r { return (groupInfo, relayResults) }
|
||||
if let r { await apiConnectResponseAlert(r, isChannel ? .channel : .group) }
|
||||
if let r { await apiConnectResponseAlert(r) }
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1253,29 +1251,23 @@ func apiConnectContactViaAddress(incognito: Bool, contactId: Int64) async -> Con
|
||||
if case let .result(.sentInvitationToContact(_, contact, _)) = r { return contact }
|
||||
if let r {
|
||||
logger.error("apiConnectContactViaAddress error: \(responseError(r.unexpected))")
|
||||
await MainActor.run {
|
||||
if case let .error(.linkNotice(expiresAt, reason)) = r.unexpected {
|
||||
showLinkNoticeAlert(.address, expiresAt: expiresAt, reason: reason)
|
||||
} else {
|
||||
showAlert(connectionErrorAlert(r))
|
||||
}
|
||||
}
|
||||
await MainActor.run { showAlert(connectionErrorAlert(r)) }
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func apiDeleteChat(type: ChatType, id: Int64, chatDeleteMode: ChatDeleteMode = .full(notify: true), notice: LinkNotice? = nil) async throws {
|
||||
func apiDeleteChat(type: ChatType, id: Int64, chatDeleteMode: ChatDeleteMode = .full(notify: true)) async throws {
|
||||
let chatId = type.rawValue + id.description
|
||||
DispatchQueue.main.async { ChatModel.shared.deletedChats.insert(chatId) }
|
||||
defer { DispatchQueue.main.async { ChatModel.shared.deletedChats.remove(chatId) } }
|
||||
let r: ChatResponse1 = try await chatSendCmd(.apiDeleteChat(type: type, id: id, chatDeleteMode: chatDeleteMode, notice: notice), bgTask: false)
|
||||
let r: ChatResponse1 = try await chatSendCmd(.apiDeleteChat(type: type, id: id, chatDeleteMode: chatDeleteMode), bgTask: false)
|
||||
if case .direct = type, case .contactDeleted = r { return }
|
||||
if case .contactConnection = type, case .contactConnectionDeleted = r { return }
|
||||
if case .group = type, case .groupDeletedUser = r { return }
|
||||
throw r.unexpected
|
||||
}
|
||||
|
||||
func apiDeleteContact(id: Int64, chatDeleteMode: ChatDeleteMode = .full(notify: true), notice: LinkNotice? = nil) async throws -> Contact {
|
||||
func apiDeleteContact(id: Int64, chatDeleteMode: ChatDeleteMode = .full(notify: true)) async throws -> Contact {
|
||||
let type: ChatType = .direct
|
||||
let chatId = type.rawValue + id.description
|
||||
if case .full = chatDeleteMode {
|
||||
@@ -1286,7 +1278,7 @@ func apiDeleteContact(id: Int64, chatDeleteMode: ChatDeleteMode = .full(notify:
|
||||
DispatchQueue.main.async { ChatModel.shared.deletedChats.remove(chatId) }
|
||||
}
|
||||
}
|
||||
let r: ChatResponse1 = try await chatSendCmd(.apiDeleteChat(type: type, id: id, chatDeleteMode: chatDeleteMode, notice: notice), bgTask: false)
|
||||
let r: ChatResponse1 = try await chatSendCmd(.apiDeleteChat(type: type, id: id, chatDeleteMode: chatDeleteMode), bgTask: false)
|
||||
if case let .contactDeleted(_, contact) = r { return contact }
|
||||
throw r.unexpected
|
||||
}
|
||||
@@ -1305,10 +1297,10 @@ func deleteChat(_ chat: Chat, chatDeleteMode: ChatDeleteMode = .full(notify: tru
|
||||
}
|
||||
}
|
||||
|
||||
func deleteContactChat(_ chat: Chat, chatDeleteMode: ChatDeleteMode = .full(notify: true), notice: LinkNotice? = nil) async -> Alert? {
|
||||
func deleteContactChat(_ chat: Chat, chatDeleteMode: ChatDeleteMode = .full(notify: true)) async -> Alert? {
|
||||
do {
|
||||
let cInfo = chat.chatInfo
|
||||
let ct = try await apiDeleteContact(id: cInfo.apiId, chatDeleteMode: chatDeleteMode, notice: notice)
|
||||
let ct = try await apiDeleteContact(id: cInfo.apiId, chatDeleteMode: chatDeleteMode)
|
||||
await MainActor.run {
|
||||
switch chatDeleteMode {
|
||||
case .full:
|
||||
@@ -2034,8 +2026,8 @@ func apiDeleteMemberSupportChat(_ groupId: Int64, _ groupMemberId: Int64) async
|
||||
throw r.unexpected
|
||||
}
|
||||
|
||||
func apiRemoveMembers(_ groupId: Int64, _ memberIds: [Int64], _ withMessages: Bool, notice: LinkNotice? = nil) async throws -> (GroupInfo, [GroupMember]) {
|
||||
let r: ChatResponse2 = try await chatSendCmd(.apiRemoveMembers(groupId: groupId, memberIds: memberIds, withMessages: withMessages, notice: notice), bgTask: false)
|
||||
func apiRemoveMembers(_ groupId: Int64, _ memberIds: [Int64], _ withMessages: Bool) async throws -> (GroupInfo, [GroupMember]) {
|
||||
let r: ChatResponse2 = try await chatSendCmd(.apiRemoveMembers(groupId: groupId, memberIds: memberIds, withMessages: withMessages), bgTask: false)
|
||||
if case let .userDeletedMembers(_, updatedGroupInfo, members, _withMessages) = r { return (updatedGroupInfo, members) }
|
||||
throw r.unexpected
|
||||
}
|
||||
|
||||
@@ -1179,8 +1179,7 @@ private func deleteContactOrConversationDialog(
|
||||
showAlert: showAlert
|
||||
)
|
||||
) },
|
||||
id: "DeleteActiveContactDialog",
|
||||
fraction: contact.activeConn?.viaUserContactLink == nil ? 0.4 : 0.55
|
||||
id: "DeleteActiveContactDialog"
|
||||
))
|
||||
},
|
||||
.cancel()
|
||||
@@ -1194,12 +1193,11 @@ private func deleteContactMaybeErrorAlert(
|
||||
_ chat: Chat,
|
||||
_ contact: Contact,
|
||||
chatDeleteMode: ChatDeleteMode,
|
||||
notice: LinkNotice? = nil,
|
||||
_ dismissToChatList: Bool,
|
||||
_ showAlert: @escaping (SomeAlert) -> Void
|
||||
) {
|
||||
Task {
|
||||
let alert_ = await deleteContactChat(chat, chatDeleteMode: chatDeleteMode, notice: notice)
|
||||
let alert_ = await deleteContactChat(chat, chatDeleteMode: chatDeleteMode)
|
||||
if let alert = alert_ {
|
||||
showAlert(SomeAlert(alert: alert, id: "deleteContactMaybeErrorAlert, error"))
|
||||
} else {
|
||||
@@ -1269,8 +1267,6 @@ struct DeleteActiveContactDialog: View {
|
||||
var dismissToChatList: Bool
|
||||
var showAlert: (SomeAlert) -> Void
|
||||
@State private var keepConversation = false
|
||||
@State private var ban: LinkBan = .no
|
||||
@State private var reason: ReportReason = .spam
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
@@ -1278,20 +1274,16 @@ struct DeleteActiveContactDialog: View {
|
||||
Section {
|
||||
Toggle("Keep conversation", isOn: $keepConversation)
|
||||
|
||||
if contact.activeConn?.viaUserContactLink != nil {
|
||||
linkBanRows("Ban from connecting", $ban, $reason)
|
||||
}
|
||||
|
||||
Button(role: .destructive) {
|
||||
dismiss()
|
||||
deleteContactMaybeErrorAlert(chat, contact, chatDeleteMode: contactDeleteMode.toChatDeleteMode(notify: false), notice: ban.notice(reason), dismissToChatList, showAlert)
|
||||
deleteContactMaybeErrorAlert(chat, contact, chatDeleteMode: contactDeleteMode.toChatDeleteMode(notify: false), dismissToChatList, showAlert)
|
||||
} label: {
|
||||
Text("Delete without notification")
|
||||
}
|
||||
|
||||
Button(role: .destructive) {
|
||||
dismiss()
|
||||
deleteContactMaybeErrorAlert(chat, contact, chatDeleteMode: contactDeleteMode.toChatDeleteMode(notify: true), notice: ban.notice(reason), dismissToChatList, showAlert)
|
||||
deleteContactMaybeErrorAlert(chat, contact, chatDeleteMode: contactDeleteMode.toChatDeleteMode(notify: true), dismissToChatList, showAlert)
|
||||
} label: {
|
||||
Text("Delete and notify contact")
|
||||
}
|
||||
|
||||
@@ -1209,7 +1209,7 @@ struct ComposeView: View {
|
||||
await sending()
|
||||
let mc = connectCheckLinkPreview()
|
||||
let incognito = chat.chatInfo.profileChangeProhibited ? chat.chatInfo.incognito : incognitoDefault
|
||||
if let (groupInfo, relayResults) = await apiConnectPreparedGroup(groupId: chat.chatInfo.apiId, isChannel: chat.chatInfo.isChannel, incognito: incognito, msg: mc) {
|
||||
if let (groupInfo, relayResults) = await apiConnectPreparedGroup(groupId: chat.chatInfo.apiId, incognito: incognito, msg: mc) {
|
||||
await MainActor.run {
|
||||
self.chatModel.updateGroup(groupInfo)
|
||||
self.chatModel.channelRelayHostnames.removeValue(forKey: groupInfo.groupId)
|
||||
@@ -1422,7 +1422,7 @@ struct ComposeView: View {
|
||||
case .spam: NSLocalizedString("Report spam: only group moderators will see it.", comment: "report reason")
|
||||
case .profile: NSLocalizedString("Report member profile: only group moderators will see it.", comment: "report reason")
|
||||
case .community: NSLocalizedString("Report violation: only group moderators will see it.", comment: "report reason")
|
||||
case .content: NSLocalizedString("Report content: only group moderators will see it.", comment: "report reason")
|
||||
case .illegal: NSLocalizedString("Report content: only group moderators will see it.", comment: "report reason")
|
||||
case .other: NSLocalizedString("Report other: only group moderators will see it.", comment: "report reason")
|
||||
case .unknown: "" // Should never happen
|
||||
}
|
||||
|
||||
@@ -45,16 +45,11 @@ struct ContextPendingMemberActionsView: View {
|
||||
}
|
||||
|
||||
func showRejectMemberAlert(_ groupInfo: GroupInfo, _ member: GroupMember, dismiss: DismissAction? = nil) {
|
||||
showMemberBanAlert(
|
||||
groupInfo,
|
||||
member,
|
||||
NSLocalizedString("Reject member?", comment: "alert title"),
|
||||
message: nil,
|
||||
actions: [
|
||||
LinkBanAction(title: NSLocalizedString("Reject", comment: "alert action")) { notice in
|
||||
removeMember(groupInfo, member, withMessages: false, notice: notice, dismiss: dismiss)
|
||||
}
|
||||
]
|
||||
showAlert(
|
||||
title: NSLocalizedString("Reject member?", comment: "alert title"),
|
||||
buttonTitle: "Reject",
|
||||
buttonAction: { removeMember(groupInfo, member, withMessages: false, dismiss: dismiss) },
|
||||
cancelButton: true
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -874,14 +874,7 @@ struct GroupChatInfoView: View {
|
||||
@ViewBuilder private func deleteGroupButton() -> some View {
|
||||
let label: LocalizedStringKey = groupInfo.useRelays ? "Delete channel" : groupInfo.businessChat == nil ? "Delete group" : "Delete chat"
|
||||
Button(role: .destructive) {
|
||||
if canBanOnChatDeletion(groupInfo) {
|
||||
showDeleteBusinessChatAlert(chat) {
|
||||
dismiss()
|
||||
chatModel.chatId = nil
|
||||
}
|
||||
} else {
|
||||
alert = .deleteGroupAlert
|
||||
}
|
||||
alert = .deleteGroupAlert
|
||||
} label: {
|
||||
Label(label, systemImage: "trash")
|
||||
.foregroundColor(Color.red)
|
||||
@@ -1017,54 +1010,43 @@ func showRemoveMemberAlert(_ groupInfo: GroupInfo, _ mem: GroupMember, dismiss:
|
||||
cancelAlertAction
|
||||
]}
|
||||
)
|
||||
} else if groupInfo.useRelays {
|
||||
showAlert(
|
||||
NSLocalizedString("Remove subscriber?", comment: "alert title"),
|
||||
message: NSLocalizedString("Subscriber will be removed from channel - this cannot be undone!", comment: "alert message"),
|
||||
actions: {[
|
||||
UIAlertAction(title: NSLocalizedString("Remove", comment: "alert action"), style: .destructive) { _ in
|
||||
removeMember(groupInfo, mem, withMessages: false, dismiss: dismiss)
|
||||
},
|
||||
UIAlertAction(title: NSLocalizedString("Remove and delete messages", comment: "alert action"), style: .destructive) { _ in
|
||||
removeMember(groupInfo, mem, withMessages: true, dismiss: dismiss)
|
||||
},
|
||||
cancelAlertAction
|
||||
]}
|
||||
)
|
||||
} else {
|
||||
showMemberBanAlert(
|
||||
groupInfo,
|
||||
mem,
|
||||
groupInfo.useRelays
|
||||
? NSLocalizedString("Remove subscriber?", comment: "alert title")
|
||||
: NSLocalizedString("Remove member?", comment: "alert title"),
|
||||
message: groupInfo.useRelays
|
||||
? NSLocalizedString("Subscriber will be removed from channel - this cannot be undone!", comment: "alert message")
|
||||
: groupInfo.businessChat == nil
|
||||
showAlert(
|
||||
NSLocalizedString("Remove member?", comment: "alert title"),
|
||||
message: groupInfo.businessChat == nil
|
||||
? NSLocalizedString("Member will be removed from group - this cannot be undone!", comment: "alert message")
|
||||
: NSLocalizedString("Member will be removed from chat - this cannot be undone!", comment: "alert message"),
|
||||
actions: [
|
||||
LinkBanAction(title: NSLocalizedString("Remove", comment: "alert action")) { notice in
|
||||
removeMember(groupInfo, mem, withMessages: false, notice: notice, dismiss: dismiss)
|
||||
actions: {[
|
||||
UIAlertAction(title: NSLocalizedString("Remove", comment: "alert action"), style: .destructive) { _ in
|
||||
removeMember(groupInfo, mem, withMessages: false, dismiss: dismiss)
|
||||
},
|
||||
LinkBanAction(title: NSLocalizedString("Remove and delete messages", comment: "alert action")) { notice in
|
||||
removeMember(groupInfo, mem, withMessages: true, notice: notice, dismiss: dismiss)
|
||||
}
|
||||
]
|
||||
UIAlertAction(title: NSLocalizedString("Remove and delete messages", comment: "alert action"), style: .destructive) { _ in
|
||||
removeMember(groupInfo, mem, withMessages: true, dismiss: dismiss)
|
||||
},
|
||||
cancelAlertAction
|
||||
]}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func showMemberBanAlert(_ groupInfo: GroupInfo, _ mem: GroupMember, _ title: String, message: String?, actions: [LinkBanAction]) {
|
||||
if mem.memberStatus == .memInvited {
|
||||
showAlert(title, message: message) {
|
||||
actions.map { a in
|
||||
UIAlertAction(title: a.title, style: .destructive) { _ in a.action(nil) }
|
||||
} + [cancelAlertAction]
|
||||
}
|
||||
} else {
|
||||
showAppSheet(detents: [.medium(), .large()]) {
|
||||
LinkBanDialog(
|
||||
title: title,
|
||||
message: message,
|
||||
banLabel: groupInfo.businessChat == nil ? "Ban from joining" : "Ban from connecting",
|
||||
ban: .day,
|
||||
actions: actions
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeMember(_ groupInfo: GroupInfo, _ mem: GroupMember, withMessages: Bool, notice: LinkNotice? = nil, dismiss: DismissAction?) {
|
||||
func removeMember(_ groupInfo: GroupInfo, _ mem: GroupMember, withMessages: Bool, dismiss: DismissAction?) {
|
||||
Task {
|
||||
do {
|
||||
let (updatedGroupInfo, updatedMembers) = try await apiRemoveMembers(groupInfo.groupId, [mem.groupMemberId], withMessages, notice: notice)
|
||||
let (updatedGroupInfo, updatedMembers) = try await apiRemoveMembers(groupInfo.groupId, [mem.groupMemberId], withMessages)
|
||||
await MainActor.run {
|
||||
ChatModel.shared.updateGroup(updatedGroupInfo)
|
||||
updatedMembers.forEach { updatedMember in
|
||||
@@ -1087,128 +1069,6 @@ func removeMember(_ groupInfo: GroupInfo, _ mem: GroupMember, withMessages: Bool
|
||||
}
|
||||
}
|
||||
|
||||
enum LinkBan: Identifiable, Hashable {
|
||||
case no
|
||||
case hour
|
||||
case day
|
||||
case week
|
||||
case month
|
||||
case permanent
|
||||
|
||||
static let values: [LinkBan] = [.no, .hour, .day, .week, .month, .permanent]
|
||||
|
||||
var id: Self { self }
|
||||
|
||||
var text: String {
|
||||
switch self {
|
||||
case .no: NSLocalizedString("No", comment: "ban duration")
|
||||
case .hour: NSLocalizedString("1 hour", comment: "ban duration")
|
||||
case .day: NSLocalizedString("1 day", comment: "ban duration")
|
||||
case .week: NSLocalizedString("1 week", comment: "ban duration")
|
||||
case .month: NSLocalizedString("1 month", comment: "ban duration")
|
||||
case .permanent: NSLocalizedString("Permanently", comment: "ban duration")
|
||||
}
|
||||
}
|
||||
|
||||
func notice(_ reason: ReportReason) -> LinkNotice? {
|
||||
switch self {
|
||||
case .no: nil
|
||||
case .hour: LinkNotice(ttl: 3600, reason: reason)
|
||||
case .day: LinkNotice(ttl: 86400, reason: reason)
|
||||
case .week: LinkNotice(ttl: 604800, reason: reason)
|
||||
case .month: LinkNotice(ttl: 2592000, reason: reason)
|
||||
case .permanent: LinkNotice(ttl: nil, reason: reason)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LinkBanAction {
|
||||
var title: String
|
||||
var action: (LinkNotice?) -> Void
|
||||
}
|
||||
|
||||
@ViewBuilder func linkBanRows(_ label: LocalizedStringKey, _ ban: Binding<LinkBan>, _ reason: Binding<ReportReason>) -> some View {
|
||||
WrappedPicker(label, selection: ban) {
|
||||
ForEach(LinkBan.values) { Text($0.text) }
|
||||
}
|
||||
if ban.wrappedValue != .no {
|
||||
WrappedPicker("Reason", selection: reason) {
|
||||
ForEach(ReportReason.supportedReasons, id: \.self) { Text($0.text) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LinkBanDialog: View {
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var title: String
|
||||
var message: String?
|
||||
var banLabel: LocalizedStringKey
|
||||
@State var ban: LinkBan
|
||||
var actions: [LinkBanAction]
|
||||
@State private var reason: ReportReason = .spam
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
List {
|
||||
Section {
|
||||
linkBanRows(banLabel, $ban, $reason)
|
||||
ForEach(actions.indices, id: \.self) { i in
|
||||
Button(actions[i].title, role: .destructive) {
|
||||
dismiss()
|
||||
actions[i].action(ban.notice(reason))
|
||||
}
|
||||
}
|
||||
Button("Cancel", role: .cancel) { dismiss() }
|
||||
} footer: {
|
||||
if let message {
|
||||
Text(message).foregroundColor(theme.colors.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle(title)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.modifier(ThemedBackground(grouped: true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func showDeleteBusinessChatAlert(_ chat: Chat, deleted: (() -> Void)? = nil) {
|
||||
showAppSheet(detents: [.medium(), .large()]) {
|
||||
LinkBanDialog(
|
||||
title: NSLocalizedString("Delete chat?", comment: "alert title"),
|
||||
message: chat.chatInfo.displayName + "\n\n" + NSLocalizedString("Chat will be deleted for all members - this cannot be undone!", comment: "alert message"),
|
||||
banLabel: "Ban from connecting",
|
||||
ban: .no,
|
||||
actions: [
|
||||
LinkBanAction(title: NSLocalizedString("Delete", comment: "alert action")) { notice in
|
||||
Task {
|
||||
do {
|
||||
try await apiDeleteChat(type: chat.chatInfo.chatType, id: chat.chatInfo.apiId, notice: notice)
|
||||
await MainActor.run {
|
||||
deleted?()
|
||||
ChatModel.shared.removeChat(chat.chatInfo.id)
|
||||
}
|
||||
} catch let error {
|
||||
logger.error("showDeleteBusinessChatAlert apiDeleteChat error: \(responseError(error))")
|
||||
await MainActor.run {
|
||||
showAlert(
|
||||
NSLocalizedString("Error deleting chat!", comment: "alert title"),
|
||||
message: responseError(error)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func canBanOnChatDeletion(_ groupInfo: GroupInfo) -> Bool {
|
||||
groupInfo.businessChat?.chatType == .customer && groupInfo.membership.memberCurrent
|
||||
}
|
||||
|
||||
func deleteGroupAlertMessage(_ groupInfo: GroupInfo) -> Text {
|
||||
groupInfo.useRelays ? (
|
||||
groupInfo.membership.memberCurrent
|
||||
|
||||
@@ -446,7 +446,7 @@ struct ChatListNavLink: View {
|
||||
|
||||
if canShowGroupDelete, let gi = groupInfo, gi.canDelete {
|
||||
buttons.append(.destructive(Text("Delete")) {
|
||||
showDeleteGroupAlert(gi)
|
||||
AlertManager.shared.showAlert(deleteGroupAlert(gi))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -484,23 +484,13 @@ struct ChatListNavLink: View {
|
||||
|
||||
private func deleteGroupChatButton(_ groupInfo: GroupInfo) -> some View {
|
||||
Button {
|
||||
showDeleteGroupAlert(groupInfo)
|
||||
AlertManager.shared.showAlert(deleteGroupAlert(groupInfo))
|
||||
} label: {
|
||||
deleteLabel
|
||||
}
|
||||
.tint(.red)
|
||||
}
|
||||
|
||||
private func showDeleteGroupAlert(_ groupInfo: GroupInfo) {
|
||||
if canBanOnChatDeletion(groupInfo) {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
||||
showDeleteBusinessChatAlert(chat)
|
||||
}
|
||||
} else {
|
||||
AlertManager.shared.showAlert(deleteGroupAlert(groupInfo))
|
||||
}
|
||||
}
|
||||
|
||||
// Spec: spec/client/chat-list.md#contactRequestNavLink
|
||||
private func contactRequestNavLink(_ contactRequest: UserContactRequest) -> some View {
|
||||
ContactRequestView(contactRequest: contactRequest, chat: chat)
|
||||
|
||||
@@ -43,7 +43,7 @@ struct ContactListNavLink: View {
|
||||
.sheet(item: $sheet) {
|
||||
if #available(iOS 16.0, *) {
|
||||
$0.content
|
||||
.presentationDetents([.fraction($0.fraction)])
|
||||
.presentationDetents([.fraction(0.4)])
|
||||
} else {
|
||||
$0.content
|
||||
}
|
||||
|
||||
@@ -32,16 +32,13 @@ private struct PrivacySensitive: ViewModifier {
|
||||
// Presented from the top view controller instead of a .sheet on a parent view, so an alert button or
|
||||
// a view that is itself in a sheet can open it.
|
||||
@discardableResult
|
||||
func showAppSheet<Content: View>(detents: [UISheetPresentationController.Detent]? = nil, @ViewBuilder content: () -> Content) -> UIViewController? {
|
||||
func showAppSheet<Content: View>(@ViewBuilder content: () -> Content) -> UIViewController? {
|
||||
if let topController = getTopViewController() {
|
||||
let v = content()
|
||||
.modifier(PrivacySensitive())
|
||||
.environmentObject(ChatModel.shared)
|
||||
.environmentObject(AppTheme.shared)
|
||||
let sheet = UIHostingController(rootView: v)
|
||||
if let detents, let presentation = sheet.sheetPresentationController {
|
||||
presentation.detents = detents
|
||||
}
|
||||
topController.present(sheet, animated: true)
|
||||
return sheet
|
||||
}
|
||||
|
||||
@@ -920,46 +920,6 @@ private func showInvitationLinkConnectingAlert(cleanup: (() -> Void)?) {
|
||||
)
|
||||
}
|
||||
|
||||
enum LinkNoticeTarget {
|
||||
case group
|
||||
case channel
|
||||
case address
|
||||
case link
|
||||
}
|
||||
|
||||
func showLinkNoticeAlert(_ target: LinkNoticeTarget, expiresAt: Date?, reason: ReportReason?, cleanup: (() -> Void)? = nil) {
|
||||
let join = target == .group || target == .channel
|
||||
let banned = switch target {
|
||||
case .group: NSLocalizedString("You were banned from this group.", comment: "alert message")
|
||||
case .channel: NSLocalizedString("You were banned from this channel.", comment: "alert message")
|
||||
case .address: NSLocalizedString("You were banned from connecting via this address.", comment: "alert message")
|
||||
case .link: NSLocalizedString("You were banned from connecting via this link.", comment: "alert message")
|
||||
}
|
||||
let until =
|
||||
if let expiresAt {
|
||||
String.localizedStringWithFormat(
|
||||
join
|
||||
? NSLocalizedString("You can join again after %@.", comment: "alert message")
|
||||
: NSLocalizedString("You can connect again after %@.", comment: "alert message"),
|
||||
expiresAt.formatted(date: .abbreviated, time: .shortened)
|
||||
)
|
||||
} else {
|
||||
join
|
||||
? NSLocalizedString("You can't join it again.", comment: "alert message")
|
||||
: NSLocalizedString("You can't connect again.", comment: "alert message")
|
||||
}
|
||||
let reasonLine: String? =
|
||||
if case .some(.unknown) = reason { nil }
|
||||
else { reason.map { String.localizedStringWithFormat(NSLocalizedString("Reason: %@.", comment: "alert message"), $0.text) } }
|
||||
showAlert(
|
||||
NSLocalizedString("Banned", comment: "alert title"),
|
||||
message: [banned, until, reasonLine].compactMap { $0 }.joined(separator: "\n"),
|
||||
actions: {[
|
||||
okCleanupAlertAction(cleanup: cleanup)
|
||||
]}
|
||||
)
|
||||
}
|
||||
|
||||
private func showGroupLinkConnectingAlert(groupInfo: GroupInfo?, cleanup: (() -> Void)?) {
|
||||
if let groupInfo = groupInfo {
|
||||
if groupInfo.businessChat == nil {
|
||||
@@ -1557,11 +1517,6 @@ func planAndConnect(
|
||||
cleanup: cleanup
|
||||
)
|
||||
}
|
||||
case let .linkNotice(expiresAt, reason):
|
||||
logger.debug("planAndConnect, .contactAddress, .linkNotice")
|
||||
await MainActor.run {
|
||||
showLinkNoticeAlert(.address, expiresAt: expiresAt, reason: reason, cleanup: cleanup)
|
||||
}
|
||||
}
|
||||
case let .groupLink(glp):
|
||||
switch glp {
|
||||
@@ -1692,14 +1647,6 @@ func planAndConnect(
|
||||
cleanup?()
|
||||
}
|
||||
}
|
||||
case let .linkNotice(expiresAt, reason):
|
||||
logger.debug("planAndConnect, .groupLink, .linkNotice")
|
||||
let isChannel =
|
||||
if case .link(_, .channel, _) = strConnectTarget(shortOrFullLink) { true }
|
||||
else { planSimplexName?.nameType == .publicGroup }
|
||||
await MainActor.run {
|
||||
showLinkNoticeAlert(isChannel ? .channel : .group, expiresAt: expiresAt, reason: reason, cleanup: cleanup)
|
||||
}
|
||||
}
|
||||
case let .error(chatError):
|
||||
logger.debug("planAndConnect, .error \(chatErrorString(chatError))")
|
||||
|
||||
@@ -768,7 +768,6 @@ public enum ChatErrorType: Decodable, Hashable {
|
||||
case unsupportedConnReq
|
||||
case invalidChatMessage(connection: Connection, message: String)
|
||||
case connReqMessageProhibited
|
||||
case linkNotice(expiresAt: Date?, reason: ReportReason?)
|
||||
case contactNotReady(contact: Contact)
|
||||
case contactNotActive(contact: Contact)
|
||||
case contactDisabled(contact: Contact)
|
||||
|
||||
@@ -2638,7 +2638,6 @@ public struct Connection: Decodable, Hashable {
|
||||
public var peerChatVRange: VersionRange
|
||||
public var connStatus: ConnStatus
|
||||
public var connLevel: Int
|
||||
public var viaUserContactLink: Int64?
|
||||
public var viaGroupLink: Bool
|
||||
public var customUserProfileId: Int64?
|
||||
public var connectionCode: SecurityCode?
|
||||
@@ -2652,7 +2651,7 @@ public struct Connection: Decodable, Hashable {
|
||||
public var connectionStats: ConnectionStats? = nil
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case connId, agentConnId, peerChatVRange, connStatus, connLevel, viaUserContactLink, viaGroupLink, customUserProfileId, connectionCode, pqSupport, pqEncryption, pqSndEnabled, pqRcvEnabled, authErrCounter, quotaErrCounter
|
||||
case connId, agentConnId, peerChatVRange, connStatus, connLevel, viaGroupLink, customUserProfileId, connectionCode, pqSupport, pqEncryption, pqSndEnabled, pqRcvEnabled, authErrCounter, quotaErrCounter
|
||||
}
|
||||
|
||||
public var id: ChatId { get { ":\(connId)" } }
|
||||
@@ -5928,29 +5927,18 @@ public enum FormatColor: String, Decodable, Hashable {
|
||||
|
||||
public enum ReportReason: Hashable {
|
||||
case spam
|
||||
case content
|
||||
case illegal
|
||||
case community
|
||||
case profile
|
||||
case other
|
||||
case unknown(type: String)
|
||||
|
||||
public static var supportedReasons: [ReportReason] = [.spam, .content, .community, .profile, .other]
|
||||
|
||||
public var rawValue: String {
|
||||
switch self {
|
||||
case .spam: "spam"
|
||||
case .content: "content"
|
||||
case .community: "community"
|
||||
case .profile: "profile"
|
||||
case .other: "other"
|
||||
case let .unknown(type): type
|
||||
}
|
||||
}
|
||||
public static var supportedReasons: [ReportReason] = [.spam, .illegal, .community, .profile, .other]
|
||||
|
||||
public var text: String {
|
||||
switch self {
|
||||
case .spam: return NSLocalizedString("Spam", comment: "report reason")
|
||||
case .content: return NSLocalizedString("Inappropriate content", comment: "report reason")
|
||||
case .illegal: return NSLocalizedString("Inappropriate content", comment: "report reason")
|
||||
case .community: return NSLocalizedString("Community guidelines violation", comment: "report reason")
|
||||
case .profile: return NSLocalizedString("Inappropriate profile", comment: "report reason")
|
||||
case .other: return NSLocalizedString("Another reason", comment: "report reason")
|
||||
@@ -5970,7 +5958,14 @@ public enum ReportReason: Hashable {
|
||||
extension ReportReason: Encodable {
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
try container.encode(rawValue)
|
||||
switch self {
|
||||
case .spam: try container.encode("spam")
|
||||
case .illegal: try container.encode("illegal")
|
||||
case .community: try container.encode("community")
|
||||
case .profile: try container.encode("profile")
|
||||
case .other: try container.encode("other")
|
||||
case let .unknown(type): try container.encode(type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5980,7 +5975,7 @@ extension ReportReason: Decodable {
|
||||
let type = try container.decode(String.self)
|
||||
switch type {
|
||||
case "spam": self = .spam
|
||||
case "content", "illegal": self = .content
|
||||
case "illegal": self = .illegal
|
||||
case "community": self = .community
|
||||
case "profile": self = .profile
|
||||
case "other": self = .other
|
||||
|
||||
+5
-6
@@ -2053,7 +2053,6 @@ data class Connection(
|
||||
val peerChatVRange: VersionRange,
|
||||
val connStatus: ConnStatus,
|
||||
val connLevel: Int,
|
||||
val viaUserContactLink: Long? = null,
|
||||
val viaGroupLink: Boolean,
|
||||
val customUserProfileId: Long? = null,
|
||||
val connectionCode: SecurityCode? = null,
|
||||
@@ -5415,19 +5414,19 @@ enum class FormatColor(val color: String) {
|
||||
@Serializable(with = ReportReasonSerializer::class)
|
||||
sealed class ReportReason {
|
||||
@Serializable @SerialName("spam") object Spam: ReportReason()
|
||||
@Serializable @SerialName("content") object Content: ReportReason()
|
||||
@Serializable @SerialName("illegal") object Illegal: ReportReason()
|
||||
@Serializable @SerialName("community") object Community: ReportReason()
|
||||
@Serializable @SerialName("profile") object Profile: ReportReason()
|
||||
@Serializable @SerialName("other") object Other: ReportReason()
|
||||
@Serializable @SerialName("unknown") data class Unknown(val type: String): ReportReason()
|
||||
|
||||
companion object {
|
||||
val supportedReasons: List<ReportReason> = listOf(Spam, Content, Community, Profile, Other)
|
||||
val supportedReasons: List<ReportReason> = listOf(Spam, Illegal, Community, Profile, Other)
|
||||
}
|
||||
|
||||
val text: String get() = when (this) {
|
||||
Spam -> generalGetString(MR.strings.report_reason_spam)
|
||||
Content -> generalGetString(MR.strings.report_reason_illegal)
|
||||
Illegal -> generalGetString(MR.strings.report_reason_illegal)
|
||||
Community -> generalGetString(MR.strings.report_reason_community)
|
||||
Profile -> generalGetString(MR.strings.report_reason_profile)
|
||||
Other -> generalGetString(MR.strings.report_reason_other)
|
||||
@@ -5442,7 +5441,7 @@ object ReportReasonSerializer : KSerializer<ReportReason> {
|
||||
override fun deserialize(decoder: Decoder): ReportReason {
|
||||
return when (val value = decoder.decodeString()) {
|
||||
"spam" -> ReportReason.Spam
|
||||
"content", "illegal" -> ReportReason.Content
|
||||
"illegal" -> ReportReason.Illegal
|
||||
"community" -> ReportReason.Community
|
||||
"profile" -> ReportReason.Profile
|
||||
"other" -> ReportReason.Other
|
||||
@@ -5453,7 +5452,7 @@ object ReportReasonSerializer : KSerializer<ReportReason> {
|
||||
override fun serialize(encoder: Encoder, value: ReportReason) {
|
||||
val stringValue = when (value) {
|
||||
is ReportReason.Spam -> "spam"
|
||||
is ReportReason.Content -> "content"
|
||||
is ReportReason.Illegal -> "illegal"
|
||||
is ReportReason.Community -> "community"
|
||||
is ReportReason.Profile -> "profile"
|
||||
is ReportReason.Other -> "other"
|
||||
|
||||
+17
-34
@@ -36,8 +36,6 @@ import chat.simplex.common.views.chat.item.showQuotedItemDoesNotExistAlert
|
||||
import chat.simplex.common.views.chatlist.openGroupChat
|
||||
import chat.simplex.common.views.database.deleteDatabaseBackups
|
||||
import chat.simplex.common.views.migration.MigrationFileLinkData
|
||||
import chat.simplex.common.views.newchat.LinkNoticeTarget
|
||||
import chat.simplex.common.views.newchat.showLinkNoticeAlert
|
||||
import chat.simplex.common.views.onboarding.OnboardingStage
|
||||
import chat.simplex.common.views.usersettings.*
|
||||
import chat.simplex.common.views.usersettings.networkAndServers.defaultConditionsLink
|
||||
@@ -1699,12 +1697,8 @@ object ChatController {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun apiConnectResponseAlert(r: API, noticeTarget: LinkNoticeTarget = LinkNoticeTarget.Link) {
|
||||
private fun apiConnectResponseAlert(r: API) {
|
||||
when {
|
||||
r is API.Error && r.err is ChatError.ChatErrorChat
|
||||
&& r.err.errorType is ChatErrorType.LinkNotice -> {
|
||||
showLinkNoticeAlert(noticeTarget, r.err.errorType.expiresAt, r.err.errorType.reason)
|
||||
}
|
||||
r is API.Error && r.err is ChatError.ChatErrorChat
|
||||
&& r.err.errorType is ChatErrorType.InvalidConnReq -> {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
@@ -1865,17 +1859,17 @@ object ChatController {
|
||||
if (r is API.Result && r.res is CR.StartedConnectionToContact) return r.res.contact
|
||||
if (r != null) {
|
||||
Log.e(TAG, "apiConnectPreparedContact bad response: ${r.responseType} ${r.details}")
|
||||
apiConnectResponseAlert(r, LinkNoticeTarget.Address)
|
||||
apiConnectResponseAlert(r)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiConnectPreparedGroup(rh: Long?, groupId: Long, isChannel: Boolean, incognito: Boolean, msg: MsgContent?): Pair<GroupInfo, List<RelayConnectionResult>>? {
|
||||
suspend fun apiConnectPreparedGroup(rh: Long?, groupId: Long, incognito: Boolean, msg: MsgContent?): Pair<GroupInfo, List<RelayConnectionResult>>? {
|
||||
val r = sendCmdWithRetry(rh, CC.APIConnectPreparedGroup(groupId, incognito, msg))
|
||||
if (r is API.Result && r.res is CR.StartedConnectionToGroup) return Pair(r.res.groupInfo, r.res.relayResults)
|
||||
if (r != null) {
|
||||
Log.e(TAG, "apiConnectPreparedGroup bad response: ${r.responseType} ${r.details}")
|
||||
apiConnectResponseAlert(r, if (isChannel) LinkNoticeTarget.Channel else LinkNoticeTarget.Group)
|
||||
apiConnectResponseAlert(r)
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -1885,26 +1879,24 @@ object ChatController {
|
||||
val r = sendCmdWithRetry(rh, CC.ApiConnectContactViaAddress(userId, incognito, contactId))
|
||||
if (r is API.Result && r.res is CR.SentInvitationToContact) return r.res.contact
|
||||
if (r == null) return null
|
||||
if (r is API.Error && r.err is ChatError.ChatErrorChat && r.err.errorType is ChatErrorType.LinkNotice) {
|
||||
showLinkNoticeAlert(LinkNoticeTarget.Address, r.err.errorType.expiresAt, r.err.errorType.reason)
|
||||
} else if (!(networkErrorAlert(r))) {
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiConnectContactViaAddress", generalGetString(MR.strings.connection_error), r)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun deleteChat(chat: Chat, chatDeleteMode: ChatDeleteMode = ChatDeleteMode.Full(notify = true), notice: LinkNotice? = null) {
|
||||
suspend fun deleteChat(chat: Chat, chatDeleteMode: ChatDeleteMode = ChatDeleteMode.Full(notify = true)) {
|
||||
val cInfo = chat.chatInfo
|
||||
if (apiDeleteChat(rh = chat.remoteHostId, type = cInfo.chatType, id = cInfo.apiId, chatDeleteMode = chatDeleteMode, notice = notice)) {
|
||||
if (apiDeleteChat(rh = chat.remoteHostId, type = cInfo.chatType, id = cInfo.apiId, chatDeleteMode = chatDeleteMode)) {
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.removeChat(chat.remoteHostId, cInfo.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun apiDeleteChat(rh: Long?, type: ChatType, id: Long, chatDeleteMode: ChatDeleteMode = ChatDeleteMode.Full(notify = true), notice: LinkNotice? = null): Boolean {
|
||||
suspend fun apiDeleteChat(rh: Long?, type: ChatType, id: Long, chatDeleteMode: ChatDeleteMode = ChatDeleteMode.Full(notify = true)): Boolean {
|
||||
chatModel.deletedChats.value += rh to type.type + id
|
||||
val r = sendCmd(rh, CC.ApiDeleteChat(type, id, chatDeleteMode, notice))
|
||||
val r = sendCmd(rh, CC.ApiDeleteChat(type, id, chatDeleteMode))
|
||||
val res = r.result
|
||||
val success = when {
|
||||
res is CR.ContactDeleted && type == ChatType.Direct -> true
|
||||
@@ -1926,10 +1918,10 @@ object ChatController {
|
||||
return success
|
||||
}
|
||||
|
||||
suspend fun apiDeleteContact(rh: Long?, id: Long, chatDeleteMode: ChatDeleteMode = ChatDeleteMode.Full(notify = true), notice: LinkNotice? = null): Contact? {
|
||||
suspend fun apiDeleteContact(rh: Long?, id: Long, chatDeleteMode: ChatDeleteMode = ChatDeleteMode.Full(notify = true)): Contact? {
|
||||
val type = ChatType.Direct
|
||||
chatModel.deletedChats.value += rh to type.type + id
|
||||
val r = sendCmd(rh, CC.ApiDeleteChat(type, id, chatDeleteMode, notice))
|
||||
val r = sendCmd(rh, CC.ApiDeleteChat(type, id, chatDeleteMode))
|
||||
val contact = if (r is API.Result && r.res is CR.ContactDeleted) {
|
||||
r.res.contact
|
||||
} else {
|
||||
@@ -2505,8 +2497,8 @@ object ChatController {
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiRemoveMembers(rh: Long?, groupId: Long, memberIds: List<Long>, withMessages: Boolean, notice: LinkNotice? = null): Pair<GroupInfo, List<GroupMember>>? {
|
||||
val r = sendCmd(rh, CC.ApiRemoveMembers(groupId, memberIds, withMessages, notice))
|
||||
suspend fun apiRemoveMembers(rh: Long?, groupId: Long, memberIds: List<Long>, withMessages: Boolean): Pair<GroupInfo, List<GroupMember>>? {
|
||||
val r = sendCmd(rh, CC.ApiRemoveMembers(groupId, memberIds, withMessages))
|
||||
if (r is API.Result && r.res is CR.UserDeletedMembers) return r.res.groupInfo to r.res.members
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiRemoveMembers", generalGetString(MR.strings.error_removing_member), r)
|
||||
@@ -4052,7 +4044,7 @@ sealed class CC {
|
||||
class ApiDeleteMemberSupportChat(val groupId: Long, val groupMemberId: Long): CC()
|
||||
class ApiMembersRole(val groupId: Long, val memberIds: List<Long>, val memberRole: GroupMemberRole): CC()
|
||||
class ApiBlockMembersForAll(val groupId: Long, val memberIds: List<Long>, val blocked: Boolean): CC()
|
||||
class ApiRemoveMembers(val groupId: Long, val memberIds: List<Long>, val withMessages: Boolean, val notice: LinkNotice?): CC()
|
||||
class ApiRemoveMembers(val groupId: Long, val memberIds: List<Long>, val withMessages: Boolean): CC()
|
||||
class ApiLeaveGroup(val groupId: Long): CC()
|
||||
class ApiListMembers(val groupId: Long): CC()
|
||||
class ApiUpdateGroupProfile(val groupId: Long, val groupProfile: GroupProfile): CC()
|
||||
@@ -4112,7 +4104,7 @@ sealed class CC {
|
||||
class APIConnectPreparedGroup(val groupId: Long, val incognito: Boolean, val msg: MsgContent?): CC()
|
||||
class APIConnect(val userId: Long, val incognito: Boolean, val connLink: CreatedConnLink): CC()
|
||||
class ApiConnectContactViaAddress(val userId: Long, val incognito: Boolean, val contactId: Long): CC()
|
||||
class ApiDeleteChat(val type: ChatType, val id: Long, val chatDeleteMode: ChatDeleteMode, val notice: LinkNotice?): CC()
|
||||
class ApiDeleteChat(val type: ChatType, val id: Long, val chatDeleteMode: ChatDeleteMode): CC()
|
||||
class ApiClearChat(val type: ChatType, val id: Long): CC()
|
||||
class ApiListContacts(val userId: Long): CC()
|
||||
class ApiUpdateProfile(val userId: Long, val profile: Profile): CC()
|
||||
@@ -4270,7 +4262,7 @@ sealed class CC {
|
||||
is ApiDeleteMemberSupportChat -> "/_delete member chat #$groupId $groupMemberId"
|
||||
is ApiMembersRole -> "/_member role #$groupId ${memberIds.joinToString(",")} ${memberRole.memberRole}"
|
||||
is ApiBlockMembersForAll -> "/_block #$groupId ${memberIds.joinToString(",")} blocked=${onOff(blocked)}"
|
||||
is ApiRemoveMembers -> "/_remove #$groupId ${memberIds.joinToString(",")} messages=${onOff(withMessages)}${maybeNotice(notice)}"
|
||||
is ApiRemoveMembers -> "/_remove #$groupId ${memberIds.joinToString(",")} messages=${onOff(withMessages)}"
|
||||
is ApiLeaveGroup -> "/_leave #$groupId"
|
||||
is ApiListMembers -> "/_members #$groupId"
|
||||
is ApiUpdateGroupProfile -> "/_group_profile #$groupId ${json.encodeToString(groupProfile)}"
|
||||
@@ -4333,7 +4325,7 @@ sealed class CC {
|
||||
is APIConnectPreparedGroup -> "/_connect group #$groupId incognito=${onOff(incognito)}${maybeContent(msg)}"
|
||||
is APIConnect -> "/_connect $userId incognito=${onOff(incognito)} ${connLink.connFullLink} ${connLink.connShortLink ?: ""}"
|
||||
is ApiConnectContactViaAddress -> "/_connect contact $userId incognito=${onOff(incognito)} $contactId"
|
||||
is ApiDeleteChat -> "/_delete ${chatRef(type, id, scope = null)} ${chatDeleteMode.cmdString}${maybeNotice(notice)}"
|
||||
is ApiDeleteChat -> "/_delete ${chatRef(type, id, scope = null)} ${chatDeleteMode.cmdString}"
|
||||
is ApiClearChat -> "/_clear chat ${chatRef(type, id, scope = null)}"
|
||||
is ApiListContacts -> "/_contacts $userId"
|
||||
is ApiUpdateProfile -> "/_profile $userId ${json.encodeToString(profile)}"
|
||||
@@ -4633,8 +4625,6 @@ sealed class CC {
|
||||
}
|
||||
}
|
||||
|
||||
private fun maybeNotice(notice: LinkNotice?): String = if (notice == null) "" else " notice=" + json.encodeToString(notice)
|
||||
|
||||
companion object {
|
||||
fun chatRef(chatType: ChatType, id: Long, scope: GroupChatScope?) = when (scope) {
|
||||
null -> "${chatType.type}${id}"
|
||||
@@ -7430,9 +7420,6 @@ sealed class ChatDeleteMode {
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class LinkNotice(val ttl: Long? = null, val reason: ReportReason? = null)
|
||||
|
||||
@Serializable
|
||||
data class CreatedConnLink(val connFullLink: String, val connShortLink: String?) {
|
||||
fun simplexChatUri(short: Boolean): String =
|
||||
@@ -7616,7 +7603,6 @@ sealed class ContactAddressPlan {
|
||||
@Serializable @SerialName("connectingProhibit") class ConnectingProhibit(val contact: Contact): ContactAddressPlan()
|
||||
@Serializable @SerialName("known") class Known(val contact: Contact): ContactAddressPlan()
|
||||
@Serializable @SerialName("contactViaAddress") class ContactViaAddress(val contact: Contact): ContactAddressPlan()
|
||||
@Serializable @SerialName("linkNotice") class LinkNotice(val expiresAt: Instant? = null, val reason: ReportReason? = null): ContactAddressPlan()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@@ -7628,7 +7614,6 @@ sealed class GroupLinkPlan {
|
||||
@Serializable @SerialName("known") class Known(val groupInfo: GroupInfo): GroupLinkPlan()
|
||||
@Serializable @SerialName("noRelays") class NoRelays(val groupSLinkData_: GroupShortLinkData? = null): GroupLinkPlan()
|
||||
@Serializable @SerialName("updateRequired") class UpdateRequired(val groupSLinkData_: GroupShortLinkData? = null): GroupLinkPlan()
|
||||
@Serializable @SerialName("linkNotice") class LinkNotice(val expiresAt: Instant? = null, val reason: ReportReason? = null): GroupLinkPlan()
|
||||
}
|
||||
|
||||
abstract class TerminalItem {
|
||||
@@ -7937,7 +7922,6 @@ sealed class ChatErrorType {
|
||||
is UnsupportedConnReq -> "unsupportedConnReq"
|
||||
is InvalidChatMessage -> "invalidChatMessage"
|
||||
is ConnReqMessageProhibited -> "connReqMessageProhibited"
|
||||
is LinkNotice -> "linkNotice"
|
||||
is ContactNotReady -> "contactNotReady"
|
||||
is ContactNotActive -> "contactNotActive"
|
||||
is ContactDisabled -> "contactDisabled"
|
||||
@@ -8023,7 +8007,6 @@ sealed class ChatErrorType {
|
||||
@Serializable @SerialName("unsupportedConnReq") object UnsupportedConnReq: ChatErrorType()
|
||||
@Serializable @SerialName("invalidChatMessage") class InvalidChatMessage(val connection: Connection, val message: String): ChatErrorType()
|
||||
@Serializable @SerialName("connReqMessageProhibited") object ConnReqMessageProhibited: ChatErrorType()
|
||||
@Serializable @SerialName("linkNotice") class LinkNotice(val expiresAt: Instant? = null, val reason: ReportReason? = null): ChatErrorType()
|
||||
@Serializable @SerialName("contactNotReady") class ContactNotReady(val contact: Contact): ChatErrorType()
|
||||
@Serializable @SerialName("contactNotActive") class ContactNotActive(val contact: Contact): ChatErrorType()
|
||||
@Serializable @SerialName("contactDisabled") class ContactDisabled(val contact: Contact): ChatErrorType()
|
||||
|
||||
+4
-11
@@ -40,8 +40,6 @@ import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.usersettings.*
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.chat.group.ChatTTLOption
|
||||
import chat.simplex.common.views.chat.group.LinkBan
|
||||
import chat.simplex.common.views.chat.group.LinkBanRows
|
||||
import chat.simplex.common.views.chat.item.MarkdownText
|
||||
import chat.simplex.common.views.chatlist.updateChatSettings
|
||||
import chat.simplex.common.views.newchat.*
|
||||
@@ -307,8 +305,6 @@ sealed class ContactDeleteMode {
|
||||
|
||||
private fun deleteActiveContactDialog(chat: Chat, contact: Contact, chatModel: ChatModel, close: (() -> Unit)? = null) {
|
||||
val contactDeleteMode = mutableStateOf<ContactDeleteMode>(ContactDeleteMode.Full())
|
||||
val ban = mutableStateOf(LinkBan.No)
|
||||
val reason = mutableStateOf<ReportReason>(ReportReason.Spam)
|
||||
|
||||
AlertManager.shared.showAlertDialogButtonsColumn(
|
||||
title = generalGetString(MR.strings.delete_contact_question),
|
||||
@@ -334,13 +330,10 @@ private fun deleteActiveContactDialog(chat: Chat, contact: Contact, chatModel: C
|
||||
)
|
||||
}
|
||||
}
|
||||
if (contact.activeConn?.viaUserContactLink != null) {
|
||||
LinkBanRows(stringResource(MR.strings.link_ban_from_connecting), ban, reason)
|
||||
}
|
||||
// Delete without notification
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
deleteContact(chat, chatModel, close, chatDeleteMode = contactDeleteMode.value.toChatDeleteMode(notify = false), notice = ban.value.notice(reason.value))
|
||||
deleteContact(chat, chatModel, close, chatDeleteMode = contactDeleteMode.value.toChatDeleteMode(notify = false))
|
||||
if (contactDeleteMode.value is ContactDeleteMode.Entity && chatModel.controller.appPrefs.showDeleteContactNotice.get()) {
|
||||
showDeleteContactNotice(contact)
|
||||
}
|
||||
@@ -350,7 +343,7 @@ private fun deleteActiveContactDialog(chat: Chat, contact: Contact, chatModel: C
|
||||
// Delete contact and notify
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
deleteContact(chat, chatModel, close, chatDeleteMode = contactDeleteMode.value.toChatDeleteMode(notify = true), notice = ban.value.notice(reason.value))
|
||||
deleteContact(chat, chatModel, close, chatDeleteMode = contactDeleteMode.value.toChatDeleteMode(notify = true))
|
||||
if (contactDeleteMode.value is ContactDeleteMode.Entity && chatModel.controller.appPrefs.showDeleteContactNotice.get()) {
|
||||
showDeleteContactNotice(contact)
|
||||
}
|
||||
@@ -475,11 +468,11 @@ private fun showDeleteContactNotice(contact: Contact) {
|
||||
)
|
||||
}
|
||||
|
||||
fun deleteContact(chat: Chat, chatModel: ChatModel, close: (() -> Unit)?, chatDeleteMode: ChatDeleteMode = ChatDeleteMode.Full(notify = true), notice: LinkNotice? = null) {
|
||||
fun deleteContact(chat: Chat, chatModel: ChatModel, close: (() -> Unit)?, chatDeleteMode: ChatDeleteMode = ChatDeleteMode.Full(notify = true)) {
|
||||
val chatInfo = chat.chatInfo
|
||||
withBGApi {
|
||||
val chatRh = chat.remoteHostId
|
||||
val ct = chatModel.controller.apiDeleteContact(chatRh, chatInfo.apiId, chatDeleteMode, notice)
|
||||
val ct = chatModel.controller.apiDeleteContact(chatRh, chatInfo.apiId, chatDeleteMode)
|
||||
if (ct != null) {
|
||||
withContext(Dispatchers.Main) {
|
||||
when (chatDeleteMode) {
|
||||
|
||||
+6
-11
@@ -13,10 +13,8 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.platform.chatModel
|
||||
import chat.simplex.common.views.chat.group.LinkBanAction
|
||||
import chat.simplex.common.views.chat.group.removeMember
|
||||
import chat.simplex.common.views.chat.group.removeMemberDialog
|
||||
import chat.simplex.common.views.chat.group.showMemberBanAlert
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.*
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
@@ -73,16 +71,13 @@ fun ComposeContextPendingMemberActionsView(
|
||||
}
|
||||
|
||||
fun rejectMemberDialog(rhId: Long?, groupInfo: GroupInfo, member: GroupMember, chatModel: ChatModel, close: (() -> Unit)? = null) {
|
||||
showMemberBanAlert(
|
||||
groupInfo,
|
||||
showBan = true,
|
||||
AlertManager.shared.showAlertDialog(
|
||||
title = generalGetString(MR.strings.reject_pending_member_alert_title),
|
||||
text = null,
|
||||
actions = listOf(
|
||||
LinkBanAction(generalGetString(MR.strings.reject_pending_member_button)) { notice ->
|
||||
removeMember(rhId, groupInfo, member, withMessages = false, chatModel, close, notice)
|
||||
}
|
||||
)
|
||||
confirmText = generalGetString(MR.strings.reject_pending_member_button),
|
||||
onConfirm = {
|
||||
removeMember(rhId, groupInfo, member, withMessages = false, chatModel, close)
|
||||
},
|
||||
destructive = true,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -698,7 +698,6 @@ fun ComposeView(
|
||||
val result = chatModel.controller.apiConnectPreparedGroup(
|
||||
rh = chat.remoteHostId,
|
||||
groupId = chat.chatInfo.apiId,
|
||||
isChannel = chat.chatInfo.isChannel,
|
||||
incognito = incognito,
|
||||
msg = mc
|
||||
)
|
||||
@@ -1240,7 +1239,7 @@ fun ComposeView(
|
||||
fun ReportReasonView(reason: ReportReason) {
|
||||
val reportText = when (reason) {
|
||||
is ReportReason.Spam -> generalGetString(MR.strings.report_compose_reason_header_spam)
|
||||
is ReportReason.Content -> generalGetString(MR.strings.report_compose_reason_header_illegal)
|
||||
is ReportReason.Illegal -> generalGetString(MR.strings.report_compose_reason_header_illegal)
|
||||
is ReportReason.Profile -> generalGetString(MR.strings.report_compose_reason_header_profile)
|
||||
is ReportReason.Community -> generalGetString(MR.strings.report_compose_reason_header_community)
|
||||
is ReportReason.Other -> generalGetString(MR.strings.report_compose_reason_header_other)
|
||||
|
||||
+88
-129
@@ -225,41 +225,29 @@ fun deleteGroupDialog(chat: Chat, groupInfo: GroupInfo, chatModel: ChatModel, cl
|
||||
if (groupInfo.membership.memberCurrent) MR.strings.delete_chat_for_all_members_cannot_undo_warning
|
||||
else MR.strings.delete_chat_for_self_cannot_undo_warning
|
||||
}
|
||||
val text = "${groupInfo.displayName}\n\n${generalGetString(messageId)}"
|
||||
val deleteGroup: (LinkNotice?) -> Unit = { notice ->
|
||||
withBGApi {
|
||||
val r = chatModel.controller.apiDeleteChat(chat.remoteHostId, chatInfo.chatType, chatInfo.apiId, notice = notice)
|
||||
if (r) {
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.removeChat(chat.remoteHostId, chatInfo.id)
|
||||
if (chatModel.chatId.value == chatInfo.id) {
|
||||
chatModel.chatId.value = null
|
||||
ModalManager.end.closeModals()
|
||||
AlertManager.shared.showAlertDialog(
|
||||
title = generalGetString(titleId),
|
||||
text = "${groupInfo.displayName}\n\n${generalGetString(messageId)}",
|
||||
parseHtml = false,
|
||||
confirmText = generalGetString(MR.strings.delete_verb),
|
||||
onConfirm = {
|
||||
withBGApi {
|
||||
val r = chatModel.controller.apiDeleteChat(chat.remoteHostId, chatInfo.chatType, chatInfo.apiId)
|
||||
if (r) {
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.removeChat(chat.remoteHostId, chatInfo.id)
|
||||
if (chatModel.chatId.value == chatInfo.id) {
|
||||
chatModel.chatId.value = null
|
||||
ModalManager.end.closeModals()
|
||||
}
|
||||
ntfManager.cancelNotificationsForChat(chatInfo.id)
|
||||
close?.invoke()
|
||||
}
|
||||
ntfManager.cancelNotificationsForChat(chatInfo.id)
|
||||
close?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (canBanOnChatDeletion(groupInfo)) {
|
||||
showLinkBanAlert(
|
||||
generalGetString(titleId),
|
||||
text,
|
||||
generalGetString(MR.strings.link_ban_from_connecting),
|
||||
LinkBan.No,
|
||||
listOf(LinkBanAction(generalGetString(MR.strings.delete_verb), deleteGroup))
|
||||
)
|
||||
} else {
|
||||
AlertManager.shared.showAlertDialog(
|
||||
title = generalGetString(titleId),
|
||||
text = text,
|
||||
parseHtml = false,
|
||||
confirmText = generalGetString(MR.strings.delete_verb),
|
||||
onConfirm = { deleteGroup(null) },
|
||||
destructive = true,
|
||||
)
|
||||
}
|
||||
},
|
||||
destructive = true,
|
||||
)
|
||||
}
|
||||
|
||||
fun leaveGroupDialog(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel, close: (() -> Unit)? = null) {
|
||||
@@ -313,25 +301,61 @@ fun removeMemberAlert(rhId: Long?, groupInfo: GroupInfo, mem: GroupMember) {
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
showMemberBanAlert(
|
||||
groupInfo,
|
||||
showBan = mem.memberStatus != GroupMemberStatus.MemInvited,
|
||||
title = generalGetString(if (groupInfo.useRelays) MR.strings.button_remove_subscriber_question else MR.strings.button_remove_member_question),
|
||||
text = generalGetString(
|
||||
if (groupInfo.useRelays) MR.strings.subscriber_will_be_removed_from_channel_cannot_be_undone
|
||||
else if (groupInfo.businessChat == null) MR.strings.member_will_be_removed_from_group_cannot_be_undone
|
||||
else MR.strings.member_will_be_removed_from_chat_cannot_be_undone
|
||||
),
|
||||
actions = listOf(
|
||||
LinkBanAction(generalGetString(MR.strings.remove_member_confirmation)) { notice ->
|
||||
removeMembers(rhId, groupInfo, listOf(mem.groupMemberId), withMessages = false, notice = notice)
|
||||
},
|
||||
LinkBanAction(generalGetString(MR.strings.remove_member_delete_messages_confirmation)) { notice ->
|
||||
removeMembers(rhId, groupInfo, listOf(mem.groupMemberId), withMessages = true, notice = notice)
|
||||
} else if (groupInfo.useRelays) {
|
||||
AlertManager.shared.showAlertDialogButtonsColumn(
|
||||
generalGetString(MR.strings.button_remove_subscriber_question),
|
||||
generalGetString(MR.strings.subscriber_will_be_removed_from_channel_cannot_be_undone),
|
||||
buttons = {
|
||||
Column {
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
removeMembers(rhId, groupInfo, listOf(mem.groupMemberId), withMessages = false)
|
||||
}) {
|
||||
Text(generalGetString(MR.strings.remove_member_confirmation), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = Color.Red)
|
||||
}
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
removeMembers(rhId, groupInfo, listOf(mem.groupMemberId), withMessages = true)
|
||||
}) {
|
||||
Text(generalGetString(MR.strings.remove_member_delete_messages_confirmation), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = Color.Red)
|
||||
}
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
}) {
|
||||
Text(generalGetString(MR.strings.cancel_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
})
|
||||
} else {
|
||||
val titleId = MR.strings.button_remove_member_question
|
||||
val messageId = if (groupInfo.businessChat == null)
|
||||
MR.strings.member_will_be_removed_from_group_cannot_be_undone
|
||||
else
|
||||
MR.strings.member_will_be_removed_from_chat_cannot_be_undone
|
||||
AlertManager.shared.showAlertDialogButtonsColumn(
|
||||
generalGetString(titleId),
|
||||
generalGetString(messageId),
|
||||
buttons = {
|
||||
Column {
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
removeMembers(rhId, groupInfo, listOf(mem.groupMemberId), withMessages = false)
|
||||
}) {
|
||||
Text(generalGetString(MR.strings.remove_member_confirmation), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = Color.Red)
|
||||
}
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
removeMembers(rhId, groupInfo, listOf(mem.groupMemberId), withMessages = true)
|
||||
}) {
|
||||
Text(generalGetString(MR.strings.remove_member_delete_messages_confirmation), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = Color.Red)
|
||||
}
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
}) {
|
||||
Text(generalGetString(MR.strings.cancel_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,75 +364,22 @@ private fun removeMembersAlert(rhId: Long?, groupInfo: GroupInfo, memberIds: Lis
|
||||
MR.strings.members_will_be_removed_from_group_cannot_be_undone
|
||||
else
|
||||
MR.strings.members_will_be_removed_from_chat_cannot_be_undone
|
||||
showMemberBanAlert(
|
||||
groupInfo,
|
||||
showBan = true,
|
||||
title = generalGetString(MR.strings.button_remove_members_question),
|
||||
text = generalGetString(messageId),
|
||||
actions = listOf(
|
||||
LinkBanAction(generalGetString(MR.strings.remove_member_confirmation)) { notice ->
|
||||
removeMembers(rhId, groupInfo, memberIds, withMessages = false, notice = notice, onSuccess = onSuccess)
|
||||
},
|
||||
LinkBanAction(generalGetString(MR.strings.remove_member_delete_messages_confirmation)) { notice ->
|
||||
removeMembers(rhId, groupInfo, memberIds, withMessages = true, notice = notice, onSuccess = onSuccess)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
enum class LinkBan {
|
||||
No, Hour, Day, Week, Month, Permanent;
|
||||
|
||||
val text: String get() = generalGetString(
|
||||
when (this) {
|
||||
No -> MR.strings.link_ban_no
|
||||
Hour -> MR.strings.link_ban_1_hour
|
||||
Day -> MR.strings.link_ban_1_day
|
||||
Week -> MR.strings.link_ban_1_week
|
||||
Month -> MR.strings.link_ban_1_month
|
||||
Permanent -> MR.strings.link_ban_permanently
|
||||
}
|
||||
)
|
||||
|
||||
fun notice(reason: ReportReason): LinkNotice? = when (this) {
|
||||
No -> null
|
||||
Hour -> LinkNotice(ttl = 3600, reason = reason)
|
||||
Day -> LinkNotice(ttl = 86400, reason = reason)
|
||||
Week -> LinkNotice(ttl = 604800, reason = reason)
|
||||
Month -> LinkNotice(ttl = 2592000, reason = reason)
|
||||
Permanent -> LinkNotice(ttl = null, reason = reason)
|
||||
}
|
||||
}
|
||||
|
||||
class LinkBanAction(val title: String, val action: (LinkNotice?) -> Unit)
|
||||
|
||||
@Composable
|
||||
fun LinkBanRows(label: String, ban: MutableState<LinkBan>, reason: MutableState<ReportReason>) {
|
||||
ExposedDropDownSettingRow(label, LinkBan.entries.map { it to it.text }, ban, onSelected = { ban.value = it })
|
||||
if (ban.value != LinkBan.No) {
|
||||
ExposedDropDownSettingRow(generalGetString(MR.strings.link_ban_reason), ReportReason.supportedReasons.map { it to it.text }, reason, onSelected = { reason.value = it })
|
||||
}
|
||||
}
|
||||
|
||||
fun showLinkBanAlert(title: String, text: String?, banLabel: String?, ban: LinkBan, actions: List<LinkBanAction>) {
|
||||
val banState = mutableStateOf(ban)
|
||||
val reason = mutableStateOf<ReportReason>(ReportReason.Spam)
|
||||
AlertManager.shared.showAlertDialogButtonsColumn(
|
||||
title,
|
||||
text,
|
||||
parseHtml = false,
|
||||
generalGetString(MR.strings.button_remove_members_question),
|
||||
generalGetString(messageId),
|
||||
buttons = {
|
||||
Column {
|
||||
if (banLabel != null) {
|
||||
LinkBanRows(banLabel, banState, reason)
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
removeMembers(rhId, groupInfo, memberIds, withMessages = false, onSuccess = onSuccess)
|
||||
}) {
|
||||
Text(generalGetString(MR.strings.remove_member_confirmation), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = Color.Red)
|
||||
}
|
||||
actions.forEach { a ->
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
a.action(banState.value.notice(reason.value))
|
||||
}) {
|
||||
Text(a.title, Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = Color.Red)
|
||||
}
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
removeMembers(rhId, groupInfo, memberIds, withMessages = true, onSuccess = onSuccess)
|
||||
}) {
|
||||
Text(generalGetString(MR.strings.remove_member_delete_messages_confirmation), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = Color.Red)
|
||||
}
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
@@ -419,18 +390,6 @@ fun showLinkBanAlert(title: String, text: String?, banLabel: String?, ban: LinkB
|
||||
})
|
||||
}
|
||||
|
||||
fun showMemberBanAlert(groupInfo: GroupInfo, showBan: Boolean, title: String, text: String?, actions: List<LinkBanAction>) {
|
||||
if (showBan) {
|
||||
val banLabel = generalGetString(if (groupInfo.businessChat == null) MR.strings.link_ban_from_joining else MR.strings.link_ban_from_connecting)
|
||||
showLinkBanAlert(title, text, banLabel, LinkBan.Day, actions)
|
||||
} else {
|
||||
showLinkBanAlert(title, text, null, LinkBan.No, actions)
|
||||
}
|
||||
}
|
||||
|
||||
fun canBanOnChatDeletion(groupInfo: GroupInfo): Boolean =
|
||||
groupInfo.businessChat?.chatType == BusinessChatType.Customer && groupInfo.membership.memberCurrent
|
||||
|
||||
@Composable
|
||||
fun SearchButton(
|
||||
modifier: Modifier,
|
||||
@@ -1442,9 +1401,9 @@ private fun setGroupAlias(chat: Chat, localAlias: String, chatModel: ChatModel)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeMembers(rhId: Long?, groupInfo: GroupInfo, memberIds: List<Long>, withMessages: Boolean, notice: LinkNotice? = null, onSuccess: () -> Unit = {}) {
|
||||
fun removeMembers(rhId: Long?, groupInfo: GroupInfo, memberIds: List<Long>, withMessages: Boolean, onSuccess: () -> Unit = {}) {
|
||||
withBGApi {
|
||||
val r = chatModel.controller.apiRemoveMembers(rhId, groupInfo.groupId, memberIds, withMessages = withMessages, notice = notice)
|
||||
val r = chatModel.controller.apiRemoveMembers(rhId, groupInfo.groupId, memberIds, withMessages = withMessages)
|
||||
if (r != null) {
|
||||
val (updatedGroupInfo, updatedMembers) = r
|
||||
withContext(Dispatchers.Main) {
|
||||
|
||||
+55
-20
@@ -276,25 +276,60 @@ fun removeMemberDialog(rhId: Long?, groupInfo: GroupInfo, member: GroupMember, c
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
showMemberBanAlert(
|
||||
groupInfo,
|
||||
showBan = member.memberStatus != GroupMemberStatus.MemInvited,
|
||||
title = generalGetString(if (groupInfo.useRelays) MR.strings.button_remove_subscriber_question else MR.strings.button_remove_member_question),
|
||||
text = generalGetString(
|
||||
if (groupInfo.useRelays) MR.strings.subscriber_will_be_removed_from_channel_cannot_be_undone
|
||||
else if (groupInfo.businessChat == null) MR.strings.member_will_be_removed_from_group_cannot_be_undone
|
||||
else MR.strings.member_will_be_removed_from_chat_cannot_be_undone
|
||||
),
|
||||
actions = listOf(
|
||||
LinkBanAction(generalGetString(MR.strings.remove_member_confirmation)) { notice ->
|
||||
removeMember(rhId, groupInfo, member, withMessages = false, chatModel, close, notice)
|
||||
},
|
||||
LinkBanAction(generalGetString(MR.strings.remove_member_delete_messages_confirmation)) { notice ->
|
||||
removeMember(rhId, groupInfo, member, withMessages = true, chatModel, close, notice)
|
||||
} else if (groupInfo.useRelays) {
|
||||
AlertManager.shared.showAlertDialogButtonsColumn(
|
||||
generalGetString(MR.strings.button_remove_subscriber_question),
|
||||
generalGetString(MR.strings.subscriber_will_be_removed_from_channel_cannot_be_undone),
|
||||
buttons = {
|
||||
Column {
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
removeMember(rhId, groupInfo, member, withMessages = false, chatModel, close)
|
||||
}) {
|
||||
Text(generalGetString(MR.strings.remove_member_confirmation), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = Color.Red)
|
||||
}
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
removeMember(rhId, groupInfo, member, withMessages = true, chatModel, close)
|
||||
}) {
|
||||
Text(generalGetString(MR.strings.remove_member_delete_messages_confirmation), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = Color.Red)
|
||||
}
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
}) {
|
||||
Text(generalGetString(MR.strings.cancel_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
})
|
||||
} else {
|
||||
val messageId = if (groupInfo.businessChat == null)
|
||||
MR.strings.member_will_be_removed_from_group_cannot_be_undone
|
||||
else
|
||||
MR.strings.member_will_be_removed_from_chat_cannot_be_undone
|
||||
AlertManager.shared.showAlertDialogButtonsColumn(
|
||||
generalGetString(MR.strings.button_remove_member_question),
|
||||
generalGetString(messageId),
|
||||
buttons = {
|
||||
Column {
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
removeMember(rhId, groupInfo, member, withMessages = false, chatModel, close)
|
||||
}) {
|
||||
Text(generalGetString(MR.strings.remove_member_confirmation), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = Color.Red)
|
||||
}
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
removeMember(rhId, groupInfo, member, withMessages = true, chatModel, close)
|
||||
}) {
|
||||
Text(generalGetString(MR.strings.remove_member_delete_messages_confirmation), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = Color.Red)
|
||||
}
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
}) {
|
||||
Text(generalGetString(MR.strings.cancel_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,9 +345,9 @@ fun deleteMemberMessagesDialog(rhId: Long?, groupInfo: GroupInfo, member: GroupM
|
||||
)
|
||||
}
|
||||
|
||||
fun removeMember(rhId: Long?, groupInfo: GroupInfo, member: GroupMember, withMessages: Boolean, chatModel: ChatModel, close: (() -> Unit)? = null, notice: LinkNotice? = null) {
|
||||
fun removeMember(rhId: Long?, groupInfo: GroupInfo, member: GroupMember, withMessages: Boolean, chatModel: ChatModel, close: (() -> Unit)? = null) {
|
||||
withBGApi {
|
||||
val r = chatModel.controller.apiRemoveMembers(rhId, member.groupId, listOf(member.groupMemberId), withMessages = withMessages, notice = notice)
|
||||
val r = chatModel.controller.apiRemoveMembers(rhId, member.groupId, listOf(member.groupMemberId), withMessages = withMessages)
|
||||
if (r != null) {
|
||||
val (updatedGroupInfo, removedMembers) = r
|
||||
withContext(Dispatchers.Main) {
|
||||
|
||||
-39
@@ -16,7 +16,6 @@ import chat.simplex.common.views.chatlist.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.datetime.Instant
|
||||
|
||||
enum class ConnectionLinkType {
|
||||
INVITATION, CONTACT, GROUP
|
||||
@@ -245,11 +244,6 @@ private suspend fun planAndConnectTask(
|
||||
cleanup()
|
||||
}
|
||||
}
|
||||
is ContactAddressPlan.LinkNotice -> {
|
||||
Log.d(TAG, "planAndConnect, .ContactAddress, .LinkNotice")
|
||||
showLinkNoticeAlert(LinkNoticeTarget.Address, connectionPlan.contactAddressPlan.expiresAt, connectionPlan.contactAddressPlan.reason)
|
||||
cleanup()
|
||||
}
|
||||
}
|
||||
is ConnectionPlan.GroupLink -> when (connectionPlan.groupLinkPlan) {
|
||||
is GroupLinkPlan.Ok ->
|
||||
@@ -394,12 +388,6 @@ private suspend fun planAndConnectTask(
|
||||
cleanup()
|
||||
}
|
||||
}
|
||||
is GroupLinkPlan.LinkNotice -> {
|
||||
Log.d(TAG, "planAndConnect, .GroupLink, .LinkNotice")
|
||||
val isChannel = (target as? ConnectTarget.Link)?.linkType == SimplexLinkType.channel || planSimplexName?.nameType == SimplexNameType.publicGroup
|
||||
showLinkNoticeAlert(if (isChannel) LinkNoticeTarget.Channel else LinkNoticeTarget.Group, connectionPlan.groupLinkPlan.expiresAt, connectionPlan.groupLinkPlan.reason)
|
||||
cleanup()
|
||||
}
|
||||
}
|
||||
is ConnectionPlan.Error -> {
|
||||
Log.d(TAG, "planAndConnect, error ${connectionPlan.chatError}")
|
||||
@@ -448,33 +436,6 @@ suspend fun connectViaUri(
|
||||
return pcc != null
|
||||
}
|
||||
|
||||
enum class LinkNoticeTarget {
|
||||
Group, Channel, Address, Link
|
||||
}
|
||||
|
||||
fun showLinkNoticeAlert(target: LinkNoticeTarget, expiresAt: Instant?, reason: ReportReason?) {
|
||||
val join = target == LinkNoticeTarget.Group || target == LinkNoticeTarget.Channel
|
||||
val banned = generalGetString(
|
||||
when (target) {
|
||||
LinkNoticeTarget.Group -> MR.strings.link_ban_alert_group
|
||||
LinkNoticeTarget.Channel -> MR.strings.link_ban_alert_channel
|
||||
LinkNoticeTarget.Address -> MR.strings.link_ban_alert_address
|
||||
LinkNoticeTarget.Link -> MR.strings.link_ban_alert_link
|
||||
}
|
||||
)
|
||||
val until =
|
||||
if (expiresAt != null) {
|
||||
String.format(generalGetString(if (join) MR.strings.link_ban_alert_join_after else MR.strings.link_ban_alert_connect_after), localTimestamp(expiresAt))
|
||||
} else {
|
||||
generalGetString(if (join) MR.strings.link_ban_alert_cant_join else MR.strings.link_ban_alert_cant_connect)
|
||||
}
|
||||
val reasonLine = if (reason == null || reason is ReportReason.Unknown) null else String.format(generalGetString(MR.strings.link_ban_alert_reason), reason.text)
|
||||
AlertManager.privacySensitive.showAlertMsg(
|
||||
generalGetString(MR.strings.link_ban_alert_title),
|
||||
listOfNotNull(banned, until, reasonLine).joinToString("\n")
|
||||
)
|
||||
}
|
||||
|
||||
fun planToConnectionLinkType(connectionPlan: ConnectionPlan): ConnectionLinkType? {
|
||||
return when(connectionPlan) {
|
||||
is ConnectionPlan.InvitationLink -> ConnectionLinkType.INVITATION
|
||||
|
||||
@@ -2039,25 +2039,6 @@
|
||||
<string name="remove_member_delete_messages_confirmation">Remove and delete messages</string>
|
||||
<string name="delete_member_messages_confirmation">Delete messages</string>
|
||||
<string name="remove_member_button">Remove member</string>
|
||||
<string name="link_ban_from_joining">Ban from joining</string>
|
||||
<string name="link_ban_from_connecting">Ban from connecting</string>
|
||||
<string name="link_ban_reason">Reason</string>
|
||||
<string name="link_ban_no">No</string>
|
||||
<string name="link_ban_1_hour">1 hour</string>
|
||||
<string name="link_ban_1_day">1 day</string>
|
||||
<string name="link_ban_1_week">1 week</string>
|
||||
<string name="link_ban_1_month">1 month</string>
|
||||
<string name="link_ban_permanently">Permanently</string>
|
||||
<string name="link_ban_alert_title">Banned</string>
|
||||
<string name="link_ban_alert_group">You were banned from this group.</string>
|
||||
<string name="link_ban_alert_channel">You were banned from this channel.</string>
|
||||
<string name="link_ban_alert_address">You were banned from connecting via this address.</string>
|
||||
<string name="link_ban_alert_link">You were banned from connecting via this link.</string>
|
||||
<string name="link_ban_alert_join_after">You can join again after %s.</string>
|
||||
<string name="link_ban_alert_connect_after">You can connect again after %s.</string>
|
||||
<string name="link_ban_alert_cant_join">You can\'t join it again.</string>
|
||||
<string name="link_ban_alert_cant_connect">You can\'t connect again.</string>
|
||||
<string name="link_ban_alert_reason">Reason: %s.</string>
|
||||
<string name="block_member_question">Block member?</string>
|
||||
<string name="block_member_button">Block member</string>
|
||||
<string name="block_member_confirmation">Block</string>
|
||||
|
||||
@@ -176,8 +176,8 @@ export class ChatClient {
|
||||
}
|
||||
}
|
||||
|
||||
async apiDeleteChat(chatType: T.ChatType, chatId: number, deleteMode: T.ChatDeleteMode = {type: "full", notify: true}, notice?: T.LinkNotice): Promise<void> {
|
||||
const r = await this.sendChatCmd(CC.APIDeleteChat.cmdString({chatRef: {chatType, chatId}, chatDeleteMode: deleteMode, notice}))
|
||||
async apiDeleteChat(chatType: T.ChatType, chatId: number, deleteMode: T.ChatDeleteMode = {type: "full", notify: true}): Promise<void> {
|
||||
const r = await this.sendChatCmd(CC.APIDeleteChat.cmdString({chatRef: {chatType, chatId}, chatDeleteMode: deleteMode}))
|
||||
switch (chatType) {
|
||||
case T.ChatType.Direct:
|
||||
if (r.type === "contactDeleted") return
|
||||
@@ -273,8 +273,8 @@ export class ChatClient {
|
||||
throw new ChatCommandError("error joining group", r)
|
||||
}
|
||||
|
||||
async apiRemoveMembers(groupId: number, memberIds: number[], withMessages = false, notice?: T.LinkNotice): Promise<T.GroupMember[]> {
|
||||
const r = await this.sendChatCmd(CC.APIRemoveMembers.cmdString({groupId, groupMemberIds: memberIds, withMessages, notice}))
|
||||
async apiRemoveMembers(groupId: number, memberIds: number[], withMessages = false): Promise<T.GroupMember[]> {
|
||||
const r = await this.sendChatCmd(CC.APIRemoveMembers.cmdString({groupId, groupMemberIds: memberIds, withMessages}))
|
||||
if (r.type === "userDeletedMembers") return r.members
|
||||
throw new ChatCommandError("error removing member", r)
|
||||
}
|
||||
|
||||
@@ -620,8 +620,8 @@ export class ChatApi {
|
||||
* Remove members. Requires Admin role.
|
||||
* Network usage: background.
|
||||
*/
|
||||
async apiRemoveMembers(groupId: number, memberIds: number[], withMessages = false, notice?: T.LinkNotice): Promise<T.GroupMember[]> {
|
||||
const r = await this.sendChatCmd(CC.APIRemoveMembers.cmdString({groupId, groupMemberIds: memberIds, withMessages, notice}))
|
||||
async apiRemoveMembers(groupId: number, memberIds: number[], withMessages = false): Promise<T.GroupMember[]> {
|
||||
const r = await this.sendChatCmd(CC.APIRemoveMembers.cmdString({groupId, groupMemberIds: memberIds, withMessages}))
|
||||
if (r.type === "userDeletedMembers") return r.members
|
||||
throw new ChatCommandError("error removing member", r)
|
||||
}
|
||||
@@ -829,8 +829,8 @@ export class ChatApi {
|
||||
* Delete chat.
|
||||
* Network usage: background.
|
||||
*/
|
||||
async apiDeleteChat(chatType: T.ChatType, chatId: number, deleteMode: T.ChatDeleteMode = {type: "full", notify: true}, notice?: T.LinkNotice): Promise<void> {
|
||||
const r = await this.sendChatCmd(CC.APIDeleteChat.cmdString({chatRef: {chatType, chatId}, chatDeleteMode: deleteMode, notice}))
|
||||
async apiDeleteChat(chatType: T.ChatType, chatId: number, deleteMode: T.ChatDeleteMode = {type: "full", notify: true}): Promise<void> {
|
||||
const r = await this.sendChatCmd(CC.APIDeleteChat.cmdString({chatRef: {chatType, chatId}, chatDeleteMode: deleteMode}))
|
||||
switch (chatType) {
|
||||
case T.ChatType.Direct:
|
||||
if (r.type === "contactDeleted") return
|
||||
|
||||
Reference in new issue
Block a user