Skip to content
Snippets Groups Projects
Commit 88f47a45 authored by Bruno Muniz's avatar Bruno Muniz :apple:
Browse files

Finished adding report to group

parent a9fc1b33
No related branches found
No related tags found
3 merge requests!71Releasing v1.1.5 (214),!69Implemented filtering for banned/blocked users and reporting,!67v1.1.5 b(203)
import HUD
import UIKit import UIKit
import Theme import Theme
import Models import Models
...@@ -6,8 +7,10 @@ import Combine ...@@ -6,8 +7,10 @@ import Combine
import XXModels import XXModels
import Voxophone import Voxophone
import ChatLayout import ChatLayout
import Integration
import DrawerFeature import DrawerFeature
import DifferenceKit import DifferenceKit
import ReportingFeature
import ChatInputFeature import ChatInputFeature
import DependencyInjection import DependencyInjection
...@@ -19,7 +22,11 @@ typealias OutgoingFailedGroupTextCell = CollectionCell<FlexibleSpace, StackMessa ...@@ -19,7 +22,11 @@ typealias OutgoingFailedGroupTextCell = CollectionCell<FlexibleSpace, StackMessa
typealias OutgoingFailedGroupReplyCell = CollectionCell<FlexibleSpace, ReplyStackMessageView> typealias OutgoingFailedGroupReplyCell = CollectionCell<FlexibleSpace, ReplyStackMessageView>
public final class GroupChatController: UIViewController { public final class GroupChatController: UIViewController {
@Dependency private var hud: HUD
@Dependency private var session: SessionType
@Dependency private var coordinator: ChatCoordinating @Dependency private var coordinator: ChatCoordinating
@Dependency private var makeReportDrawer: MakeReportDrawer
@Dependency private var makeAppScreenshot: MakeAppScreenshot
@Dependency private var statusBarController: StatusBarStyleControlling @Dependency private var statusBarController: StatusBarStyleControlling
private let members: MembersController private let members: MembersController
...@@ -176,6 +183,17 @@ public final class GroupChatController: UIViewController { ...@@ -176,6 +183,17 @@ public final class GroupChatController: UIViewController {
} }
}.store(in: &cancellables) }.store(in: &cancellables)
viewModel.hudPublisher
.receive(on: DispatchQueue.main)
.sink { [hud] in hud.update(with: $0) }
.store(in: &cancellables)
viewModel.reportPopupPublisher
.receive(on: DispatchQueue.main)
.sink { [unowned self] contact in
presentReportDrawer(contact)
}.store(in: &cancellables)
viewModel.messages viewModel.messages
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [unowned self] sections in .sink { [unowned self] sections in
...@@ -229,6 +247,19 @@ public final class GroupChatController: UIViewController { ...@@ -229,6 +247,19 @@ public final class GroupChatController: UIViewController {
coordinator.toMembersList(members, from: self) coordinator.toMembersList(members, from: self)
} }
private func presentReportDrawer(_ contact: Contact) {
var config = MakeReportDrawer.Config()
config.onReport = { [weak self] in
guard let self = self else { return }
let screenshot = try! self.makeAppScreenshot()
self.viewModel.report(contact: contact, screenshot: screenshot) {
self.collectionView.reloadData()
}
}
let drawer = makeReportDrawer(config)
coordinator.toDrawer(drawer, from: self)
}
private func makeWaitingRoundDrawer() -> UIViewController { private func makeWaitingRoundDrawer() -> UIViewController {
let text = DrawerText( let text = DrawerText(
font: Fonts.Mulish.semiBold.font(size: 14.0), font: Fonts.Mulish.semiBold.font(size: 14.0),
...@@ -332,8 +363,7 @@ extension GroupChatController: UICollectionViewDataSource { ...@@ -332,8 +363,7 @@ extension GroupChatController: UICollectionViewDataSource {
var isSenderBanned = false var isSenderBanned = false
if let database = try? DependencyInjection.Container.shared.resolve() as Database, if let sender = try? session.dbManager.fetchContacts(.init(id: [item.senderId])).first {
let sender = try? database.fetchContacts(.init(id: [item.senderId])).first {
isSenderBanned = sender.isBanned isSenderBanned = sender.isBanned
} }
...@@ -568,6 +598,10 @@ extension GroupChatController: UICollectionViewDelegate { ...@@ -568,6 +598,10 @@ extension GroupChatController: UICollectionViewDelegate {
self?.viewModel.didRequestDelete([item]) self?.viewModel.didRequestDelete([item])
} }
let report = UIAction(title: Localized.Chat.BubbleMenu.report, state: .off) { [weak self] _ in
self?.viewModel.didRequestReport(item)
}
let retry = UIAction(title: Localized.Chat.BubbleMenu.retry, state: .off) { [weak self] _ in let retry = UIAction(title: Localized.Chat.BubbleMenu.retry, state: .off) { [weak self] _ in
self?.viewModel.retry(item) self?.viewModel.retry(item)
} }
...@@ -579,7 +613,7 @@ extension GroupChatController: UICollectionViewDelegate { ...@@ -579,7 +613,7 @@ extension GroupChatController: UICollectionViewDelegate {
} else if item.status == .sending { } else if item.status == .sending {
menu = UIMenu(title: "", children: [copy]) menu = UIMenu(title: "", children: [copy])
} else { } else {
menu = UIMenu(title: "", children: [copy, reply, delete]) menu = UIMenu(title: "", children: [copy, reply, delete, report])
} }
return menu return menu
......
import HUD
import UIKit import UIKit
import Models import Models
import Shared
import Combine import Combine
import XXModels import XXModels
import Defaults
import Foundation import Foundation
import Integration import Integration
import ToastFeature
import DifferenceKit import DifferenceKit
import ReportingFeature
import DependencyInjection import DependencyInjection
enum GroupChatNavigationRoutes: Equatable { enum GroupChatNavigationRoutes: Equatable {
...@@ -14,6 +19,18 @@ enum GroupChatNavigationRoutes: Equatable { ...@@ -14,6 +19,18 @@ enum GroupChatNavigationRoutes: Equatable {
final class GroupChatViewModel { final class GroupChatViewModel {
@Dependency private var session: SessionType @Dependency private var session: SessionType
@Dependency private var sendReport: SendReport
@Dependency private var toastController: ToastController
@KeyObject(.username, defaultValue: nil) var username: String?
var hudPublisher: AnyPublisher<HUDStatus, Never> {
hudSubject.eraseToAnyPublisher()
}
var reportPopupPublisher: AnyPublisher<Contact, Never> {
reportPopupSubject.eraseToAnyPublisher()
}
var replyPublisher: AnyPublisher<(String, String), Never> { var replyPublisher: AnyPublisher<(String, String), Never> {
replySubject.eraseToAnyPublisher() replySubject.eraseToAnyPublisher()
...@@ -26,11 +43,13 @@ final class GroupChatViewModel { ...@@ -26,11 +43,13 @@ final class GroupChatViewModel {
let info: GroupInfo let info: GroupInfo
private var stagedReply: Reply? private var stagedReply: Reply?
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
private let hudSubject = CurrentValueSubject<HUDStatus, Never>(.none)
private let reportPopupSubject = PassthroughSubject<Contact, Never>()
private let replySubject = PassthroughSubject<(String, String), Never>() private let replySubject = PassthroughSubject<(String, String), Never>()
private let routesSubject = PassthroughSubject<GroupChatNavigationRoutes, Never>() private let routesSubject = PassthroughSubject<GroupChatNavigationRoutes, Never>()
var messages: AnyPublisher<[ArraySection<ChatSection, Message>], Never> { var messages: AnyPublisher<[ArraySection<ChatSection, Message>], Never> {
session.dbManager.fetchMessagesPublisher(.init(chat: .group(info.group.id), isSenderBanned: false)) session.dbManager.fetchMessagesPublisher(.init(chat: .group(info.group.id)))
.assertNoFailure() .assertNoFailure()
.map { messages -> [ArraySection<ChatSection, Message>] in .map { messages -> [ArraySection<ChatSection, Message>] in
let groupedByDate = Dictionary(grouping: messages) { domainModel -> Date in let groupedByDate = Dictionary(grouping: messages) { domainModel -> Date in
...@@ -63,6 +82,12 @@ final class GroupChatViewModel { ...@@ -63,6 +82,12 @@ final class GroupChatViewModel {
_ = try? session.dbManager.deleteMessages(.init(id: Set(messages.map(\.id)))) _ = try? session.dbManager.deleteMessages(.init(id: Set(messages.map(\.id))))
} }
func didRequestReport(_ message: Message) {
if let contact = try? session.dbManager.fetchContacts(.init(id: [message.senderId])).first {
reportPopupSubject.send(contact)
}
}
func send(_ text: String) { func send(_ text: String) {
session.send(.init( session.send(.init(
text: text.trimmingCharacters(in: .whitespacesAndNewlines), text: text.trimmingCharacters(in: .whitespacesAndNewlines),
...@@ -117,4 +142,57 @@ final class GroupChatViewModel { ...@@ -117,4 +142,57 @@ final class GroupChatViewModel {
stagedReply = Reply(messageId: networkId, senderId: message.senderId) stagedReply = Reply(messageId: networkId, senderId: message.senderId)
replySubject.send(getReplyContent(for: networkId)) replySubject.send(getReplyContent(for: networkId))
} }
func report(contact: Contact, screenshot: UIImage, completion: @escaping () -> Void) {
let report = Report(
sender: .init(
userId: contact.id.base64EncodedString(),
username: contact.username!
),
recipient: .init(
userId: session.myId.base64EncodedString(),
username: username!
),
type: .group,
screenshot: screenshot.pngData()!,
partyName: info.group.name,
partyBlob: info.group.id.base64EncodedString(),
partyMembers: info.members.map { Report.ReportUser(
userId: $0.id.base64EncodedString(),
username: $0.username ?? "")
}
)
hudSubject.send(.on)
sendReport(report) { result in
switch result {
case .failure(let error):
DispatchQueue.main.async {
self.hudSubject.send(.error(.init(with: error)))
}
case .success(_):
self.blockContact(contact)
DispatchQueue.main.async {
self.hudSubject.send(.none)
self.presentReportConfirmation(contact: contact)
completion()
}
}
}
}
private func blockContact(_ contact: Contact) {
var contact = contact
contact.isBlocked = true
_ = try? session.dbManager.saveContact(contact)
}
private func presentReportConfirmation(contact: Contact) {
let name = (contact.nickname ?? contact.username) ?? "the contact"
toastController.enqueueToast(model: .init(
title: "Your report has been sent and \(name) is now blocked.",
leftImage: Asset.requestSentToaster.image
))
}
} }
...@@ -139,15 +139,11 @@ final class ChatListViewModel { ...@@ -139,15 +139,11 @@ final class ChatListViewModel {
), ),
groupChatInfoQuery: GroupChatInfo.Query( groupChatInfoQuery: GroupChatInfo.Query(
authStatus: [.participating], authStatus: [.participating],
isLeaderBlocked: false,
isLeaderBanned: false,
excludeBannedContactsMessages: true excludeBannedContactsMessages: true
), ),
groupQuery: Group.Query( groupQuery: Group.Query(
withMessages: false, withMessages: false,
authStatus: [.participating], authStatus: [.participating]
isLeaderBlocked: false,
isLeaderBanned: false
) )
)) ))
.assertNoFailure() .assertNoFailure()
......
...@@ -53,7 +53,7 @@ extension Session { ...@@ -53,7 +53,7 @@ extension Session {
recipientId: nil, recipientId: nil,
groupId: group.id, groupId: group.id,
date: group.createdAt, date: group.createdAt,
status: .received, status: .sent,
isUnread: false, isUnread: false,
text: welcome, text: welcome,
replyMessageId: nil, replyMessageId: nil,
......
...@@ -5,18 +5,27 @@ public struct Report: Encodable { ...@@ -5,18 +5,27 @@ public struct Report: Encodable {
sender: ReportUser, sender: ReportUser,
recipient: ReportUser, recipient: ReportUser,
type: ReportType, type: ReportType,
screenshot: Data screenshot: Data,
partyName: String? = nil,
partyBlob: String? = nil,
partyMembers: [ReportUser]? = nil
) { ) {
self.sender = sender self.sender = sender
self.recipient = recipient self.recipient = recipient
self.type = type self.type = type
self.screenshot = screenshot self.screenshot = screenshot
self.partyName = partyName
self.partyBlob = partyBlob
self.partyMembers = partyMembers
} }
public var sender: ReportUser public var sender: ReportUser
public var recipient: ReportUser public var recipient: ReportUser
public var type: ReportType public var type: ReportType
public var screenshot: Data public var screenshot: Data
public var partyName: String?
public var partyBlob: String?
public var partyMembers: [ReportUser]?
} }
extension Report { extension Report {
......
No preview for this file type
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment