Send reaction method placeholder (#355)

* Send reaction method placeholder

* Removed unnecessary emoji skin parsing

* Code review fixes
This commit is contained in:
Aleksandrs Proskurins
2022-12-07 13:19:47 +02:00
committed by GitHub
parent 7db7eec98f
commit 1e197d48de
24 changed files with 88 additions and 80 deletions
-4
View File
@@ -163,7 +163,6 @@
5C8AFBF168A41E20835F3B86 /* LoginScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DB34B0C74CD242FED9DD069 /* LoginScreenUITests.swift */; }; 5C8AFBF168A41E20835F3B86 /* LoginScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DB34B0C74CD242FED9DD069 /* LoginScreenUITests.swift */; };
5D04B17929378AB300FD5B00 /* apple_emojis_data.json in Resources */ = {isa = PBXBuildFile; fileRef = 5D04B17829378AB300FD5B00 /* apple_emojis_data.json */; }; 5D04B17929378AB300FD5B00 /* apple_emojis_data.json in Resources */ = {isa = PBXBuildFile; fileRef = 5D04B17829378AB300FD5B00 /* apple_emojis_data.json */; };
5D04B17B29378D3600FD5B00 /* EmojiMartEmoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D04B17A29378D3600FD5B00 /* EmojiMartEmoji.swift */; }; 5D04B17B29378D3600FD5B00 /* EmojiMartEmoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D04B17A29378D3600FD5B00 /* EmojiMartEmoji.swift */; };
5D04B17D2937ADE300FD5B00 /* EmojiItemSkin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D04B17C2937ADE300FD5B00 /* EmojiItemSkin.swift */; };
5D04B17F293A333600FD5B00 /* EmojiPickerSearchFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D04B17E293A333600FD5B00 /* EmojiPickerSearchFieldView.swift */; }; 5D04B17F293A333600FD5B00 /* EmojiPickerSearchFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D04B17E293A333600FD5B00 /* EmojiPickerSearchFieldView.swift */; };
5D04B181293A337400FD5B00 /* EmojiPickerHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D04B180293A337400FD5B00 /* EmojiPickerHeaderView.swift */; }; 5D04B181293A337400FD5B00 /* EmojiPickerHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D04B180293A337400FD5B00 /* EmojiPickerHeaderView.swift */; };
5D2AF8C0DF872E7985F8FE54 /* TimelineDeliveryStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AC06FC11B6638F7BF1670E /* TimelineDeliveryStatusView.swift */; }; 5D2AF8C0DF872E7985F8FE54 /* TimelineDeliveryStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AC06FC11B6638F7BF1670E /* TimelineDeliveryStatusView.swift */; };
@@ -689,7 +688,6 @@
5B2F9D5C39A4494D19F33E38 /* SettingsViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModelProtocol.swift; sourceTree = "<group>"; }; 5B2F9D5C39A4494D19F33E38 /* SettingsViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModelProtocol.swift; sourceTree = "<group>"; };
5D04B17829378AB300FD5B00 /* apple_emojis_data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = apple_emojis_data.json; sourceTree = "<group>"; }; 5D04B17829378AB300FD5B00 /* apple_emojis_data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = apple_emojis_data.json; sourceTree = "<group>"; };
5D04B17A29378D3600FD5B00 /* EmojiMartEmoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiMartEmoji.swift; sourceTree = "<group>"; }; 5D04B17A29378D3600FD5B00 /* EmojiMartEmoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiMartEmoji.swift; sourceTree = "<group>"; };
5D04B17C2937ADE300FD5B00 /* EmojiItemSkin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiItemSkin.swift; sourceTree = "<group>"; };
5D04B17E293A333600FD5B00 /* EmojiPickerSearchFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerSearchFieldView.swift; sourceTree = "<group>"; }; 5D04B17E293A333600FD5B00 /* EmojiPickerSearchFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerSearchFieldView.swift; sourceTree = "<group>"; };
5D04B180293A337400FD5B00 /* EmojiPickerHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerHeaderView.swift; sourceTree = "<group>"; }; 5D04B180293A337400FD5B00 /* EmojiPickerHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerHeaderView.swift; sourceTree = "<group>"; };
5D26A086A8278D39B5756D6F /* project.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = project.yml; sourceTree = "<group>"; }; 5D26A086A8278D39B5756D6F /* project.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = project.yml; sourceTree = "<group>"; };
@@ -1274,7 +1272,6 @@
5DE2282D293F2CF6001790FD /* EmojiLoaderProtocol.swift */, 5DE2282D293F2CF6001790FD /* EmojiLoaderProtocol.swift */,
5DE2282B293F29FC001790FD /* EmojiProvider.swift */, 5DE2282B293F29FC001790FD /* EmojiProvider.swift */,
5BACB442D02C878293C04837 /* EmojiMart */, 5BACB442D02C878293C04837 /* EmojiMart */,
5D04B17C2937ADE300FD5B00 /* EmojiItemSkin.swift */,
); );
path = Emojis; path = Emojis;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -3001,7 +2998,6 @@
A7D48E44D485B143AADDB77D /* Strings+Untranslated.swift in Sources */, A7D48E44D485B143AADDB77D /* Strings+Untranslated.swift in Sources */,
066A1E9B94723EE9F3038044 /* Strings.swift in Sources */, 066A1E9B94723EE9F3038044 /* Strings.swift in Sources */,
44AE0752E001D1D10605CD88 /* Swipe.swift in Sources */, 44AE0752E001D1D10605CD88 /* Swipe.swift in Sources */,
5D04B17D2937ADE300FD5B00 /* EmojiItemSkin.swift in Sources */,
E290C78E7F09F47FD2662986 /* Task.swift in Sources */, E290C78E7F09F47FD2662986 /* Task.swift in Sources */,
43FD77998F33C32718C51450 /* TemplateCoordinator.swift in Sources */, 43FD77998F33C32718C51450 /* TemplateCoordinator.swift in Sources */,
63C9AF0FB8278AF1C0388A0C /* TemplateModels.swift in Sources */, 63C9AF0FB8278AF1C0388A0C /* TemplateModels.swift in Sources */,
@@ -22,7 +22,7 @@ struct EmojiPickerScreenCoordinatorParameters {
} }
enum EmojiPickerScreenCoordinatorAction { enum EmojiPickerScreenCoordinatorAction {
case selectEmoji(emojiId: String, itemId: String) case emojiSelected(emoji: String, itemId: String)
} }
final class EmojiPickerScreenCoordinator: CoordinatorProtocol { final class EmojiPickerScreenCoordinator: CoordinatorProtocol {
@@ -42,8 +42,8 @@ final class EmojiPickerScreenCoordinator: CoordinatorProtocol {
guard let self else { return } guard let self else { return }
MXLog.debug("EmojiPickerScreenViewModel did complete with result: \(action).") MXLog.debug("EmojiPickerScreenViewModel did complete with result: \(action).")
switch action { switch action {
case let .selectEmoji(emojiId: emojiId): case let .emojiSelected(emoji: emoji):
self.callback?(.selectEmoji(emojiId: emojiId, itemId: self.parameters.itemId)) self.callback?(.emojiSelected(emoji: emoji, itemId: self.parameters.itemId))
} }
} }
} }
@@ -17,7 +17,7 @@
import Foundation import Foundation
enum EmojiPickerScreenViewModelAction { enum EmojiPickerScreenViewModelAction {
case selectEmoji(emojiId: String) case emojiSelected(emoji: String)
} }
struct EmojiPickerScreenViewState: BindableState { struct EmojiPickerScreenViewState: BindableState {
@@ -26,7 +26,7 @@ struct EmojiPickerScreenViewState: BindableState {
enum EmojiPickerScreenViewAction { enum EmojiPickerScreenViewAction {
case search(searchString: String) case search(searchString: String)
case emojiSelected(emoji: EmojiPickerEmojiViewData) case emojiTapped(emoji: EmojiPickerEmojiViewData)
} }
struct EmojiPickerEmojiCategoryViewData: Identifiable { struct EmojiPickerEmojiCategoryViewData: Identifiable {
@@ -37,8 +37,8 @@ class EmojiPickerScreenViewModel: EmojiPickerScreenViewModelType, EmojiPickerScr
case let .search(searchString: searchString): case let .search(searchString: searchString):
let categories = await emojiProvider.getCategories(searchString: searchString) let categories = await emojiProvider.getCategories(searchString: searchString)
state.categories = convert(emojiCategories: categories) state.categories = convert(emojiCategories: categories)
case let .emojiSelected(emoji: emoji): case let .emojiTapped(emoji: emoji):
callback?(.selectEmoji(emojiId: emoji.id)) callback?(.emojiSelected(emoji: emoji.value))
} }
} }
@@ -59,7 +59,7 @@ class EmojiPickerScreenViewModel: EmojiPickerScreenViewModelType, EmojiPickerScr
guard let firstSkin = emojiItem.skins.first else { guard let firstSkin = emojiItem.skins.first else {
return nil return nil
} }
return EmojiPickerEmojiViewData(id: emojiItem.id, value: firstSkin.value) return EmojiPickerEmojiViewData(id: emojiItem.id, value: firstSkin)
} }
return EmojiPickerEmojiCategoryViewData(id: emojiCategory.id, emojis: emojisViewData) return EmojiPickerEmojiCategoryViewData(id: emojiCategory.id, emojis: emojisViewData)
@@ -36,7 +36,7 @@ struct EmojiPickerScreen: View {
Text(emoji.value) Text(emoji.value)
.frame(width: 45, height: 45) .frame(width: 45, height: 45)
.onTapGesture { .onTapGesture {
context.send(viewAction: .emojiSelected(emoji: emoji)) context.send(viewAction: .emojiTapped(emoji: emoji))
} }
} }
} }
@@ -22,7 +22,7 @@ struct RoomScreenCoordinatorParameters {
let mediaProvider: MediaProviderProtocol let mediaProvider: MediaProviderProtocol
let roomName: String? let roomName: String?
let roomAvatarUrl: String? let roomAvatarUrl: String?
let emojiProvide: EmojiProviderProtocol let emojiProvider: EmojiProviderProtocol
} }
final class RoomScreenCoordinator: CoordinatorProtocol { final class RoomScreenCoordinator: CoordinatorProtocol {
@@ -113,7 +113,8 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
} }
private func displayEmojiPickerScreen(for itemId: String) { private func displayEmojiPickerScreen(for itemId: String) {
guard let emojiProvider = parameters?.emojiProvide else { guard let emojiProvider = parameters?.emojiProvider,
let timelineController = parameters?.timelineController else {
fatalError() fatalError()
} }
let params = EmojiPickerScreenCoordinatorParameters(emojiProvider: emojiProvider, let params = EmojiPickerScreenCoordinatorParameters(emojiProvider: emojiProvider,
@@ -121,9 +122,11 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
let coordinator = EmojiPickerScreenCoordinator(parameters: params) let coordinator = EmojiPickerScreenCoordinator(parameters: params)
coordinator.callback = { [weak self] action in coordinator.callback = { [weak self] action in
switch action { switch action {
case let .selectEmoji(emojiId: emojiId, itemId: itemId): case let .emojiSelected(emoji: emoji, itemId: itemId):
self?.navigationController.dismissSheet() self?.navigationController.dismissSheet()
MXLog.debug("Save \(emojiId) for \(itemId)") Task {
await timelineController.sendReaction(emoji, for: itemId)
}
} }
} }
@@ -31,6 +31,7 @@ enum RoomScreenComposerMode: Equatable {
enum RoomScreenViewAction { enum RoomScreenViewAction {
case displayEmojiPicker(itemId: String) case displayEmojiPicker(itemId: String)
case emojiTapped(emoji: String, itemId: String)
case paginateBackwards case paginateBackwards
case itemAppeared(id: String) case itemAppeared(id: String)
case itemDisappeared(id: String) case itemDisappeared(id: String)
@@ -117,6 +117,9 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
state.composerMode = .default state.composerMode = .default
case .cancelEdit: case .cancelEdit:
state.composerMode = .default state.composerMode = .default
case .emojiTapped(let emoji, let itemId):
await timelineController.sendReaction(emoji, for: itemId)
state.displayReactionsMenuForItemId = ""
} }
} }
@@ -19,13 +19,18 @@ import SwiftUI
struct TimelineItemReactionsMenuView: View { struct TimelineItemReactionsMenuView: View {
private let emojis = ["👍🏼", "👎🏼", "😄", "🙏🏼", "😇"] private let emojis = ["👍🏼", "👎🏼", "😄", "🙏🏼", "😇"]
var onEmojiSelected: ((String) -> Void)?
var onDisplayEmojiPicker: (() -> Void)? var onDisplayEmojiPicker: (() -> Void)?
var body: some View { var body: some View {
HStack { HStack {
HStack(spacing: 10) { HStack(spacing: 10) {
ForEach(emojis, id: \.self) { emoji in ForEach(emojis, id: \.self) { emoji in
Text(emoji) Button {
onEmojiSelected?(emoji)
} label: {
Text(emoji)
}
} }
} }
.padding(10) .padding(10)
@@ -183,9 +183,11 @@ struct TimelineTableView: UIViewRepresentable {
cell.contentConfiguration = UIHostingConfiguration { cell.contentConfiguration = UIHostingConfiguration {
VStack { VStack {
if viewModelContext.viewState.displayReactionsMenuForItemId == timelineItem.id { if viewModelContext.viewState.displayReactionsMenuForItemId == timelineItem.id {
TimelineItemReactionsMenuView { TimelineItemReactionsMenuView(onEmojiSelected: { emoji in
viewModelContext.send(viewAction: .emojiTapped(emoji: emoji, itemId: timelineItem.id))
}, onDisplayEmojiPicker: {
viewModelContext.send(viewAction: .displayEmojiPicker(itemId: timelineItem.id)) viewModelContext.send(viewAction: .displayEmojiPicker(itemId: timelineItem.id))
} })
} }
timelineItem timelineItem
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
@@ -20,7 +20,7 @@ struct EmojiItem: Equatable, Identifiable {
var id: String var id: String
let name: String let name: String
let keywords: [String] let keywords: [String]
let skins: [EmojiItemSkin] let skins: [String]
} }
extension EmojiItem { extension EmojiItem {
@@ -28,8 +28,8 @@ extension EmojiItem {
id = emojiMart.id id = emojiMart.id
name = emojiMart.name name = emojiMart.name
keywords = emojiMart.keywords keywords = emojiMart.keywords
skins = emojiMart.skins.compactMap { emojiMartEmojiSkin in skins = emojiMart.skins.map { emojiMartEmojiSkin in
EmojiItemSkin(from: emojiMartEmojiSkin) emojiMartEmojiSkin.native
} }
} }
} }
@@ -1,36 +0,0 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
struct EmojiItemSkin: Equatable {
let value: String
init?(from emojiMartEmojiSkin: EmojiMartEmojiSkin) {
let unicodeStringComponents = emojiMartEmojiSkin.unified.components(separatedBy: "-")
var emoji = ""
for unicodeStringComponent in unicodeStringComponents {
guard let unicodeCodePoint = Int(unicodeStringComponent, radix: 16),
let emojiUnicodeScalar = UnicodeScalar(unicodeCodePoint) else {
return nil
}
emoji.append(String(emojiUnicodeScalar))
}
value = emoji
}
}
@@ -65,6 +65,10 @@ struct MockRoomProxy: RoomProxyProtocol {
.failure(.failedSendingMessage) .failure(.failedSendingMessage)
} }
func sendReaction(_ reaction: String, for eventId: String) async -> Result<Void, RoomProxyError> {
.failure(.failedSendingMessage)
}
func editMessage(_ newMessage: String, originalEventId: String) async -> Result<Void, RoomProxyError> { func editMessage(_ newMessage: String, originalEventId: String) async -> Result<Void, RoomProxyError> {
.failure(.failedSendingMessage) .failure(.failedSendingMessage)
} }
@@ -186,6 +186,12 @@ class RoomProxy: RoomProxyProtocol {
} }
} }
func sendReaction(_ reaction: String, for eventId: String) async -> Result<Void, RoomProxyError> {
await Task.dispatch(on: .global()) {
.success(())
}
}
func editMessage(_ newMessage: String, originalEventId: String) async -> Result<Void, RoomProxyError> { func editMessage(_ newMessage: String, originalEventId: String) async -> Result<Void, RoomProxyError> {
sendMessageBgTask = backgroundTaskService.startBackgroundTask(withName: "SendMessage", isReusable: true) sendMessageBgTask = backgroundTaskService.startBackgroundTask(withName: "SendMessage", isReusable: true)
defer { defer {
@@ -57,6 +57,8 @@ protocol RoomProxyProtocol {
func sendMessage(_ message: String, inReplyToEventId: String?) async -> Result<Void, RoomProxyError> func sendMessage(_ message: String, inReplyToEventId: String?) async -> Result<Void, RoomProxyError>
func sendReaction(_ reaction: String, for eventId: String) async -> Result<Void, RoomProxyError>
func editMessage(_ newMessage: String, originalEventId: String) async -> Result<Void, RoomProxyError> func editMessage(_ newMessage: String, originalEventId: String) async -> Result<Void, RoomProxyError>
func redact(_ eventID: String) async -> Result<Void, RoomProxyError> func redact(_ eventID: String) async -> Result<Void, RoomProxyError>
@@ -116,6 +116,8 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
func sendReply(_ message: String, to itemId: String) async { } func sendReply(_ message: String, to itemId: String) async { }
func sendReaction(_ reaction: String, for itemId: String) async { }
func editMessage(_ newMessage: String, of itemId: String) async { } func editMessage(_ newMessage: String, of itemId: String) async { }
func redact(_ eventID: String) async { } func redact(_ eventID: String) async { }
@@ -31,6 +31,10 @@ struct MockRoomTimelineProvider: RoomTimelineProviderProtocol {
.failure(.failedSendingMessage) .failure(.failedSendingMessage)
} }
func sendReaction(_ reaction: String, for itemId: String) async -> Result<Void, RoomTimelineProviderError> {
.failure(.failedSendingReaction)
}
func editMessage(_ newMessage: String, originalItemId: String) async -> Result<Void, RoomTimelineProviderError> { func editMessage(_ newMessage: String, originalItemId: String) async -> Result<Void, RoomTimelineProviderError> {
.failure(.failedSendingMessage) .failure(.failedSendingMessage)
} }
@@ -108,6 +108,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
func processItemDisappearance(_ itemId: String) { } func processItemDisappearance(_ itemId: String) { }
// swiftlint:disable:next cyclomatic_complexity
func processItemTap(_ itemId: String) async -> RoomTimelineControllerAction { func processItemTap(_ itemId: String) async -> RoomTimelineControllerAction {
guard let timelineItem = timelineItems.first(where: { $0.id == itemId }) else { guard let timelineItem = timelineItems.first(where: { $0.id == itemId }) else {
return .none return .none
@@ -163,6 +164,13 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
} }
} }
func sendReaction(_ reaction: String, for itemId: String) async {
switch await timelineProvider.sendReaction(reaction, for: itemId) {
default:
break
}
}
func editMessage(_ newMessage: String, of itemId: String) async { func editMessage(_ newMessage: String, of itemId: String) async {
switch await timelineProvider.editMessage(newMessage, originalItemId: itemId) { switch await timelineProvider.editMessage(newMessage, originalItemId: itemId) {
default: default:
@@ -56,6 +56,8 @@ protocol RoomTimelineControllerProtocol {
func editMessage(_ newMessage: String, of itemId: String) async func editMessage(_ newMessage: String, of itemId: String) async
func sendReaction(_ reaction: String, for itemId: String) async
func redact(_ eventID: String) async func redact(_ eventID: String) async
func debugDescriptionFor(_ itemId: String) -> String func debugDescriptionFor(_ itemId: String) -> String
@@ -104,6 +104,17 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
} }
} }
func sendReaction(_ reaction: String, for itemId: String) async -> Result<Void, RoomTimelineProviderError> {
switch await roomProxy.sendReaction(reaction, for: itemId) {
case .success:
MXLog.info("Finished sending reaction")
return .success(())
case .failure(let error):
MXLog.error("Failed sending reaction with error: \(error)")
return .failure(.failedSendingReaction)
}
}
func editMessage(_ newMessage: String, originalItemId: String) async -> Result<Void, RoomTimelineProviderError> { func editMessage(_ newMessage: String, originalItemId: String) async -> Result<Void, RoomTimelineProviderError> {
MXLog.info("Editing message: \(originalItemId)") MXLog.info("Editing message: \(originalItemId)")
switch await roomProxy.editMessage(newMessage, originalEventId: originalItemId) { switch await roomProxy.editMessage(newMessage, originalEventId: originalItemId) {
@@ -21,6 +21,7 @@ enum RoomTimelineProviderError: Error {
case noMoreMessagesToBackPaginate case noMoreMessagesToBackPaginate
case failedPaginatingBackwards case failedPaginatingBackwards
case failedSendingMessage case failedSendingMessage
case failedSendingReaction
case failedRedactingItem case failedRedactingItem
case generic case generic
} }
@@ -34,6 +35,8 @@ protocol RoomTimelineProviderProtocol {
func sendMessage(_ message: String, inReplyToItemId: String?) async -> Result<Void, RoomTimelineProviderError> func sendMessage(_ message: String, inReplyToItemId: String?) async -> Result<Void, RoomTimelineProviderError>
func sendReaction(_ reaction: String, for itemId: String) async -> Result<Void, RoomTimelineProviderError>
func editMessage(_ newMessage: String, originalItemId: String) async -> Result<Void, RoomTimelineProviderError> func editMessage(_ newMessage: String, originalItemId: String) async -> Result<Void, RoomTimelineProviderError>
func redact(_ eventID: String) async -> Result<Void, RoomTimelineProviderError> func redact(_ eventID: String) async -> Result<Void, RoomTimelineProviderError>
@@ -150,7 +150,7 @@ class UserSessionFlowCoordinator: CoordinatorProtocol {
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
roomName: roomProxy.displayName ?? roomProxy.name, roomName: roomProxy.displayName ?? roomProxy.name,
roomAvatarUrl: roomProxy.avatarURL, roomAvatarUrl: roomProxy.avatarURL,
emojiProvide: emojiProvider) emojiProvider: emojiProvider)
let coordinator = RoomScreenCoordinator(parameters: parameters) let coordinator = RoomScreenCoordinator(parameters: parameters)
navigationController.push(coordinator) { [weak self] in navigationController.push(coordinator) { [weak self] in
@@ -122,7 +122,7 @@ class MockScreen: Identifiable {
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
roomName: "Some room name", roomName: "Some room name",
roomAvatarUrl: nil, roomAvatarUrl: nil,
emojiProvide: EmojiProvider()) emojiProvider: EmojiProvider())
return RoomScreenCoordinator(parameters: parameters) return RoomScreenCoordinator(parameters: parameters)
case .roomEncryptedWithAvatar: case .roomEncryptedWithAvatar:
let parameters = RoomScreenCoordinatorParameters(navigationController: navigationController, let parameters = RoomScreenCoordinatorParameters(navigationController: navigationController,
@@ -130,7 +130,7 @@ class MockScreen: Identifiable {
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
roomName: "Some room name", roomName: "Some room name",
roomAvatarUrl: "mock_url", roomAvatarUrl: "mock_url",
emojiProvide: EmojiProvider()) emojiProvider: EmojiProvider())
return RoomScreenCoordinator(parameters: parameters) return RoomScreenCoordinator(parameters: parameters)
case .sessionVerification: case .sessionVerification:
let parameters = SessionVerificationCoordinatorParameters(sessionVerificationControllerProxy: MockSessionVerificationControllerProxy()) let parameters = SessionVerificationCoordinatorParameters(sessionVerificationControllerProxy: MockSessionVerificationControllerProxy())
+9 -17
View File
@@ -28,7 +28,7 @@ final class EmojiProviderTests: XCTestCase {
} }
func test_whenEmojisLoaded_categoriesAreLoadedFromLoader() async throws { func test_whenEmojisLoaded_categoriesAreLoadedFromLoader() async throws {
let item = EmojiItem(id: "test", name: "test", keywords: ["1", "2"], skins: [try slightlySmilingFaceEmoji()]) let item = EmojiItem(id: "test", name: "test", keywords: ["1", "2"], skins: ["🙂"])
let category = EmojiCategory(id: "test", emojis: [item]) let category = EmojiCategory(id: "test", emojis: [item])
emojiLoaderMock.categories = [category] emojiLoaderMock.categories = [category]
let categories = await sut.getCategories() let categories = await sut.getCategories()
@@ -36,7 +36,7 @@ final class EmojiProviderTests: XCTestCase {
} }
func test_whenEmojisLoadedAndSearchStringEmpty_allCategoriesReturned() async throws { func test_whenEmojisLoadedAndSearchStringEmpty_allCategoriesReturned() async throws {
let item = EmojiItem(id: "test", name: "test", keywords: ["1", "2"], skins: [try slightlySmilingFaceEmoji()]) let item = EmojiItem(id: "test", name: "test", keywords: ["1", "2"], skins: ["🙂"])
let category = EmojiCategory(id: "test", emojis: [item]) let category = EmojiCategory(id: "test", emojis: [item])
emojiLoaderMock.categories = [category] emojiLoaderMock.categories = [category]
let categories = await sut.getCategories(searchString: "") let categories = await sut.getCategories(searchString: "")
@@ -45,9 +45,9 @@ final class EmojiProviderTests: XCTestCase {
func test_whenEmojisLoadedSecondTime_cachedValuesAreUsed() async throws { func test_whenEmojisLoadedSecondTime_cachedValuesAreUsed() async throws {
let categoriesForFirstLoad = [EmojiCategory(id: "test", let categoriesForFirstLoad = [EmojiCategory(id: "test",
emojis: [EmojiItem(id: "test", name: "test", keywords: ["1", "2"], skins: [try slightlySmilingFaceEmoji()])])] emojis: [EmojiItem(id: "test", name: "test", keywords: ["1", "2"], skins: ["🙂"])])]
let categoriesForSecondLoad = [EmojiCategory(id: "test2", let categoriesForSecondLoad = [EmojiCategory(id: "test2",
emojis: [EmojiItem(id: "test2", name: "test2", keywords: ["3", "4"], skins: [try meltingFaceEmoji()])])] emojis: [EmojiItem(id: "test2", name: "test2", keywords: ["3", "4"], skins: ["🫠"])])]
emojiLoaderMock.categories = categoriesForFirstLoad emojiLoaderMock.categories = categoriesForFirstLoad
_ = await sut.getCategories() _ = await sut.getCategories()
emojiLoaderMock.categories = categoriesForSecondLoad emojiLoaderMock.categories = categoriesForSecondLoad
@@ -62,38 +62,30 @@ final class EmojiProviderTests: XCTestCase {
emojis: [EmojiItem(id: "\(searchString)_123", emojis: [EmojiItem(id: "\(searchString)_123",
name: "emoji0", name: "emoji0",
keywords: ["key1", "key1"], keywords: ["key1", "key1"],
skins: [try slightlySmilingFaceEmoji()]), skins: ["🙂"]),
EmojiItem(id: "emoji_1", EmojiItem(id: "emoji_1",
name: searchString, name: searchString,
keywords: ["key1", "key1"], keywords: ["key1", "key1"],
skins: [try slightlySmilingFaceEmoji()]), skins: ["🙂"]),
EmojiItem(id: "emoji_2", EmojiItem(id: "emoji_2",
name: "emoji2", name: "emoji2",
keywords: ["key1", "\(searchString)_123"], keywords: ["key1", "\(searchString)_123"],
skins: [try slightlySmilingFaceEmoji()]), skins: ["🙂"]),
EmojiItem(id: "emoji_3", EmojiItem(id: "emoji_3",
name: "emoji_3", name: "emoji_3",
keywords: ["key1", "key1"], keywords: ["key1", "key1"],
skins: [try slightlySmilingFaceEmoji()])])) skins: ["🙂"])]))
categories.append(EmojiCategory(id: "test", categories.append(EmojiCategory(id: "test",
emojis: [EmojiItem(id: "\(searchString)_123", emojis: [EmojiItem(id: "\(searchString)_123",
name: "emoji0", name: "emoji0",
keywords: ["key1", "key1"], keywords: ["key1", "key1"],
skins: [try slightlySmilingFaceEmoji()])])) skins: ["🙂"])]))
emojiLoaderMock.categories = categories emojiLoaderMock.categories = categories
_ = await sut.getCategories() _ = await sut.getCategories()
let result = await sut.getCategories(searchString: searchString) let result = await sut.getCategories(searchString: searchString)
XCTAssertEqual(result.count, 2) XCTAssertEqual(result.count, 2)
XCTAssertEqual(result.first?.emojis.count, 3) XCTAssertEqual(result.first?.emojis.count, 3)
} }
private func slightlySmilingFaceEmoji() throws -> EmojiItemSkin {
try XCTUnwrap(EmojiItemSkin(from: EmojiMartEmojiSkin(unified: "1f642", native: "🙂")))
}
private func meltingFaceEmoji() throws -> EmojiItemSkin {
try XCTUnwrap(EmojiItemSkin(from: EmojiMartEmojiSkin(unified: "1fae0", native: "🫠")))
}
} }
private class EmojiLoaderMock: EmojiLoaderProtocol { private class EmojiLoaderMock: EmojiLoaderProtocol {