Skip to content

Commit

Permalink
feat: Remove folder conversation filter when folder deleted - WPB-139…
Browse files Browse the repository at this point in the history
…05 (#2225)
  • Loading branch information
samwyndham authored Nov 29, 2024
1 parent b545aa4 commit eaf59ce
Show file tree
Hide file tree
Showing 21 changed files with 265 additions and 352 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public final class AnyMainCoordinator<Dependencies: MainCoordinatorDependenciesP
public let base: any MainCoordinatorProtocol

private let _showConversationList: @MainActor (_ conversationFilter: ConversationFilter?) async -> Void
private let _applyConversationFilter: @MainActor (_ conversationFilter: ConversationFilter?) -> Void
private let _showArchive: @MainActor () async -> Void
private let _showSettings: @MainActor () async -> Void
private let _showConversation: @MainActor (
Expand All @@ -44,6 +45,9 @@ public final class AnyMainCoordinator<Dependencies: MainCoordinatorDependenciesP
self._showConversationList = { conversationFilter in
await mainCoordinator.showConversationList(conversationFilter: conversationFilter)
}
self._applyConversationFilter = { conversationFilter in
mainCoordinator.applyConversationFilter(conversationFilter)
}
self._showArchive = {
await mainCoordinator.showArchive()
}
Expand Down Expand Up @@ -75,6 +79,11 @@ public final class AnyMainCoordinator<Dependencies: MainCoordinatorDependenciesP
await _showConversationList(conversationFilter)
}

@MainActor
public func applyConversationFilter(_ conversationFilter: ConversationFilter?) {
_applyConversationFilter(conversationFilter)
}

@MainActor
public func showArchive() async {
await _showArchive()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ public final class MainCoordinator<Dependencies>: NSObject, MainCoordinatorProto
}
}

applyConversationFilter(conversationFilter)
}

public func applyConversationFilter(_ conversationFilter: ConversationFilter?) {
// apply the filter to the conversation list
let conversationFilter = conversationFilter.map { ConversationFilter(mappingFrom: $0) }
conversationListUI.conversationFilter = conversationFilter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public protocol MainCoordinatorProtocol: AnyObject {
@MainActor
func showConversationList(conversationFilter: ConversationFilter?) async
@MainActor
func applyConversationFilter(_ filter: ConversationFilter?)
@MainActor
func showArchive() async
@MainActor
func showSettings() async
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ final class AnyMainCoordinatorTests: XCTestCase {
XCTAssertEqual(mockMainCoordinator.showConversationList_Invocations.first, .groups)
}

func testApplyFilterIsInvoked() {
// When
sut.applyConversationFilter(.groups)

// Then
XCTAssertEqual(mockMainCoordinator.applyConversationFilter_Invocations.count, 1)
XCTAssertEqual(mockMainCoordinator.applyConversationFilter_Invocations.first, .groups)
}

func testShowArchiveIsInvoked() async {
// When
await sut.showArchive()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ final class MockMainCoordinatorProtocol: MainCoordinatorProtocol {
showConversationList_Invocations += [conversationFilter]
}

var applyConversationFilter_Invocations: [ConversationFilter?] = []
func applyConversationFilter(_ filter: ConversationFilter?) {
applyConversationFilter_Invocations += [filter]
}

var showArchive_Invocations: [Void] = []
func showArchive() async {
showArchive_Invocations.append(())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,17 @@ public struct ConversationDirectoryChangeInfo {

public protocol ConversationDirectoryObserver: AnyObject {

func conversationDirectoryDidChange(_ changeInfo: ConversationDirectoryChangeInfo)
func conversationDirectoryDidChange(
conversationDirectory: ConversationDirectoryType,
changeInfo: ConversationDirectoryChangeInfo
)

}

public protocol ConversationDirectoryType {

/// All folder created by the user
var allFolders: [LabelType] { get }
/// Folders excluding those marked for deletion
var nonDeletedFolders: [LabelType] { get }

/// Create a new folder with a given name
func createFolder(_ name: String) -> LabelType?
Expand Down Expand Up @@ -136,19 +139,17 @@ private class ConversationListObserverProxy: NSObject, ZMConversationListObserve
}

func conversationListsDidReload() {
observer?.conversationDirectoryDidChange(ConversationDirectoryChangeInfo(
reloaded: true,
updatedLists: [],
updatedFolders: false
))
observer?.conversationDirectoryDidChange(
conversationDirectory: directory,
changeInfo: ConversationDirectoryChangeInfo(reloaded: true, updatedLists: [], updatedFolders: false)
)
}

func conversationListsDidChangeFolders() {
observer?.conversationDirectoryDidChange(ConversationDirectoryChangeInfo(
reloaded: false,
updatedLists: [],
updatedFolders: true
))
observer?.conversationDirectoryDidChange(
conversationDirectory: directory,
changeInfo: ConversationDirectoryChangeInfo(reloaded: false, updatedLists: [], updatedFolders: true)
)
}

func conversationListDidChange(_ changeInfo: ConversationListChangeInfo) {
Expand All @@ -170,11 +171,14 @@ private class ConversationListObserverProxy: NSObject, ZMConversationListObserve
[]
}

observer?.conversationDirectoryDidChange(ConversationDirectoryChangeInfo(
reloaded: false,
updatedLists: updatedLists,
updatedFolders: false
))
observer?.conversationDirectoryDidChange(
conversationDirectory: directory,
changeInfo: ConversationDirectoryChangeInfo(
reloaded: false,
updatedLists: updatedLists,
updatedFolders: false
)
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@

@property (nonatomic, readonly, nonnull) NSMutableDictionary<NSManagedObjectID *, ZMConversationList *> *listsByFolder;
@property (nonatomic, readonly, nonnull) NSArray<id<LabelType>> *allFolders;
@property (nonatomic, readonly, nonnull) NSArray<id<LabelType>> *nonDeletedFolders;


/// Refetches all conversation lists and resets the snapshots
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,12 @@ - (void)refetchAllListsInManagedObjectContext:(NSManagedObjectContext *)moc
return self.folderList.backingList;
}

- (NSArray<id<LabelType>> *)nonDeletedFolders
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"markedForDeletion == NO"];
return [self.folderList.backingList filteredArrayUsingPredicate:predicate];
}

@end


Expand Down
2 changes: 1 addition & 1 deletion wire-ios/Tests/Mocks/MockConverationDirectory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import WireDataModel

class MockConversationDirectory: ConversationDirectoryType {

var allFolders: [LabelType] = []
var nonDeletedFolders: [LabelType] = []
var mockGroupConversations: [ZMConversation] = []
var mockContactsConversations: [ZMConversation] = []
var mockFavoritesConversations: [ZMConversation] = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,10 @@ import XCTest
@testable import Wire

final class MockConversationListViewModelDelegate: NSObject, ConversationListViewModelDelegate {
func listViewModel(_ model: ConversationListViewModel?, didUpdateSection section: Int) {
// no-op
}

func listViewModel(_ model: ConversationListViewModel?, didUpdateSectionForReload section: Int, animated: Bool) {
// no-op
}

func listViewModel(_ model: ConversationListViewModel?, didChangeFolderEnabled folderEnabled: Bool) {
// no-op
}

func reload<C>(
using stagedChangeset: StagedChangeset<C>,
interrupt: ((Changeset<C>) -> Bool)?,
Expand Down Expand Up @@ -65,7 +57,6 @@ final class ConversationListViewModelTests: XCTestCase {
override func setUp() {
super.setUp()

removeViewModelState()
mockUserSession = UserSessionMock()
sut = ConversationListViewModel(userSession: mockUserSession)

Expand All @@ -89,12 +80,6 @@ final class ConversationListViewModelTests: XCTestCase {
super.tearDown()
}

func removeViewModelState() {
guard let persistentURL = ConversationListViewModel.persistentURL else { return }

try? FileManager.default.removeItem(at: persistentURL)
}

// 2 group conversations and 1 contact. First group conversation is mock conversation
func fillDummyConversations(mockConversation: ZMConversation) {
let info = ConversationDirectoryChangeInfo(
Expand All @@ -115,7 +100,10 @@ final class ConversationListViewModelTests: XCTestCase {
mockUserSession.mockConversationDirectory.mockGroupConversations = [mockConversation, teamConversation]
mockUserSession.mockConversationDirectory.mockContactsConversations = [oneToOneConversation]

sut.conversationDirectoryDidChange(info)
sut.conversationDirectoryDidChange(
conversationDirectory: mockUserSession.mockConversationDirectory,
changeInfo: info
)
}

func testForNumberOfItems() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
//
// Wire
// Copyright (C) 2024 Wire Swiss GmbH
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/.
//

import WireDataModel
import XCTest

@testable import Wire

final class ConversationFilterSelectorTests: XCTestCase {

private var sut: ConversationFilterSelector!
private var conversationDirectory: MockConversationDirectory!
private var conversationFilter: ConversationFilter?
private var conversationFilterDidChange: Bool!

@MainActor
override func setUpWithError() throws {
conversationDirectory = MockConversationDirectory()
sut = ConversationFilterSelector(
conversationFilter: { [unowned self] in conversationFilter },
updateConversationFilter: { [unowned self] newValue in
conversationFilter = newValue
conversationFilterDidChange = true
}
)
conversationFilterDidChange = false
}

@MainActor
override func tearDownWithError() throws {
conversationDirectory = nil
sut = nil
conversationFilterDidChange = nil
}

@MainActor
func testConversationDirectoryDidChange_whenFolderFilterSelected() throws {
// GIVEN
let folderA = MockLabel(remoteIdentifier: UUID())
let folderB = MockLabel(remoteIdentifier: UUID())

conversationFilter = .folder(id: folderA.remoteIdentifier!, name: "folderName")

// WHEN
conversationDirectory.nonDeletedFolders = [folderA, folderB]
sut.conversationDirectoryDidChange(conversationDirectory: conversationDirectory, changeInfo: .someValue)

// THEN
XCTAssertFalse(conversationFilterDidChange)
XCTAssertEqual(conversationFilter, .folder(id: folderA.remoteIdentifier!, name: "folderName"))

// WHEN
conversationDirectory.nonDeletedFolders = [folderB]
sut.conversationDirectoryDidChange(conversationDirectory: conversationDirectory, changeInfo: .someValue)

// THEN
XCTAssertTrue(conversationFilterDidChange)
XCTAssertNil(conversationFilter)
}

@MainActor
func testConversationDirectoryDidChange_whenNotFolderFilterSelected() throws {
let testCases: [ConversationFilter?] = [
.favorites,
.groups,
.oneOnOne,
.none
]

for testCase in testCases {
// GIVEN
conversationFilter = testCase

// WHEN
sut.conversationDirectoryDidChange(conversationDirectory: conversationDirectory, changeInfo: .someValue)

// THEN
XCTAssertFalse(conversationFilterDidChange)
}
}

}

private extension ConversationDirectoryChangeInfo {
static let someValue = ConversationDirectoryChangeInfo(
reloaded: false,
updatedLists: [],
updatedFolders: false
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ final class MockMainCoordinator: MainCoordinatorProtocol {
fatalError("Mock method not implemented")
}

@MainActor
func applyConversationFilter(_ filter: ConversationFilter?) {
fatalError("Mock method not implemented")
}

@MainActor
func showArchive() {
fatalError("Mock method not implemented")
Expand Down
4 changes: 4 additions & 0 deletions wire-ios/Wire-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,7 @@
CB43DBC42CE639E800BF5AEB /* FolderPickerViewControllerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB43DBC32CE639E800BF5AEB /* FolderPickerViewControllerBuilder.swift */; };
CB4870F22C7F4FE5001E9151 /* WireTransportSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB4870F12C7F4FE5001E9151 /* WireTransportSupport.framework */; };
CB4E15122C81CC81005DDEC8 /* Down in Frameworks */ = {isa = PBXBuildFile; productRef = CB4E15112C81CC81005DDEC8 /* Down */; };
CB8E51E32CF89D9B00F7F01D /* ConversationFilterSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB8E51E22CF89D9B00F7F01D /* ConversationFilterSelector.swift */; };
D30880FB292CD8F200DDEAB0 /* CallingBottomSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D30880FA292CD8F200DDEAB0 /* CallingBottomSheetViewController.swift */; };
D30880FE292E521D00DDEAB0 /* CallingActionsInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D30880FD292E521D00DDEAB0 /* CallingActionsInfoViewController.swift */; };
D3095761283CEB1C00CEC620 /* MediaShareRestrictionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D309575D283CDFED00CEC620 /* MediaShareRestrictionManager.swift */; };
Expand Down Expand Up @@ -3243,6 +3244,7 @@
CB366A8F2CC7DE410083701F /* ArchivedListViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchivedListViewModelTests.swift; sourceTree = "<group>"; };
CB43DBC32CE639E800BF5AEB /* FolderPickerViewControllerBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderPickerViewControllerBuilder.swift; sourceTree = "<group>"; };
CB4870F12C7F4FE5001E9151 /* WireTransportSupport.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = WireTransportSupport.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CB8E51E22CF89D9B00F7F01D /* ConversationFilterSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationFilterSelector.swift; sourceTree = "<group>"; };
CE06C93E1DF5C3D900497685 /* AVAsset+VideoConvert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVAsset+VideoConvert.swift"; sourceTree = "<group>"; };
CE8E4FA91DF066750009F437 /* FileMetaDataGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileMetaDataGenerator.swift; sourceTree = "<group>"; };
CE8E4FB21DF066EE0009F437 /* AudioProcessing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioProcessing.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -6750,6 +6752,7 @@
children = (
59AF77A02CC7FB39002438D1 /* AnyMainCoordinator.swift */,
596184AC2CC7B5F600787AF0 /* DefaultSettingsPropertyFactoryDelegate.swift */,
CB8E51E22CF89D9B00F7F01D /* ConversationFilterSelector.swift */,
5965E9722C9B1491001D8AE1 /* ConversationListViewController+MainConversationListProtocol.swift */,
59E67CB12CA8C356000F1C17 /* ConversationRootViewController+MainConversationProtocol.swift */,
597CAEB22CA40111002A1160 /* MainCoordinator+ArchivedListViewControllerDelegate.swift */,
Expand Down Expand Up @@ -10191,6 +10194,7 @@
5E65A7A721304B6B008BFCC0 /* UserEmailUpdateCodeSentEventHandler.swift in Sources */,
EF3371C22216D9D9005ED048 /* ZMUser+Self.swift in Sources */,
EFEE97C823229CF5007A4702 /* ConversationListCellDelegate.swift in Sources */,
CB8E51E32CF89D9B00F7F01D /* ConversationFilterSelector.swift in Sources */,
7CED30721FD97748009F0DAC /* IconStringsBuilder.swift in Sources */,
E9B0DED42B5E7151006DC9E4 /* AccentColorPicker.swift in Sources */,
EFABC92420208D80001F9866 /* UIViewController+removeUserConfirmationUI.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ extension ConversationListViewController: ConversationListContainerViewModelDele
withConfiguration: symbolConfiguration
)!

var selectedFilterImage: UIImage = switch listContentController.listViewModel.selectedFilter {
let selectedFilterImage: UIImage = switch listContentController.listViewModel.selectedFilter {
case .favorites, .groups, .oneOnOne, .folder:
filledFilterImage
case .none:
Expand Down
Loading

0 comments on commit eaf59ce

Please sign in to comment.