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

Merge branch 'feature/reporting-switcher' into 'development'

Add user defaults flag for reporting enabled and reporting optional

See merge request elixxir/client-ios!70
parents ecd3bac3 d407039f
No related branches found
No related tags found
2 merge requests!71Releasing v1.1.5 (214),!70Add user defaults flag for reporting enabled and reporting optional
Showing
with 294 additions and 55 deletions
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
......@@ -52,6 +52,24 @@
migratedStopOnEveryIssue = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<PreActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Script"
scriptText = "#!/bin/sh&#10;/usr/libexec/PlistBuddy -c &quot;Set :isReportingOptional YES&quot; &quot;${SRCROOT}/client-ios/Resources/Info.plist&quot;&#10;">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "02FDD06121EDA39A000F1286"
BuildableName = "client-ios.app"
BlueprintName = "client-ios"
ReferencedContainer = "container:client-ios.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PreActions>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
......@@ -91,5 +109,23 @@
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
<PreActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Script"
scriptText = "#!/bin/sh&#10;/usr/libexec/PlistBuddy -c &quot;Set :isReportingOptional NO&quot; &quot;${SRCROOT}/client-ios/Resources/Info.plist&quot;&#10;">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "02FDD06121EDA39A000F1286"
BuildableName = "client-ios.app"
BlueprintName = "client-ios"
ReferencedContainer = "container:client-ios.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PreActions>
</ArchiveAction>
</Scheme>
......@@ -104,5 +104,7 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
<key>isReportingOptional</key>
<false/>
</dict>
</plist>
......@@ -81,6 +81,7 @@ struct DependencyRegistrator {
container.register(XXLogger.live())
container.register(CrashReporter.live)
container.register(VersionChecker.live())
container.register(ReportingStatus.live())
container.register(XXNetwork<BindingsClient>() as XXNetworking)
container.register(NetworkMonitor() as NetworkMonitoring)
......
......@@ -25,6 +25,7 @@ public final class GroupChatController: UIViewController {
@Dependency private var hud: HUD
@Dependency private var session: SessionType
@Dependency private var coordinator: ChatCoordinating
@Dependency private var reportingStatus: ReportingStatus
@Dependency private var makeReportDrawer: MakeReportDrawer
@Dependency private var makeAppScreenshot: MakeAppScreenshot
@Dependency private var statusBarController: StatusBarStyleControlling
......@@ -602,17 +603,21 @@ extension GroupChatController: UICollectionViewDelegate {
self?.viewModel.retry(item)
}
let menu: UIMenu
var children = [UIAction]()
if item.status == .sendingFailed {
menu = UIMenu(title: "", children: [copy, retry, delete])
children = [copy, retry, delete]
} else if item.status == .sending {
menu = UIMenu(title: "", children: [copy])
children = [copy]
} else {
menu = UIMenu(title: "", children: [copy, reply, delete, report])
children = [copy, reply, delete]
if self.reportingStatus.isEnabled() {
children.append(report)
}
}
return menu
return UIMenu(title: "", children: children)
}
}
}
......@@ -29,6 +29,7 @@ public final class SingleChatController: UIViewController {
@Dependency private var logger: XXLogger
@Dependency private var voxophone: Voxophone
@Dependency private var coordinator: ChatCoordinating
@Dependency private var reportingStatus: ReportingStatus
@Dependency private var makeReportDrawer: MakeReportDrawer
@Dependency private var makeAppScreenshot: MakeAppScreenshot
@Dependency private var statusBarController: StatusBarStyleControlling
......@@ -655,13 +656,20 @@ extension SingleChatController: UICollectionViewDelegate {
guard let self = self else { return nil }
let item = self.sections[indexPath.section].elements[indexPath.item]
return UIMenu(title: "", children: [
var children = [
ActionFactory.build(from: item, action: .copy, closure: self.viewModel.didRequestCopy(_:)),
ActionFactory.build(from: item, action: .retry, closure: self.viewModel.didRequestRetry(_:)),
ActionFactory.build(from: item, action: .reply, closure: self.viewModel.didRequestReply(_:)),
ActionFactory.build(from: item, action: .delete, closure: self.viewModel.didRequestDeleteSingle(_:)),
ActionFactory.build(from: item, action: .delete, closure: self.viewModel.didRequestDeleteSingle(_:))
]
if self.reportingStatus.isEnabled() {
children.append(
ActionFactory.build(from: item, action: .report, closure: self.viewModel.didRequestReport(_:))
].compactMap { $0 })
)
}
return UIMenu(title: "", children: children.compactMap { $0 })
}
}
......
......@@ -20,6 +20,7 @@ enum GroupChatNavigationRoutes: Equatable {
final class GroupChatViewModel {
@Dependency private var session: SessionType
@Dependency private var sendReport: SendReport
@Dependency private var reportingStatus: ReportingStatus
@Dependency private var toastController: ToastController
@KeyObject(.username, defaultValue: nil) var username: String?
......@@ -130,7 +131,7 @@ final class GroupChatViewModel {
var name = (contact.nickname ?? contact.username) ?? "Fetching username..."
if contact.isBlocked {
if contact.isBlocked, reportingStatus.isEnabled() {
name = "\(name) (Blocked)"
}
......
......@@ -6,6 +6,7 @@ import Combine
import XXModels
import Defaults
import Integration
import ReportingFeature
import DependencyInjection
enum SearchSection {
......@@ -23,6 +24,7 @@ typealias SearchSnapshot = NSDiffableDataSourceSnapshot<SearchSection, SearchIte
final class ChatListViewModel {
@Dependency private var session: SessionType
@Dependency private var reportingStatus: ReportingStatus
var isOnline: AnyPublisher<Bool, Never> {
session.isOnline
......@@ -37,7 +39,12 @@ final class ChatListViewModel {
}
var recentsPublisher: AnyPublisher<RecentsSnapshot, Never> {
let query = Contact.Query(isRecent: true, isBlocked: false, isBanned: false)
let query = Contact.Query(
isRecent: true,
isBlocked: reportingStatus.isEnabled() ? false : nil,
isBanned: reportingStatus.isEnabled() ? false : nil
)
return session.dbManager.fetchContactsPublisher(query)
.assertNoFailure()
.map {
......@@ -50,8 +57,13 @@ final class ChatListViewModel {
}
var searchPublisher: AnyPublisher<SearchSnapshot, Never> {
let contactsQuery = Contact.Query(
isBlocked: reportingStatus.isEnabled() ? false : nil,
isBanned: reportingStatus.isEnabled() ? false : nil
)
let contactsStream = session.dbManager
.fetchContactsPublisher(.init(isBlocked: false, isBanned: false))
.fetchContactsPublisher(contactsQuery)
.assertNoFailure()
.map { $0.filter { $0.id != self.session.myId }}
......@@ -107,13 +119,17 @@ final class ChatListViewModel {
var badgeCountPublisher: AnyPublisher<Int, Never> {
let groupQuery = Group.Query(authStatus: [.pending])
let contactsQuery = Contact.Query(authStatus: [
let contactsQuery = Contact.Query(
authStatus: [
.verified,
.confirming,
.confirmationFailed,
.verificationFailed,
.verificationInProgress
], isBlocked: false, isBanned: false)
],
isBlocked: reportingStatus.isEnabled() ? false : nil,
isBanned: reportingStatus.isEnabled() ? false : nil
)
return Publishers.CombineLatest(
session.dbManager.fetchContactsPublisher(contactsQuery).assertNoFailure(),
......@@ -134,12 +150,12 @@ final class ChatListViewModel {
contactChatInfoQuery: .init(
userId: session.myId,
authStatus: [.friend],
isBlocked: false,
isBanned: false
isBlocked: reportingStatus.isEnabled() ? false : nil,
isBanned: reportingStatus.isEnabled() ? false : nil
),
groupChatInfoQuery: GroupChatInfo.Query(
authStatus: [.participating],
excludeBannedContactsMessages: true
excludeBannedContactsMessages: reportingStatus.isEnabled()
),
groupQuery: Group.Query(
withMessages: false,
......
import Models
import Combine
import XXModels
import Defaults
import Integration
import ReportingFeature
import DependencyInjection
final class ContactListViewModel {
@Dependency private var session: SessionType
@Dependency private var reportingStatus: ReportingStatus
var contacts: AnyPublisher<[Contact], Never> {
let query = Contact.Query(authStatus: [.friend], isBlocked: false, isBanned: false)
let query = Contact.Query(
authStatus: [.friend],
isBlocked: reportingStatus.isEnabled() ? false : nil,
isBanned: reportingStatus.isEnabled() ? false: nil
)
return session.dbManager.fetchContactsPublisher(query)
.assertNoFailure()
......@@ -17,14 +24,23 @@ final class ContactListViewModel {
}
var requestCount: AnyPublisher<Int, Never> {
let groupQuery = Group.Query(authStatus: [.pending], isLeaderBlocked: false, isLeaderBanned: false)
let contactsQuery = Contact.Query(authStatus: [
let groupQuery = Group.Query(
authStatus: [.pending],
isLeaderBlocked: reportingStatus.isEnabled() ? false : nil,
isLeaderBanned: reportingStatus.isEnabled() ? false : nil
)
let contactsQuery = Contact.Query(
authStatus: [
.verified,
.confirming,
.confirmationFailed,
.verificationFailed,
.verificationInProgress
], isBlocked: false, isBanned: false)
],
isBlocked: reportingStatus.isEnabled() ? false : nil,
isBanned: reportingStatus.isEnabled() ? false : nil
)
return Publishers.CombineLatest(
session.dbManager.fetchContactsPublisher(contactsQuery).assertNoFailure(),
......
......@@ -5,6 +5,7 @@ import Combine
import XXModels
import Defaults
import Integration
import ReportingFeature
import DependencyInjection
final class CreateGroupViewModel {
......@@ -13,6 +14,7 @@ final class CreateGroupViewModel {
// MARK: Injected
@Dependency private var session: SessionType
@Dependency private var reportingStatus: ReportingStatus
// MARK: Properties
......@@ -42,7 +44,13 @@ final class CreateGroupViewModel {
// MARK: Lifecycle
init() {
session.dbManager.fetchContactsPublisher(.init(authStatus: [.friend], isBlocked: false, isBanned: false))
let query = Contact.Query(
authStatus: [.friend],
isBlocked: reportingStatus.isEnabled() ? false : nil,
isBanned: reportingStatus.isEnabled() ? false : nil
)
session.dbManager.fetchContactsPublisher(query)
.assertNoFailure()
.map { $0.filter { $0.id != self.session.myId }}
.map { $0.sorted(by: { $0.username! < $1.username! })}
......
......@@ -10,6 +10,7 @@ import Foundation
import ToastFeature
import BackupFeature
import NetworkMonitor
import ReportingFeature
import DependencyInjection
import XXLegacyDatabaseMigrator
......@@ -50,6 +51,7 @@ public final class Session: SessionType {
@Dependency var backupService: BackupService
@Dependency var toastController: ToastController
@Dependency var reportingStatus: ReportingStatus
@Dependency var networkMonitor: NetworkMonitoring
public let client: Client
......@@ -461,7 +463,7 @@ public final class Session: SessionType {
}
if let contact = try! dbManager.fetchContacts(.init(id: [request.0.leaderId])).first {
if contact.isBanned || contact.isBlocked {
if reportingStatus.isEnabled(), (contact.isBlocked || contact.isBanned) {
return
}
}
......
......@@ -36,6 +36,7 @@ final class LaunchViewModel {
@Dependency private var keychainHandler: KeychainHandling
@Dependency private var permissionHandler: PermissionHandling
@Dependency private var fetchBannedList: FetchBannedList
@Dependency private var reportingStatus: ReportingStatus
@Dependency private var processBannedList: ProcessBannedList
@Dependency private var toastController: ToastController
@Dependency private var session: SessionType
......@@ -138,7 +139,12 @@ final class LaunchViewModel {
}
func getContactWith(userId: Data) -> Contact? {
let query = Contact.Query(id: [userId], isBlocked: false, isBanned: false)
let query = Contact.Query(
id: [userId],
isBlocked: reportingStatus.isEnabled() ? false : nil,
isBanned: reportingStatus.isEnabled() ? false : nil
)
return try! session.dbManager.fetchContacts(query).first
}
......
......@@ -3,23 +3,34 @@ import XXModels
import Defaults
import Foundation
import Integration
import ReportingFeature
import DependencyInjection
final class MenuViewModel {
@Dependency private var session: SessionType
@Dependency private var reportingStatus: ReportingStatus
@KeyObject(.avatar, defaultValue: nil) var avatar: Data?
@KeyObject(.username, defaultValue: "") var username: String
var requestCount: AnyPublisher<Int, Never> {
let groupQuery = Group.Query(authStatus: [.pending], isLeaderBlocked: false, isLeaderBanned: false)
let contactsQuery = Contact.Query(authStatus: [
let groupQuery = Group.Query(
authStatus: [.pending],
isLeaderBlocked: reportingStatus.isEnabled() ? false : nil,
isLeaderBanned: reportingStatus.isEnabled() ? false : nil
)
let contactsQuery = Contact.Query(
authStatus: [
.verified,
.confirming,
.confirmationFailed,
.verificationFailed,
.verificationInProgress
], isBlocked: false, isBanned: false)
],
isBlocked: reportingStatus.isEnabled() ? false : nil,
isBanned: reportingStatus.isEnabled() ? false : nil
)
return Publishers.CombineLatest(
session.dbManager.fetchContactsPublisher(contactsQuery).assertNoFailure(),
......
......@@ -3,6 +3,7 @@ import Models
import Defaults
import XXModels
import Integration
import ReportingFeature
import DependencyInjection
public final class PushHandler: PushHandling {
......@@ -11,6 +12,8 @@ public final class PushHandler: PushHandling {
static let usernamesSetting = "isShowingUsernames"
}
@Dependency var reportingStatus: ReportingStatus
@KeyObject(.pushNotifications, defaultValue: false) var isPushEnabled: Bool
let requestAuth: RequestAuth
......@@ -108,7 +111,7 @@ public final class PushHandler: PushHandling {
return ($0.type.unknownSenderContent!, $0)
}
if contact.isBlocked || contact.isBanned {
if reportingStatus.isEnabled(), (contact.isBlocked || contact.isBanned) {
return nil
}
......
import Combine
public struct ReportingStatus {
public var isOptional: () -> Bool
public var isEnabled: () -> Bool
public var isEnabledPublisher: () -> AnyPublisher<Bool, Never>
public var enable: (Bool) -> Void
}
extension ReportingStatus {
public static func live(
isOptional: ReportingStatusIsOptional = .live(),
isEnabled: ReportingStatusIsEnabled = .live()
) -> ReportingStatus {
ReportingStatus(
isOptional: {
isOptional.get()
},
isEnabled: {
if isOptional.get() == false {
return true
}
return isEnabled.get()
},
isEnabledPublisher: {
if isOptional.get() == false {
return Just(true).eraseToAnyPublisher()
}
return isEnabled.publisher()
},
enable: { enabled in
isEnabled.set(enabled)
}
)
}
}
import Combine
import Foundation
public struct ReportingStatusIsEnabled {
public var get: () -> Bool
public var set: (Bool) -> Void
public var publisher: () -> AnyPublisher<Bool, Never>
}
extension ReportingStatusIsEnabled {
public static func live(
userDefaults: UserDefaults = .standard
) -> ReportingStatusIsEnabled {
ReportingStatusIsEnabled(
get: {
userDefaults.isReportingEnabled
},
set: { enabled in
userDefaults.isReportingEnabled = enabled
},
publisher: {
userDefaults.publisher(for: \.isReportingEnabled).eraseToAnyPublisher()
}
)
}
}
private extension UserDefaults {
static let isReportingEnabledKey = "isReportingEnabled"
@objc var isReportingEnabled: Bool {
get {
bool(forKey: Self.isReportingEnabledKey)
} set {
set(newValue, forKey: Self.isReportingEnabledKey)
}
}
}
import Foundation
public struct ReportingStatusIsOptional {
public var get: () -> Bool
}
extension ReportingStatusIsOptional {
public static func live(
plist url: URL = Bundle.main.url(forResource: "Info", withExtension: "plist")!
) -> ReportingStatusIsOptional {
ReportingStatusIsOptional {
struct Plist: Decodable {
let isReportingOptional: Bool
}
guard let data = try? Data(contentsOf: url),
let infoPlist = try? PropertyListDecoder().decode(Plist.self, from: data) else {
return true
}
return infoPlist.isReportingOptional
}
}
}
......@@ -7,6 +7,7 @@ import Defaults
import XXModels
import Integration
import DrawerFeature
import ReportingFeature
import CombineSchedulers
import DependencyInjection
......@@ -18,6 +19,7 @@ struct RequestReceived: Hashable, Equatable {
final class RequestsReceivedViewModel {
@Dependency private var session: SessionType
@Dependency private var reportingStatus: ReportingStatus
@KeyObject(.isShowingHiddenRequests, defaultValue: false) var isShowingHiddenRequests: Bool
......@@ -56,7 +58,10 @@ final class RequestsReceivedViewModel {
authStatus: [
.hidden,
.pending
], isLeaderBlocked: false, isLeaderBanned: false)
],
isLeaderBlocked: reportingStatus.isEnabled() ? false : nil,
isLeaderBanned: reportingStatus.isEnabled() ? false : nil
)
let contactsQuery = Contact.Query(
authStatus: [
......@@ -65,7 +70,10 @@ final class RequestsReceivedViewModel {
.verified,
.verificationFailed,
.verificationInProgress
], isBlocked: false, isBanned: false)
],
isBlocked: reportingStatus.isEnabled() ? false : nil,
isBanned: reportingStatus.isEnabled() ? false : nil
)
let groupStream = session.dbManager.fetchGroupsPublisher(groupsQuery).assertNoFailure()
let contactsStream = session.dbManager.fetchContactsPublisher(contactsQuery).assertNoFailure()
......
......@@ -3,9 +3,11 @@ import UIKit
import Models
import Shared
import Combine
import Defaults
import XXModels
import Integration
import ToastFeature
import ReportingFeature
import CombineSchedulers
import DependencyInjection
......@@ -16,6 +18,7 @@ struct RequestSent: Hashable, Equatable {
final class RequestsSentViewModel {
@Dependency private var session: SessionType
@Dependency private var reportingStatus: ReportingStatus
@Dependency private var toastController: ToastController
var hudPublisher: AnyPublisher<HUDStatus, Never> {
......@@ -33,10 +36,14 @@ final class RequestsSentViewModel {
var backgroundScheduler: AnySchedulerOf<DispatchQueue> = DispatchQueue.global().eraseToAnyScheduler()
init() {
let query = Contact.Query(authStatus: [
let query = Contact.Query(
authStatus: [
.requested,
.requesting
], isBlocked: false, isBanned: false)
],
isBlocked: reportingStatus.isEnabled() ? false : nil,
isBanned: reportingStatus.isEnabled() ? false : nil
)
session.dbManager.fetchContactsPublisher(query)
.assertNoFailure()
......
......@@ -3,9 +3,11 @@ import UIKit
import Shared
import Combine
import XXModels
import Defaults
import Countries
import Integration
import NetworkMonitor
import ReportingFeature
import DependencyInjection
typealias SearchSnapshot = NSDiffableDataSourceSnapshot<SearchSection, SearchItem>
......@@ -19,6 +21,7 @@ struct SearchLeftViewState {
final class SearchLeftViewModel {
@Dependency var session: SessionType
@Dependency var reportingStatus: ReportingStatus
@Dependency var networkMonitor: NetworkMonitoring
var hudPublisher: AnyPublisher<HUDStatus, Never> {
......@@ -150,7 +153,10 @@ final class SearchLeftViewModel {
user.authStatus = contact.authStatus
}
if user.authStatus != .friend, !user.isBanned, !user.isBlocked {
if user.authStatus != .friend, !reportingStatus.isEnabled() {
snapshot.appendSections([.stranger])
snapshot.appendItems([.stranger(user)], toSection: .stranger)
} else if user.authStatus != .friend, reportingStatus.isEnabled(), !user.isBanned, !user.isBlocked {
snapshot.appendSections([.stranger])
snapshot.appendItems([.stranger(user)], toSection: .stranger)
}
......@@ -159,8 +165,8 @@ final class SearchLeftViewModel {
let localsQuery = Contact.Query(
text: stateSubject.value.input,
authStatus: [.friend],
isBlocked: false,
isBanned: false
isBlocked: reportingStatus.isEnabled() ? false : nil,
isBanned: reportingStatus.isEnabled() ? false : nil
)
if let locals = try? session.dbManager.fetchContacts(localsQuery),
......
import Shared
import Combine
import XXModels
import Defaults
import Foundation
import Permissions
import Integration
import ReportingFeature
import DependencyInjection
enum ScanningStatus: Equatable {
......@@ -23,6 +25,7 @@ enum ScanningError: Equatable {
final class SearchRightViewModel {
@Dependency var session: SessionType
@Dependency var permissions: PermissionHandling
@Dependency var reportingStatus: ReportingStatus
var foundPublisher: AnyPublisher<Contact, Never> {
foundSubject.eraseToAnyPublisher()
......@@ -78,12 +81,12 @@ final class SearchRightViewModel {
/// that we already have
///
if let alreadyContact = try? session.dbManager.fetchContacts(.init(id: [userId])).first {
if alreadyContact.isBlocked {
if alreadyContact.isBlocked, reportingStatus.isEnabled() {
statusSubject.send(.failed(.unknown("You previously blocked this user.")))
return
}
if alreadyContact.isBanned {
if alreadyContact.isBanned, reportingStatus.isEnabled() {
statusSubject.send(.failed(.unknown("This user was banned.")))
return
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment