Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import Foundation
import WireCoreCrypto

@testable import WireDataModel
@testable import WireDataModelSupport

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import Foundation
@testable import WireDataModel
@testable import WireDataModelSupport

class E2EIVerificationStatusServiceTests: XCTestCase {
final class E2EIVerificationStatusServiceTests: XCTestCase {

var sut: E2EIVerificationStatusService!
var mockCoreCryptoContext: MockCoreCryptoContextProtocol!
Expand Down
3 changes: 2 additions & 1 deletion wire-ios-data-model/Tests/MLS/MLSClientIdTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@

import Foundation
import XCTest

@testable import WireDataModel

class MLSClientIdTests: ZMConversationTestsBase {
final class MLSClientIdTests: ZMConversationTestsBase {

func test_itCreatesALowercasedClientId() {
// GIVEN
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ final class AddParticipantsViewControllerSnapshotTests: XCTestCase {
// MARK: - setUp

override func setUp() {
super.setUp()
snapshotHelper = SnapshotHelper()
SelfUser.setupMockSelfUser(inTeam: UUID())
mockSelfUser = SelfUser.provider?.providedSelfUser as? MockUserType
Expand All @@ -70,8 +69,6 @@ final class AddParticipantsViewControllerSnapshotTests: XCTestCase {
sut = nil
userSession = nil
mockSelfUser = nil

super.tearDown()
}

// MARK: - Snapshot Tests
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// Wire
// Copyright (C) 2026 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 WireTestingPackage
import XCTest

@testable import Wire

final class EmptyAppsSearchResultViewSnapshotTests: XCTestCase {

// MARK: Properties

private var snapshotHelper: SnapshotHelper!

// MARK: setUp / tearDown

override func setUp() {
snapshotHelper = SnapshotHelper()
}

override func tearDown() {
snapshotHelper = nil
}

// MARK: Snapshot Tests

func testAppearance() {
// GIVEN
let sut = EmptyAppsSearchResultView()
sut.frame = CGRect(
origin: .zero,
size: CGSize(width: 320, height: 480)
)
sut.backgroundColor = .systemBackground

// WHEN & THEN
snapshotHelper
.withUserInterfaceStyle(.light)
.verify(
matching: sut,
named: "LightTheme",
file: #filePath,
testName: #function,
line: #line
)

snapshotHelper
.withUserInterfaceStyle(.dark)
.verify(
matching: sut,
named: "DarkTheme",
file: #filePath,
testName: #function,
line: #line
)
}

}
16 changes: 8 additions & 8 deletions wire-ios/Wire-iOS Tests/EmptySearchResultsViewTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ final class EmptySearchResultsViewTests: XCTestCase {
sut = setupEmptySearchResultsView(
isSelfUserAdmin: false,
isFederationEnabled: false,
searchingForApps: false,
searchingForBots: false,
hasFilter: true
)
}
Expand All @@ -59,7 +59,7 @@ final class EmptySearchResultsViewTests: XCTestCase {
sut = setupEmptySearchResultsView(
isSelfUserAdmin: false,
isFederationEnabled: true,
searchingForApps: false,
searchingForBots: false,
hasFilter: true
)

Expand All @@ -72,7 +72,7 @@ final class EmptySearchResultsViewTests: XCTestCase {
sut = setupEmptySearchResultsView(
isSelfUserAdmin: false,
isFederationEnabled: false,
searchingForApps: false,
searchingForBots: false,
hasFilter: false
)

Expand All @@ -85,7 +85,7 @@ final class EmptySearchResultsViewTests: XCTestCase {
sut = setupEmptySearchResultsView(
isSelfUserAdmin: false,
isFederationEnabled: false,
searchingForApps: true,
searchingForBots: true,
hasFilter: true
)

Expand All @@ -98,7 +98,7 @@ final class EmptySearchResultsViewTests: XCTestCase {
sut = setupEmptySearchResultsView(
isSelfUserAdmin: false,
isFederationEnabled: false,
searchingForApps: true,
searchingForBots: true,
hasFilter: false
)

Expand All @@ -111,7 +111,7 @@ final class EmptySearchResultsViewTests: XCTestCase {
sut = setupEmptySearchResultsView(
isSelfUserAdmin: true,
isFederationEnabled: false,
searchingForApps: true,
searchingForBots: true,
hasFilter: false
)

Expand All @@ -124,7 +124,7 @@ final class EmptySearchResultsViewTests: XCTestCase {
func setupEmptySearchResultsView(
isSelfUserAdmin: Bool,
isFederationEnabled: Bool,
searchingForApps: Bool,
searchingForBots: Bool,
hasFilter: Bool
) -> EmptySearchResultsView {

Expand All @@ -134,7 +134,7 @@ final class EmptySearchResultsViewTests: XCTestCase {
)
sut.overrideUserInterfaceStyle = .dark
sut.updateStatus(
searchingForApps: searchingForApps,
searchingForBots: searchingForBots,
hasFilter: hasFilter
)
configureBounds(for: sut)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions wire-ios/Wire-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,7 @@
"EmoticonSubstitution/NSMutableAttributedString+ReplaceEmojiTests.swift",
"EmoticonSubstitution/NSString+EmoticonSubstitutionTests.swift",
"EmoticonSubstitution/XCTestCase+EmoticonSubstitutionConfiguration.swift",
EmptyAppsSearchResultViewSnapshotTests.swift,
EmptySearchResultsViewTests.swift,
EphemeralKeyboardViewControllerTests.swift,
EphemeralTimeoutFormatterTests.swift,
Expand Down
6 changes: 6 additions & 0 deletions wire-ios/Wire-iOS/Generated/Strings+Generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4841,6 +4841,12 @@ internal enum L10n {
/// New conversation
internal static let title = L10n.tr("Localizable", "peoplepicker.navigation_header.title", fallback: "New conversation")
}
internal enum NoAppsAdded {
/// Apps are helpers that can improve your workflow. To use them, ask your team admin.
internal static let message = L10n.tr("Localizable", "peoplepicker.no_apps_added.message", fallback: "Apps are helpers that can improve your workflow. To use them, ask your team admin.")
/// Your team hasn’t added apps yet
internal static let title = L10n.tr("Localizable", "peoplepicker.no_apps_added.title", fallback: "Your team hasn’t added apps yet")
}
internal enum NoMatchingResults {
internal enum Action {
/// Learn more
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@
"peoplepicker.no_matching_results.message.apps" = "No results.";
"peoplepicker.no_matching_results.message.apps_not_enabled_admin" = "Apps are helpers that can improve your workflow.";
"peoplepicker.no_matching_results.message.apps_not_enabled" = "Apps are helpers that can improve your workflow. To enable them, ask your administrator.";
"peoplepicker.no_apps_added.title" = "Your team hasn’t added apps yet";
"peoplepicker.no_apps_added.message" = "Apps are helpers that can improve your workflow. To use them, ask your team admin.";

"peoplepicker.send_invitation.dialog.title" = "Invitation sent";
"peoplepicker.send_invitation.dialog.message" = "It can be used for 2 weeks. Send a new one if it expires.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ final class AddParticipantsViewController: UIViewController {
updateSelectionValues()

if searchResultsViewController.isResultEmpty {
emptyResultView.updateStatus(searchingForApps: false, hasFilter: false)
emptyResultView.updateStatus(searchingForBots: false, hasFilter: false)
}
}

Expand Down Expand Up @@ -466,10 +466,10 @@ final class AddParticipantsViewController: UIViewController {
}

private func performSearch() {
let searchingForAppsOrBots = [.apps, .bots].contains(searchResultsViewController.searchGroup)
let searchingForBots = [.apps, .bots].contains(searchResultsViewController.searchGroup)
let hasFilter = !searchHeaderViewController.tokenField.filterText.isEmpty

emptyResultView.updateStatus(searchingForApps: searchingForAppsOrBots, hasFilter: hasFilter)
emptyResultView.updateStatus(searchingForBots: searchingForBots, hasFilter: hasFilter)

switch (searchResultsViewController.searchGroup, hasFilter) {
case (.apps, _):
Expand All @@ -485,13 +485,28 @@ final class AddParticipantsViewController: UIViewController {
searchResultsViewController.mode = .search
searchResultsViewController.searchForLocalUsers(withQuery: searchHeaderViewController.tokenField.filterText)
}
if searchResultsViewController.searchGroup == .apps, !hasFilter {
showEmptyAppsSearchResultView()
} else {
hideEmptyAppsSearchResultView()
}
}

private func addSelectedParticipants(to conversation: GroupDetailsConversationType) {
let selectedUsers = userSelection.users

(conversation as? ZMConversation)?.addOrShowError(participants: Array(selectedUsers))
}

private func showEmptyAppsSearchResultView() {
let emptyAppsSearchResultView = EmptyAppsSearchResultView()
searchResultsViewController.searchResultsView.emptyResultView = emptyAppsSearchResultView
}

private func hideEmptyAppsSearchResultView() {
searchResultsViewController.searchResultsView.emptyResultView = emptyResultView
}

}

extension AddParticipantsViewController: UserSelectionObserver {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// Wire
// Copyright (C) 2026 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 UIKit
import WireDesign

final class EmptyAppsSearchResultView: UIView {

override init(frame: CGRect) {
super.init(frame: frame)
setup()
}

@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {

Check warning on line 30 in wire-ios/Wire-iOS/Sources/UserInterface/SearchUI/EmptyAppsSearchResultView.swift

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unused function parameter "aDecoder" or name it "_".

See more on https://sonarcloud.io/project/issues?id=wireapp_wire-ios&issues=AZ1EerAsPAYx8a-QbYdM&open=AZ1EerAsPAYx8a-QbYdM&pullRequest=4518
fatalError("init(coder:) is not supported")
}

private func setup() {
let headlineLabel = UILabel()
headlineLabel.numberOfLines = 0
headlineLabel.text = L10n.Localizable.Peoplepicker.NoAppsAdded.title
headlineLabel.font = .font(for: .body1).withWeight(.bold)
let contentLabel = UILabel()
contentLabel.numberOfLines = 0
contentLabel.text = L10n.Localizable.Peoplepicker.NoAppsAdded.message
contentLabel.font = .font(for: .body1)
let stackView = UIStackView(arrangedSubviews: [headlineLabel, contentLabel])
stackView.axis = .vertical
stackView.spacing = 24
stackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)
NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
stackView.topAnchor.constraint(equalTo: topAnchor, constant: 16),
trailingAnchor.constraint(greaterThanOrEqualTo: stackView.trailingAnchor, constant: 16),
bottomAnchor.constraint(greaterThanOrEqualTo: stackView.bottomAnchor, constant: 16)
])
}

}

@available(iOS 17, *)
#Preview {
EmptyAppsSearchResultView()
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ private enum EmptySearchResultsViewState {
case initialSearch
case noUsers
case everyoneAdded
case noApps
case noBots
case noAppsEnabled
}

Expand Down Expand Up @@ -78,7 +78,7 @@ final class EmptySearchResultsView: UIView {
Message.usersAndFederation
case .noUsers:
Message.users
case .noApps:
case .noBots:
Message.apps
case .noAppsEnabled where isSelfUserAdmin:
Message.appsNotEnabledAdmin
Expand All @@ -95,7 +95,7 @@ final class EmptySearchResultsView: UIView {
switch state {
case .initialSearch:
return UIImage()
case .noApps, .noAppsEnabled:
case .noBots, .noAppsEnabled:
icon = .bot
default:
icon = .personalProfile
Expand Down Expand Up @@ -171,12 +171,12 @@ final class EmptySearchResultsView: UIView {

// MARK: - Public Interface

func updateStatus(searchingForApps: Bool, hasFilter: Bool) {
switch (searchingForApps, hasFilter) {
func updateStatus(searchingForBots: Bool, hasFilter: Bool) {
switch (searchingForBots, hasFilter) {
case (true, false):
state = .noAppsEnabled
case (true, true):
state = .noApps
state = .noBots
case (false, true):
state = .noUsers
case (false, false):
Expand Down
Loading
Loading