mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
Send reaction method placeholder (#355)
* Send reaction method placeholder * Removed unnecessary emoji skin parsing * Code review fixes
This commit is contained in:
committed by
GitHub
parent
7db7eec98f
commit
1e197d48de
@@ -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 = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -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())
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user