Merge pull request #2528 from vector-im/riot_2459

Reactions: Update quick reactions
This commit is contained in:
SBiOSoftWhare 2019-06-25 16:08:55 +02:00 committed by GitHub
commit 9c830d04c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 473 additions and 413 deletions

View file

@ -13,6 +13,7 @@ Improvements:
* Message Editing: Editing in the timeline (#2404).
* Read receipts: They are now counted at the MatrixKit level.
* Migrate to Swift 5.0.
* Reactions: Update quick reactions (#2459).
Bug fix:
* Device Verification: Fix user display name and device id colors in dark theme

View file

@ -76,13 +76,8 @@
32891D75226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32891D73226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift */; };
32891D76226728EF00C82226 /* DeviceVerificationDataLoadingViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 32891D74226728EE00C82226 /* DeviceVerificationDataLoadingViewController.storyboard */; };
32B1FEDB21A46F2C00637127 /* TermsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32B1FEDA21A46F2C00637127 /* TermsView.xib */; };
32B94DF8228EC26400716A26 /* ReactionsMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF1228EC26400716A26 /* ReactionsMenuView.swift */; };
32B94DF9228EC26400716A26 /* ReactionsMenuViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF2228EC26400716A26 /* ReactionsMenuViewAction.swift */; };
32B94DFA228EC26400716A26 /* ReactionsMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF3228EC26400716A26 /* ReactionsMenuButton.swift */; };
32B94DFB228EC26400716A26 /* ReactionsMenuViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF4228EC26400716A26 /* ReactionsMenuViewModelType.swift */; };
32B94DFC228EC26400716A26 /* ReactionsMenuViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF5228EC26400716A26 /* ReactionsMenuViewModel.swift */; };
32B94DFD228EC26400716A26 /* ReactionsMenuReaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF6228EC26400716A26 /* ReactionsMenuReaction.swift */; };
32B94DFE228EC26400716A26 /* ReactionsMenuView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32B94DF7228EC26400716A26 /* ReactionsMenuView.xib */; };
32BF994F21FA29A400698084 /* SettingsKeyBackupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF994E21FA29A400698084 /* SettingsKeyBackupViewModel.swift */; };
32BF995121FA29DC00698084 /* SettingsKeyBackupViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995021FA29DC00698084 /* SettingsKeyBackupViewModelType.swift */; };
32BF995321FA2A1300698084 /* SettingsKeyBackupViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995221FA2A1300698084 /* SettingsKeyBackupViewState.swift */; };
@ -460,6 +455,12 @@
B1CA3A2921EF692B000D1D89 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA3A2821EF692B000D1D89 /* UIView.swift */; };
B1CE9EFD22148703000FAE6A /* SignOutAlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CE9EFC22148703000FAE6A /* SignOutAlertPresenter.swift */; };
B1CE9F062216FB09000FAE6A /* EncryptionKeysExportPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CE9F052216FB09000FAE6A /* EncryptionKeysExportPresenter.swift */; };
B1D1BDA622BBAFB500831367 /* ReactionsMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D1BDA522BBAFB500831367 /* ReactionsMenuView.swift */; };
B1D1BDA822BBAFC900831367 /* ReactionsMenuView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1D1BDA722BBAFC900831367 /* ReactionsMenuView.xib */; };
B1D211E222BD193C00D939BD /* ReactionsMenuViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D211E122BD193C00D939BD /* ReactionsMenuViewModel.swift */; };
B1D211E422C18E3800D939BD /* ReactionsMenuViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D211E322C18E3800D939BD /* ReactionsMenuViewModelType.swift */; };
B1D211E622C194A200D939BD /* ReactionsMenuViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D211E522C194A200D939BD /* ReactionsMenuViewState.swift */; };
B1D211E822C195B400D939BD /* ReactionMenuItemViewData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D211E722C195B400D939BD /* ReactionMenuItemViewData.swift */; };
B1D250D82118AA0A000F4E93 /* RoomPredecessorBubbleCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B1D250D72118AA0A000F4E93 /* RoomPredecessorBubbleCell.m */; };
B1D4752721EE4E630067973F /* KeyboardAvoider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D4752521EE4E620067973F /* KeyboardAvoider.swift */; };
B1D4752821EE4E630067973F /* KeyboardNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D4752621EE4E620067973F /* KeyboardNotification.swift */; };
@ -607,13 +608,8 @@
32891D73226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationDataLoadingViewController.swift; sourceTree = "<group>"; };
32891D74226728EE00C82226 /* DeviceVerificationDataLoadingViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = DeviceVerificationDataLoadingViewController.storyboard; sourceTree = "<group>"; };
32B1FEDA21A46F2C00637127 /* TermsView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TermsView.xib; sourceTree = "<group>"; };
32B94DF1228EC26400716A26 /* ReactionsMenuView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuView.swift; sourceTree = "<group>"; };
32B94DF2228EC26400716A26 /* ReactionsMenuViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewAction.swift; sourceTree = "<group>"; };
32B94DF3228EC26400716A26 /* ReactionsMenuButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuButton.swift; sourceTree = "<group>"; };
32B94DF4228EC26400716A26 /* ReactionsMenuViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewModelType.swift; sourceTree = "<group>"; };
32B94DF5228EC26400716A26 /* ReactionsMenuViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewModel.swift; sourceTree = "<group>"; };
32B94DF6228EC26400716A26 /* ReactionsMenuReaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuReaction.swift; sourceTree = "<group>"; };
32B94DF7228EC26400716A26 /* ReactionsMenuView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ReactionsMenuView.xib; sourceTree = "<group>"; };
32BDC9A1211C2C870064AF51 /* zh_Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_Hant; path = zh_Hant.lproj/InfoPlist.strings; sourceTree = "<group>"; };
32BDC9A2211C2C870064AF51 /* zh_Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_Hant; path = zh_Hant.lproj/Localizable.strings; sourceTree = "<group>"; };
32BDC9A3211C2C870064AF51 /* zh_Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_Hant; path = zh_Hant.lproj/Vector.strings; sourceTree = "<group>"; };
@ -1197,6 +1193,12 @@
B1CA3A2821EF692B000D1D89 /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = "<group>"; };
B1CE9EFC22148703000FAE6A /* SignOutAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignOutAlertPresenter.swift; sourceTree = "<group>"; };
B1CE9F052216FB09000FAE6A /* EncryptionKeysExportPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncryptionKeysExportPresenter.swift; sourceTree = "<group>"; };
B1D1BDA522BBAFB500831367 /* ReactionsMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuView.swift; sourceTree = "<group>"; };
B1D1BDA722BBAFC900831367 /* ReactionsMenuView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ReactionsMenuView.xib; sourceTree = "<group>"; };
B1D211E122BD193C00D939BD /* ReactionsMenuViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewModel.swift; sourceTree = "<group>"; };
B1D211E322C18E3800D939BD /* ReactionsMenuViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewModelType.swift; sourceTree = "<group>"; };
B1D211E522C194A200D939BD /* ReactionsMenuViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewState.swift; sourceTree = "<group>"; };
B1D211E722C195B400D939BD /* ReactionMenuItemViewData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionMenuItemViewData.swift; sourceTree = "<group>"; };
B1D250D62118AA0A000F4E93 /* RoomPredecessorBubbleCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RoomPredecessorBubbleCell.h; sourceTree = "<group>"; };
B1D250D72118AA0A000F4E93 /* RoomPredecessorBubbleCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RoomPredecessorBubbleCell.m; sourceTree = "<group>"; };
B1D4752521EE4E620067973F /* KeyboardAvoider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardAvoider.swift; sourceTree = "<group>"; };
@ -1510,13 +1512,14 @@
32B94DF0228EC26400716A26 /* ReactionsMenu */ = {
isa = PBXGroup;
children = (
32B94DF1228EC26400716A26 /* ReactionsMenuView.swift */,
B1D211E322C18E3800D939BD /* ReactionsMenuViewModelType.swift */,
B1D211E122BD193C00D939BD /* ReactionsMenuViewModel.swift */,
B1D211E722C195B400D939BD /* ReactionMenuItemViewData.swift */,
B1D1BDA522BBAFB500831367 /* ReactionsMenuView.swift */,
B1D1BDA722BBAFC900831367 /* ReactionsMenuView.xib */,
B1D211E522C194A200D939BD /* ReactionsMenuViewState.swift */,
32B94DF2228EC26400716A26 /* ReactionsMenuViewAction.swift */,
32B94DF3228EC26400716A26 /* ReactionsMenuButton.swift */,
32B94DF4228EC26400716A26 /* ReactionsMenuViewModelType.swift */,
32B94DF5228EC26400716A26 /* ReactionsMenuViewModel.swift */,
32B94DF6228EC26400716A26 /* ReactionsMenuReaction.swift */,
32B94DF7228EC26400716A26 /* ReactionsMenuView.xib */,
);
path = ReactionsMenu;
sourceTree = "<group>";
@ -3578,7 +3581,6 @@
B1107ECA2200B09F0038014B /* KeyBackupRecoverSuccessViewController.storyboard in Resources */,
B1B5579C20EF575B00210D55 /* ForgotPasswordInputsView.xib in Resources */,
F083BE011E7009ED00A9B29C /* third_party_licenses.html in Resources */,
32B94DFE228EC26400716A26 /* ReactionsMenuView.xib in Resources */,
B1098BFC21ECFE65000DDA48 /* PasswordStrengthView.xib in Resources */,
B1B5573720EE6C4D00210D55 /* GroupParticipantsViewController.xib in Resources */,
B110872421F098F0003554A5 /* ActivityIndicatorView.xib in Resources */,
@ -3601,6 +3603,7 @@
3232AB1422564D9100AD6A5C /* swiftgen-config.yml in Resources */,
324A204F225FC571004FE8B0 /* DeviceVerificationIncomingViewController.storyboard in Resources */,
3232AB4B2256558300AD6A5C /* TemplateScreenViewController.storyboard in Resources */,
B1D1BDA822BBAFC900831367 /* ReactionsMenuView.xib in Resources */,
32B1FEDB21A46F2C00637127 /* TermsView.xib in Resources */,
B1B5578E20EF568D00210D55 /* GroupInviteTableViewCell.xib in Resources */,
B1B5582020EF625800210D55 /* SimpleRoomTitleView.xib in Resources */,
@ -3906,6 +3909,7 @@
B1B558C320EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.m in Sources */,
B110872521F098F0003554A5 /* ActivityIndicatorPresenter.swift in Sources */,
32242F1521E8FBA900725742 /* DarkTheme.swift in Sources */,
B1D211E222BD193C00D939BD /* ReactionsMenuViewModel.swift in Sources */,
B140B4A621F89E7600E3F5FE /* KeyBackupSetupCoordinatorBridgePresenter.swift in Sources */,
B1B5577420EE702900210D55 /* WidgetViewController.m in Sources */,
B139C21B21FE5B9200BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift in Sources */,
@ -3942,7 +3946,6 @@
B1B5594520EF7BD000210D55 /* TableViewCellWithCollectionView.m in Sources */,
32891D75226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift in Sources */,
32891D712264DF7B00C82226 /* DeviceVerificationVerifiedViewController.swift in Sources */,
32B94DFB228EC26400716A26 /* ReactionsMenuViewModelType.swift in Sources */,
F083BDEF1E7009ED00A9B29C /* UINavigationController+Riot.m in Sources */,
B1B5581F20EF625800210D55 /* SimpleRoomTitleView.m in Sources */,
B1C562E2228C7C8D0037F12A /* RoomContextualMenuViewController.swift in Sources */,
@ -3959,6 +3962,7 @@
3232ABA7225730E100AD6A5C /* DeviceVerificationStartCoordinator.swift in Sources */,
B1D4752721EE4E630067973F /* KeyboardAvoider.swift in Sources */,
B1D4752821EE4E630067973F /* KeyboardNotification.swift in Sources */,
B1D1BDA622BBAFB500831367 /* ReactionsMenuView.swift in Sources */,
B1B5573C20EE6C4D00210D55 /* MasterTabBarController.m in Sources */,
32F6B96E2270623100BBA352 /* DeviceVerificationDataLoadingViewModelType.swift in Sources */,
B1B5592C20EF7A5D00210D55 /* TableViewCellWithButton.m in Sources */,
@ -3998,14 +4002,12 @@
3232ABAB225730E100AD6A5C /* DeviceVerificationCoordinator.swift in Sources */,
B1B5583E20EF6E7F00210D55 /* GroupRoomTableViewCell.m in Sources */,
B14F143522144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift in Sources */,
32B94DF8228EC26400716A26 /* ReactionsMenuView.swift in Sources */,
B1B5574F20EE6C4D00210D55 /* RoomsViewController.m in Sources */,
B1B5572520EE6C4D00210D55 /* RoomMessagesSearchViewController.m in Sources */,
B139C22121FE5D9D00BB68EC /* KeyBackupRecoverFromPassphraseViewState.swift in Sources */,
B1B5579120EF568D00210D55 /* GroupInviteTableViewCell.m in Sources */,
B1B5579A20EF575B00210D55 /* ForgotPasswordInputsView.m in Sources */,
B1B12B2922942315002CB419 /* UITouch.swift in Sources */,
32B94DFD228EC26400716A26 /* ReactionsMenuReaction.swift in Sources */,
B1B558CC20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.m in Sources */,
B1B5571D20EE6C4D00210D55 /* HomeViewController.m in Sources */,
3232ABA6225730E100AD6A5C /* DeviceVerificationStartViewController.swift in Sources */,
@ -4063,6 +4065,7 @@
B140B4A221F87F7100E3F5FE /* OperationQueue.swift in Sources */,
B1B5575120EE6C4D00210D55 /* AuthenticationViewController.m in Sources */,
B1B5571820EE6C4D00210D55 /* CountryPickerViewController.m in Sources */,
B1D211E622C194A200D939BD /* ReactionsMenuViewState.swift in Sources */,
B17982FF2119FED2001FD722 /* GDPRConsentViewController.swift in Sources */,
B1098BE121ECE09F000DDA48 /* Images.swift in Sources */,
3232ABA4225730E100AD6A5C /* DeviceVerificationStartViewAction.swift in Sources */,
@ -4120,6 +4123,7 @@
B1B5599420EFC5E400210D55 /* DecryptionFailureTracker.m in Sources */,
F083BDF01E7009ED00A9B29C /* UIViewController+RiotSearch.m in Sources */,
F083BDF91E7009ED00A9B29C /* RoomEmailInvitation.m in Sources */,
B1D211E422C18E3800D939BD /* ReactionsMenuViewModelType.swift in Sources */,
324A2055225FC571004FE8B0 /* DeviceVerificationIncomingViewModelType.swift in Sources */,
B1B5572C20EE6C4D00210D55 /* RoomParticipantsViewController.m in Sources */,
B1B558EE20EF768F00210D55 /* RoomOutgoingAttachmentBubbleCell.m in Sources */,
@ -4127,12 +4131,12 @@
32BF994F21FA29A400698084 /* SettingsKeyBackupViewModel.swift in Sources */,
B1B5574920EE6C4D00210D55 /* RiotSplitViewController.m in Sources */,
B1B5574E20EE6C4D00210D55 /* DirectoryServerPickerViewController.m in Sources */,
B1D211E822C195B400D939BD /* ReactionMenuItemViewData.swift in Sources */,
B1DB4F0B223131600065DBFA /* String.swift in Sources */,
3232AB522256558300AD6A5C /* TemplateScreenViewModel.swift in Sources */,
B1B5575B20EE6C4D00210D55 /* HomeFilesSearchViewController.m in Sources */,
B139C22521FF01C100BB68EC /* KeyBackupRecoverFromPassphraseCoordinator.swift in Sources */,
B1098BFD21ECFE65000DDA48 /* PasswordStrengthManager.swift in Sources */,
32B94DFC228EC26400716A26 /* ReactionsMenuViewModel.swift in Sources */,
B1B558F520EF768F00210D55 /* RoomOutgoingTextMsgWithPaginationTitleBubbleCell.m in Sources */,
3232AB482256558300AD6A5C /* FlowTemplateCoordinatorType.swift in Sources */,
B1B558F820EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */,

View file

@ -16,9 +16,7 @@
import Foundation
enum ReactionsMenuReaction: String {
case agree = "👍"
case disagree = "👎"
case like = "🙂"
case dislike = "😔"
struct ReactionMenuItemViewData {
let emoji: String
let isSelected: Bool
}

View file

@ -18,7 +18,14 @@ import UIKit
class ReactionsMenuButton: UIButton, Themable {
// MARK: Private
// MARK: - Constants
private enum Constants {
static let borderWidthSelected: CGFloat = 1/UIScreen.main.scale
static let borderColorAlpha: CGFloat = 0.15
}
// MARK: - Properties
private var theme: Theme!
@ -38,8 +45,8 @@ class ReactionsMenuButton: UIButton, Themable {
override func layoutSubviews() {
super.layoutSubviews()
self.layer.cornerRadius = self.frame.size.height / 2
self.layer.borderWidth = self.isSelected ? 1 : 0
self.layer.cornerRadius = self.frame.size.height / 3
self.layer.borderWidth = self.isSelected ? Constants.borderWidthSelected : 0
}
// MARK: - Private
@ -60,15 +67,14 @@ class ReactionsMenuButton: UIButton, Themable {
func update(theme: Theme) {
self.theme = theme
// TODO: Color for black theme
self.setTitleColor(self.theme.textPrimaryColor, for: .normal)
self.setTitleColor(self.theme.textPrimaryColor, for: .selected)
self.layer.borderColor = self.theme.tintColor.cgColor
self.layer.borderColor = self.theme.tintColor.withAlphaComponent(Constants.borderColorAlpha).cgColor
}
private func updateView() {
backgroundColor = isSelected ? self.theme.tintBackgroundColor : self.theme.headerBackgroundColor
backgroundColor = isSelected ? self.theme.tintBackgroundColor : UIColor.clear
}
override open var isSelected: Bool {

View file

@ -17,93 +17,111 @@
import UIKit
import Reusable
final class ReactionsMenuView: UIView, NibOwnerLoadable {
final class ReactionsMenuView: UIView, Themable, NibLoadable {
// MARK: - Constants
private enum Constants {
static let selectedReactionAnimationScale: CGFloat = 1.2
}
// MARK: - Properties
// MARK: Outlets
@IBOutlet weak var agreeButton: UIButton!
@IBOutlet weak var disagreeButton: UIButton!
@IBOutlet weak var likeButton: UIButton!
@IBOutlet weak var dislikeButton: UIButton!
@IBOutlet private weak var reactionsBackgroundView: UIView!
@IBOutlet private weak var reactionsStackView: UIStackView!
// MARK: Private
private var reactionViewDatas: [ReactionMenuItemViewData] = []
private var reactionButtons: [ReactionsMenuButton] = []
private var tappedReactionButton: ReactionsMenuButton?
// MARK: Public
var viewModel: ReactionsMenuViewModelType? {
didSet {
self.updateView()
self.viewModel?.viewDelegate = self
self.viewModel?.process(viewAction: .loadData)
}
}
// MARK: - Setup
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.loadNibContent()
self.commonInit()
var reactionHasBeenTapped: Bool {
return self.tappedReactionButton != nil
}
override init(frame: CGRect) {
super.init(frame: frame)
self.loadNibContent()
self.commonInit()
// MARK: - Life cycle
override func awakeFromNib() {
super.awakeFromNib()
self.reactionsBackgroundView.layer.masksToBounds = true
self.update(theme: ThemeService.shared().theme)
}
// MARK: - Actions
override func layoutSubviews() {
super.layoutSubviews()
@IBAction private func agreeButtonAction(_ sender: Any) {
self.viewModel?.process(viewAction: .toggleReaction(.agree))
self.reactionsBackgroundView.layer.cornerRadius = self.reactionsBackgroundView.frame.size.height/2
}
@IBAction private func disagreeButtonAction(_ sender: Any) {
self.viewModel?.process(viewAction: .toggleReaction(.disagree))
// MARK: - Public
func update(theme: Theme) {
self.reactionsBackgroundView.backgroundColor = theme.headerBackgroundColor
}
@IBAction private func likeButtonAction(_ sender: Any) {
self.viewModel?.process(viewAction: .toggleReaction(.like))
func selectionAnimationInstructionPart1() {
guard let tappedButton = self.tappedReactionButton else {
return
}
let scale = Constants.selectedReactionAnimationScale
tappedButton.superview?.bringSubviewToFront(tappedButton)
tappedButton.transform = CGAffineTransform(scaleX: scale, y: scale)
}
@IBAction private func dislikeButtonAction(_ sender: Any) {
self.viewModel?.process(viewAction: .toggleReaction(.dislike))
func selectionAnimationInstructionPart2() {
guard let tappedButton = self.tappedReactionButton else {
return
}
tappedButton.transform = CGAffineTransform.identity
tappedButton.isSelected.toggle()
}
// MARK: - Private
private func commonInit() {
private func fill(reactionsMenuViewDatas: [ReactionMenuItemViewData]) {
self.reactionViewDatas = reactionsMenuViewDatas
agreeButton.setTitle(VectorL10n.roomEventActionReactionAgree(ReactionsMenuReaction.agree.rawValue), for: .normal)
agreeButton.setTitle(VectorL10n.roomEventActionReactionAgree(ReactionsMenuReaction.agree.rawValue), for: .highlighted)
disagreeButton.setTitle(VectorL10n.roomEventActionReactionDisagree(ReactionsMenuReaction.disagree.rawValue), for: .normal)
disagreeButton.setTitle(VectorL10n.roomEventActionReactionDisagree(ReactionsMenuReaction.disagree.rawValue), for: .highlighted)
likeButton.setTitle(VectorL10n.roomEventActionReactionLike(ReactionsMenuReaction.like.rawValue), for: .normal)
likeButton.setTitle(VectorL10n.roomEventActionReactionLike(ReactionsMenuReaction.like.rawValue), for: .highlighted)
dislikeButton.setTitle(VectorL10n.roomEventActionReactionDislike(ReactionsMenuReaction.dislike.rawValue), for: .normal)
dislikeButton.setTitle(VectorL10n.roomEventActionReactionDislike(ReactionsMenuReaction.dislike.rawValue), for: .highlighted)
self.reactionsStackView.vc_removeAllSubviews()
customizeViewRendering()
for reactionViewData in self.reactionViewDatas {
let reactionsMenuButton = ReactionsMenuButton()
reactionsMenuButton.setTitle(reactionViewData.emoji, for: .normal)
reactionsMenuButton.isSelected = reactionViewData.isSelected
reactionsMenuButton.addTarget(self, action: #selector(reactionButtonAction), for: .touchUpInside)
self.reactionsStackView.addArrangedSubview(reactionsMenuButton)
self.reactionButtons.append(reactionsMenuButton)
}
}
private func customizeViewRendering() {
self.backgroundColor = UIColor.clear
}
private func updateView() {
guard let viewModel = self.viewModel else {
@objc private func reactionButtonAction(_ sender: ReactionsMenuButton) {
guard let tappedReaction = sender.titleLabel?.text else {
return
}
agreeButton.isSelected = viewModel.isAgreeButtonSelected
disagreeButton.isSelected = viewModel.isDisagreeButtonSelected
likeButton.isSelected = viewModel.isLikeButtonSelected
dislikeButton.isSelected = viewModel.isDislikeButtonSelected
self.tappedReactionButton = sender
self.viewModel?.process(viewAction: .tap(reaction: tappedReaction))
}
}
extension ReactionsMenuView: ReactionsMenuViewModelDelegate {
func reactionsMenuViewModelDidUpdate(_ viewModel: ReactionsMenuViewModelType) {
self.updateView()
// MARK: - ReactionsMenuViewModelViewDelegate
extension ReactionsMenuView: ReactionsMenuViewModelViewDelegate {
func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModel, didUpdateViewState viewState: ReactionsMenuViewState) {
switch viewState {
case .loaded(reactionsViewData: let reactionsViewData):
self.fill(reactionsMenuViewDatas: reactionsViewData)
}
}
}

View file

@ -1,113 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="ReactionsMenuView" customModule="Riot" customModuleProvider="target">
<connections>
<outlet property="agreeButton" destination="30O-28-rwy" id="6aH-Wm-9y3"/>
<outlet property="disagreeButton" destination="Ebe-PT-0R2" id="7FN-DF-Pxg"/>
<outlet property="dislikeButton" destination="AgH-2U-HpP" id="rMC-aV-G1t"/>
<outlet property="likeButton" destination="kao-MQ-QFq" id="SVi-dI-qWZ"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="414" height="100"/>
<view contentMode="scaleToFill" id="7Xq-Wy-z0M" customClass="ReactionsMenuView" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="459" height="59"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="30O-28-rwy" customClass="ReactionsMenuButton" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="104" y="13" width="100" height="38"/>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="38" id="TII-Yn-bza"/>
<constraint firstAttribute="width" constant="100" id="cjU-F5-DjB"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<state key="normal" title="Agree 👍">
<color key="titleColor" cocoaTouchSystemColor="darkTextColor"/>
</state>
<connections>
<action selector="agreeButtonAction:" destination="-1" eventType="touchUpInside" id="uLG-m0-aTt"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ebe-PT-0R2" customClass="ReactionsMenuButton" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="104" y="59" width="100" height="38"/>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eXy-Kc-Ck7">
<rect key="frame" x="0.0" y="0.0" width="459" height="59"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacing="2" translatesAutoresizingMaskIntoConstraints="NO" id="uzd-VB-mKT">
<rect key="frame" x="5" y="5" width="449" height="49"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7a6-zJ-2tf" customClass="ReactionsMenuButton" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="54.5" height="49"/>
<color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="width" constant="100" id="807-lM-sNX"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="38" id="bIY-hX-5PC"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="100" id="j4m-Qi-NS5"/>
<constraint firstAttribute="height" constant="38" id="tNZ-Kv-LxS"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<state key="normal" title="Disagree 👎">
<fontDescription key="fontDescription" type="system" pointSize="24"/>
<state key="normal" title="👍">
<color key="titleColor" cocoaTouchSystemColor="darkTextColor"/>
</state>
<connections>
<action selector="disagreeButtonAction:" destination="-1" eventType="touchUpInside" id="8Ir-5R-jpn"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kao-MQ-QFq" customClass="ReactionsMenuButton" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="210" y="13" width="100" height="38"/>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mHT-t1-k2f" customClass="ReactionsMenuButton" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="56.5" y="0.0" width="54.5" height="49"/>
<color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="width" constant="100" id="8hf-4f-egV"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="100" id="Gvk-Ap-Wtc"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="38" id="lZK-go-x5A"/>
<constraint firstAttribute="height" constant="38" id="o0a-8j-0et"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<state key="normal" title="Like 🙂">
<fontDescription key="fontDescription" type="system" pointSize="24"/>
<state key="normal" title="👎">
<color key="titleColor" cocoaTouchSystemColor="darkTextColor"/>
</state>
<connections>
<action selector="likeButtonAction:" destination="-1" eventType="touchUpInside" id="3Bb-iJ-Q8R"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="AgH-2U-HpP" customClass="ReactionsMenuButton" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="210" y="59" width="100" height="38"/>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="InP-XP-sh8" customClass="ReactionsMenuButton" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="113" y="0.0" width="54" height="49"/>
<color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="100" id="75c-dq-L2I"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="100" id="GgC-VF-je2"/>
<constraint firstAttribute="width" constant="100" id="RCe-5D-dg5"/>
<constraint firstAttribute="height" constant="38" id="cvn-yC-3VY"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="38" id="hVj-hk-Vob"/>
<constraint firstAttribute="height" relation="lessThanOrEqual" constant="38" id="wNs-Eg-frx"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<state key="normal" title="Dislike 😔">
<fontDescription key="fontDescription" type="system" pointSize="24"/>
<state key="normal" title="🙂">
<color key="titleColor" cocoaTouchSystemColor="darkTextColor"/>
</state>
<connections>
<action selector="dislikeButtonAction:" destination="-1" eventType="touchUpInside" id="vE5-Jn-5WM"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="sbZ-tR-mCf" customClass="ReactionsMenuButton" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="169" y="0.0" width="54.5" height="49"/>
<color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" type="system" pointSize="24"/>
<state key="normal" title="🎉">
<color key="titleColor" cocoaTouchSystemColor="darkTextColor"/>
</state>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4ze-DN-PCw" customClass="ReactionsMenuButton" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="225.5" y="0.0" width="54.5" height="49"/>
<color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" type="system" pointSize="24"/>
<state key="normal" title="🙁">
<color key="titleColor" cocoaTouchSystemColor="darkTextColor"/>
</state>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="KFI-G0-oyg" customClass="ReactionsMenuButton" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="282" y="0.0" width="54.5" height="49"/>
<color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" type="system" pointSize="24"/>
<state key="normal" title="❤️">
<color key="titleColor" cocoaTouchSystemColor="darkTextColor"/>
</state>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ren-Yo-15Q" customClass="ReactionsMenuButton" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="338.5" y="0.0" width="54" height="49"/>
<color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" type="system" pointSize="24"/>
<state key="normal" title="🚀">
<color key="titleColor" cocoaTouchSystemColor="darkTextColor"/>
</state>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="M0A-Tx-Jz9" customClass="ReactionsMenuButton" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="394.5" y="0.0" width="54.5" height="49"/>
<color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" type="system" pointSize="24"/>
<state key="normal" title="👀">
<color key="titleColor" cocoaTouchSystemColor="darkTextColor"/>
</state>
</button>
</subviews>
</stackView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="Ebe-PT-0R2" firstAttribute="trailing" secondItem="vUN-kp-3ea" secondAttribute="centerX" constant="-3" id="3kX-GA-4mf"/>
<constraint firstItem="vUN-kp-3ea" firstAttribute="centerX" secondItem="kao-MQ-QFq" secondAttribute="leading" constant="-3" id="FV3-zi-y6i"/>
<constraint firstItem="vUN-kp-3ea" firstAttribute="centerY" secondItem="Ebe-PT-0R2" secondAttribute="top" constant="-4" id="Hfb-BB-kgH"/>
<constraint firstItem="AgH-2U-HpP" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="centerX" constant="3" id="LBW-Lg-fZv"/>
<constraint firstItem="vUN-kp-3ea" firstAttribute="centerY" secondItem="AgH-2U-HpP" secondAttribute="top" constant="-4" id="MAE-KT-eAQ"/>
<constraint firstItem="vUN-kp-3ea" firstAttribute="centerY" secondItem="30O-28-rwy" secondAttribute="bottom" constant="4" id="jTW-iy-hDv"/>
<constraint firstItem="vUN-kp-3ea" firstAttribute="centerY" secondItem="kao-MQ-QFq" secondAttribute="bottom" constant="4" id="sRb-2V-clB"/>
<constraint firstItem="30O-28-rwy" firstAttribute="trailing" secondItem="vUN-kp-3ea" secondAttribute="centerX" constant="-3" id="xUt-vJ-1I1" userLabel="Agree 👍.trailing = Safe Area.centerX + 7"/>
<constraint firstAttribute="trailing" secondItem="uzd-VB-mKT" secondAttribute="trailing" constant="5" id="fDZ-NX-EyG"/>
<constraint firstItem="uzd-VB-mKT" firstAttribute="leading" secondItem="eXy-Kc-Ck7" secondAttribute="leading" constant="5" id="iCa-Ob-e7J"/>
<constraint firstItem="uzd-VB-mKT" firstAttribute="top" secondItem="eXy-Kc-Ck7" secondAttribute="top" constant="5" id="msb-Ay-Bp6"/>
<constraint firstAttribute="bottom" secondItem="uzd-VB-mKT" secondAttribute="bottom" constant="5" id="nJE-Gm-Rf1"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="eXy-Kc-Ck7" firstAttribute="top" secondItem="7Xq-Wy-z0M" secondAttribute="top" id="43J-YM-JHg"/>
<constraint firstItem="eXy-Kc-Ck7" firstAttribute="leading" secondItem="7Xq-Wy-z0M" secondAttribute="leading" id="9dy-sK-ygm"/>
<constraint firstAttribute="trailing" secondItem="eXy-Kc-Ck7" secondAttribute="trailing" id="E4Z-bt-BRw"/>
<constraint firstAttribute="bottom" secondItem="eXy-Kc-Ck7" secondAttribute="bottom" id="Nb2-0z-IiS"/>
</constraints>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
<point key="canvasLocation" x="128.98550724637681" y="35.491071428571423"/>
<connections>
<outlet property="reactionsBackgroundView" destination="eXy-Kc-Ck7" id="VYi-YD-mb9"/>
<outlet property="reactionsStackView" destination="uzd-VB-mKT" id="DTV-Nh-bcm"/>
</connections>
<point key="canvasLocation" x="444.20289855072468" y="-718.19196428571422"/>
</view>
</objects>
</document>

View file

@ -18,5 +18,6 @@ import UIKit
/// Action chosen by the user
enum ReactionsMenuViewAction {
case toggleReaction(ReactionsMenuReaction)
case loadData
case tap(reaction: String)
}

View file

@ -14,158 +14,71 @@
limitations under the License.
*/
import UIKit
import Foundation
@objc final class ReactionsMenuViewModel: NSObject, ReactionsMenuViewModelType {
// MARK: - Properties
private let reactions = ["👍", "👎", "😄", "🎉", "😕", "❤️", "🚀", "👀"]
private var currentViewDatas: [ReactionMenuItemViewData] = []
// MARK: Private
private let aggregations: MXAggregations
private let roomId: String
private let aggregatedReactions: MXAggregatedReactions?
private let reactionsViewData: [ReactionMenuItemViewData] = []
private let eventId: String
// MARK: Public
private(set) var isAgreeButtonSelected: Bool = false
private(set) var isDisagreeButtonSelected: Bool = false
private(set) var isLikeButtonSelected: Bool = false
private(set) var isDislikeButtonSelected: Bool = false
weak var viewDelegate: ReactionsMenuViewModelDelegate?
@objc weak var coordinatorDelegate: ReactionsMenuViewModelCoordinatorDelegate?
weak var viewDelegate: ReactionsMenuViewModelViewDelegate?
// MARK: - Setup
@objc init(aggregations: MXAggregations, roomId: String, eventId: String) {
self.aggregations = aggregations
self.roomId = roomId
@objc init(aggregatedReactions: MXAggregatedReactions?,
eventId: String) {
self.aggregatedReactions = aggregatedReactions
self.eventId = eventId
super.init()
self.loadData()
self.listenToDataUpdate()
}
// MARK: - Public
func process(viewAction: ReactionsMenuViewAction) {
var reaction: ReactionsMenuReaction?
var newState: Bool?
switch viewAction {
case .toggleReaction(let menuReaction):
reaction = menuReaction
switch menuReaction {
case .agree:
newState = !self.isAgreeButtonSelected
case .disagree:
newState = !self.isDisagreeButtonSelected
case .like:
newState = !self.isLikeButtonSelected
case .dislike:
newState = !self.isDislikeButtonSelected
case .loadData:
self.loadData()
case .tap(let reaction):
if let viewData = self.currentViewDatas.first(where: { $0.emoji == reaction }) {
if viewData.isSelected {
self.coordinatorDelegate?.reactionsMenuViewModel(self, didRemoveReaction: reaction, forEventId: self.eventId)
} else {
self.coordinatorDelegate?.reactionsMenuViewModel(self, didAddReaction: reaction, forEventId: self.eventId)
}
}
guard let theReaction = reaction, let theNewState = newState else {
return
}
self.react(withReaction: theReaction, selected: theNewState)
}
// MARK: - Private
private func resetData() {
self.isAgreeButtonSelected = false
self.isDisagreeButtonSelected = false
self.isLikeButtonSelected = false
self.isDislikeButtonSelected = false
}
private func loadData() {
guard let reactionCounts = self.aggregations.aggregatedReactions(onEvent: self.eventId, inRoom: self.roomId)?.withNonZeroCount()?.reactions else {
return
}
let reactionCounts = self.aggregatedReactions?.withNonZeroCount()?.reactions ?? []
var quickReactionsWithUserReactedFlag: [String: Bool] = Dictionary(uniqueKeysWithValues: self.reactions.map { ($0, false) })
self.resetData()
reactionCounts.forEach { (reactionCount) in
if reactionCount.myUserHasReacted {
if let reaction = ReactionsMenuReaction(rawValue: reactionCount.reaction) {
switch reaction {
case .agree:
self.isAgreeButtonSelected = true
case .disagree:
self.isDisagreeButtonSelected = true
case .like:
self.isLikeButtonSelected = true
case .dislike:
self.isDislikeButtonSelected = true
}
}
if let hasUserReacted = quickReactionsWithUserReactedFlag[reactionCount.reaction], hasUserReacted == false {
quickReactionsWithUserReactedFlag[reactionCount.reaction] = reactionCount.myUserHasReacted
}
}
self.viewDelegate?.reactionsMenuViewModelDidUpdate(self)
let reactionMenuItemViewDatas: [ReactionMenuItemViewData] = self.reactions.map { reaction -> ReactionMenuItemViewData in
let isSelected = quickReactionsWithUserReactedFlag[reaction] ?? false
return ReactionMenuItemViewData(emoji: reaction, isSelected: isSelected)
}
private func listenToDataUpdate() {
self.aggregations.listenToReactionCountUpdate(inRoom: self.roomId) { [weak self] (changes) in
self.currentViewDatas = reactionMenuItemViewDatas
guard let sself = self else {
return
}
if changes[sself.eventId] != nil {
sself.loadData()
}
}
}
private func react(withReaction reaction: ReactionsMenuReaction, selected: Bool) {
// If required, unreact first
if selected {
self.ensure3StateButtons(withReaction: reaction)
}
let reactionString = reaction.rawValue
if selected {
self.coordinatorDelegate?.reactionsMenuViewModel(self, didAddReaction: reactionString, forEventId: self.eventId)
} else {
self.coordinatorDelegate?.reactionsMenuViewModel(self, didRemoveReaction: reactionString, forEventId: self.eventId)
}
}
// We can like, dislike, be indifferent but we cannot like & dislike at the same time
private func ensure3StateButtons(withReaction reaction: ReactionsMenuReaction) {
var unreaction: ReactionsMenuReaction?
switch reaction {
case .agree:
if isDisagreeButtonSelected {
unreaction = .disagree
}
case .disagree:
if isAgreeButtonSelected {
unreaction = .agree
}
case .like:
if isDislikeButtonSelected {
unreaction = .dislike
}
case .dislike:
if isLikeButtonSelected {
unreaction = .like
}
}
if let unreaction = unreaction {
self.react(withReaction: unreaction, selected: false)
}
self.viewDelegate?.reactionsMenuViewModel(self, didUpdateViewState: ReactionsMenuViewState.loaded(reactionsViewData: reactionMenuItemViewDatas))
}
}

View file

@ -16,8 +16,8 @@
import Foundation
protocol ReactionsMenuViewModelDelegate: class {
func reactionsMenuViewModelDidUpdate(_ viewModel: ReactionsMenuViewModelType)
protocol ReactionsMenuViewModelViewDelegate: class {
func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModel, didUpdateViewState viewState: ReactionsMenuViewState)
}
@objc protocol ReactionsMenuViewModelCoordinatorDelegate: class {
@ -25,16 +25,10 @@ protocol ReactionsMenuViewModelDelegate: class {
func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModel, didRemoveReaction reaction: String, forEventId eventId: String)
}
protocol ReactionsMenuViewModelType {
var isAgreeButtonSelected: Bool { get }
var isDisagreeButtonSelected: Bool { get }
var isLikeButtonSelected: Bool { get }
var isDislikeButtonSelected: Bool { get }
var viewDelegate: ReactionsMenuViewModelDelegate? { get set }
var coordinatorDelegate: ReactionsMenuViewModelCoordinatorDelegate? { get set }
var viewDelegate: ReactionsMenuViewModelViewDelegate? { get set }
func process(viewAction: ReactionsMenuViewAction)
}

View file

@ -0,0 +1,22 @@
/*
Copyright 2019 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
/// ReactionsMenuView view state
enum ReactionsMenuViewState {
case loaded(reactionsViewData: [ReactionMenuItemViewData])
}

View file

@ -22,7 +22,7 @@ final class RoomContextualMenuPresenter: NSObject {
// MARK: - Constants
private enum Constants {
static let animationDuration: TimeInterval = 0.3
static let animationDuration: TimeInterval = 0.2
}
// MARK: - Properties
@ -42,24 +42,28 @@ final class RoomContextualMenuPresenter: NSObject {
func present(roomContextualMenuViewController: RoomContextualMenuViewController,
from viewController: UIViewController,
on view: UIView,
contentToReactFrame: CGRect, // Not nullable for compatibility with Obj-C
animated: Bool,
completion: (() -> Void)?) {
guard self.roomContextualMenuViewController == nil else {
return
}
roomContextualMenuViewController.view.alpha = 0
viewController.vc_addChildViewController(viewController: roomContextualMenuViewController, onView: view)
self.roomContextualMenuViewController = roomContextualMenuViewController
roomContextualMenuViewController.contentToReactFrame = contentToReactFrame
roomContextualMenuViewController.hideMenuToolbar()
roomContextualMenuViewController.prepareReactionsMenuAnimations()
roomContextualMenuViewController.hideReactionsMenu()
roomContextualMenuViewController.view.layoutIfNeeded()
let animationInstructions: (() -> Void) = {
roomContextualMenuViewController.showMenuToolbar()
roomContextualMenuViewController.view.alpha = 1
roomContextualMenuViewController.showReactionsMenu()
roomContextualMenuViewController.view.layoutIfNeeded()
}
@ -77,37 +81,43 @@ final class RoomContextualMenuPresenter: NSObject {
func hideContextualMenu(animated: Bool, completion: (() -> Void)?) {
guard let roomContextualMenuViewController = self.roomContextualMenuViewController else {
completion?()
return
}
let animationInstructions: (() -> Void) = {
roomContextualMenuViewController.hideMenuToolbar()
roomContextualMenuViewController.view.alpha = 0
roomContextualMenuViewController.hideReactionsMenu()
roomContextualMenuViewController.view.layoutIfNeeded()
}
let animationCompletionInstructions: (() -> Void) = {
roomContextualMenuViewController.vc_removeFromParent()
// TODO: To remove once the retain cycle caused by reactionsMenuViewModel is fixed
self.roomContextualMenuViewController = nil
completion?()
}
if animated {
if roomContextualMenuViewController.shouldPerformTappedReactionAnimation {
UIView.animate(withDuration: 0.15, animations: {
roomContextualMenuViewController.selectedReactionAnimationsIntructionsPart1()
}, completion: { _ in
UIView.animate(withDuration: Constants.animationDuration, animations: {
roomContextualMenuViewController.selectedReactionAnimationsIntructionsPart2()
animationInstructions()
}, completion: { completed in
animationCompletionInstructions()
})
})
} else {
UIView.animate(withDuration: Constants.animationDuration, animations: {
animationInstructions()
}, completion: { completed in
animationCompletionInstructions()
})
}
} else {
animationInstructions()
animationCompletionInstructions()
}
}
func showReactionsMenu(reactionsMenuViewModel: ReactionsMenuViewModel, aroundFrame frame: CGRect) {
self.roomContextualMenuViewController?.showReactionsMenu(withViewModel: reactionsMenuViewModel, aroundFrame: frame)
}
}

View file

@ -21,19 +21,20 @@
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Szx-Dr-Ndt">
<rect key="frame" x="0.0" y="0.0" width="414" height="793"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Vdy-rp-3g9" customClass="ReactionsMenuView" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="0.0" y="100" width="414" height="100"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Vdy-rp-3g9">
<rect key="frame" x="10" y="150" width="394" height="50"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="100" id="D3i-aE-kdL"/>
<constraint firstAttribute="height" constant="50" id="D3i-aE-kdL"/>
<constraint firstAttribute="width" relation="lessThanOrEqual" priority="999" constant="400" id="Kdn-SI-BvA"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="Vdy-rp-3g9" firstAttribute="bottom" secondItem="Szx-Dr-Ndt" secondAttribute="top" constant="200" id="0GH-Qk-vKA"/>
<constraint firstAttribute="trailing" secondItem="Vdy-rp-3g9" secondAttribute="trailing" id="9Gu-dr-IPv"/>
<constraint firstItem="Vdy-rp-3g9" firstAttribute="leading" secondItem="Szx-Dr-Ndt" secondAttribute="leading" id="h78-sP-xDx"/>
<constraint firstItem="Vdy-rp-3g9" firstAttribute="width" secondItem="Szx-Dr-Ndt" secondAttribute="width" priority="750" id="Io5-6N-mvK"/>
<constraint firstItem="Vdy-rp-3g9" firstAttribute="centerX" secondItem="Szx-Dr-Ndt" secondAttribute="centerX" id="epY-Ub-0d5"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0GC-JU-rI3" customClass="RoomContextualMenuToolbarView" customModule="Riot" customModuleProvider="target">
@ -49,8 +50,10 @@
<constraint firstAttribute="trailing" secondItem="Szx-Dr-Ndt" secondAttribute="trailing" id="2eB-6O-P3h"/>
<constraint firstItem="Szx-Dr-Ndt" firstAttribute="leading" secondItem="X0o-r8-auN" secondAttribute="leading" id="4qK-G6-nr9"/>
<constraint firstItem="Szx-Dr-Ndt" firstAttribute="top" secondItem="X0o-r8-auN" secondAttribute="top" id="GVa-P9-DcG"/>
<constraint firstItem="Vdy-rp-3g9" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="225-y0-Elg" secondAttribute="leading" constant="10" id="R2Z-jH-SRN"/>
<constraint firstItem="0GC-JU-rI3" firstAttribute="leading" secondItem="X0o-r8-auN" secondAttribute="leading" id="TZJ-nm-Ppz"/>
<constraint firstItem="0GC-JU-rI3" firstAttribute="top" secondItem="Szx-Dr-Ndt" secondAttribute="bottom" id="Wyl-wh-kh4"/>
<constraint firstItem="225-y0-Elg" firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="Vdy-rp-3g9" secondAttribute="trailing" constant="10" id="j9U-2p-huj"/>
<constraint firstAttribute="trailing" secondItem="0GC-JU-rI3" secondAttribute="trailing" id="lzM-FD-x89"/>
<constraint firstItem="225-y0-Elg" firstAttribute="bottom" secondItem="0GC-JU-rI3" secondAttribute="bottom" id="s4i-80-0iu"/>
</constraints>
@ -61,7 +64,7 @@
<outlet property="menuToolbarView" destination="0GC-JU-rI3" id="j0z-I8-Pcr"/>
<outlet property="menuToolbarViewBottomConstraint" destination="s4i-80-0iu" id="E5w-5m-m5O"/>
<outlet property="menuToolbarViewHeightConstraint" destination="ynL-KP-iB4" id="Zeb-b0-Yil"/>
<outlet property="reactionsMenuView" destination="Vdy-rp-3g9" id="jJT-mz-vg6"/>
<outlet property="reactionsMenuContainerView" destination="Vdy-rp-3g9" id="hRj-C0-5VR"/>
<outlet property="reactionsMenuViewBottomConstraint" destination="0GH-Qk-vKA" id="8lg-XL-JgW"/>
<outlet property="reactionsMenuViewHeightConstraint" destination="D3i-aE-kdL" id="CCr-hW-2dv"/>
</connections>

View file

@ -18,12 +18,18 @@ import UIKit
@objc protocol RoomContextualMenuViewControllerDelegate: class {
func roomContextualMenuViewControllerDidTapBackgroundOverlay(_ viewController: RoomContextualMenuViewController)
func roomContextualMenuViewControllerDidReaction(_ viewController: RoomContextualMenuViewController)
}
@objcMembers
final class RoomContextualMenuViewController: UIViewController, Themable {
// MARK: - Constants
private enum Constants {
static let reactionsMenuViewVerticalMargin: CGFloat = 10.0
static let reactionsMenuViewHiddenScale: CGFloat = 0.97
}
// MARK: - Properties
// MARK: Outlets
@ -34,15 +40,20 @@ final class RoomContextualMenuViewController: UIViewController, Themable {
@IBOutlet private weak var menuToolbarViewHeightConstraint: NSLayoutConstraint!
@IBOutlet private weak var menuToolbarViewBottomConstraint: NSLayoutConstraint!
@IBOutlet private weak var reactionsMenuView: ReactionsMenuView!
@IBOutlet private weak var reactionsMenuContainerView: UIView!
@IBOutlet private weak var reactionsMenuViewHeightConstraint: NSLayoutConstraint!
@IBOutlet private weak var reactionsMenuViewBottomConstraint: NSLayoutConstraint!
// MARK: Private
private var theme: Theme!
private var errorPresenter: MXKErrorPresentation!
private var contextualMenuItems: [RoomContextualMenuItem] = []
private var reactionsMenuViewModel: ReactionsMenuViewModel?
private weak var reactionsMenuView: ReactionsMenuView?
private var reactionsMenuViewBottomStartConstraintConstant: CGFloat?
private var reactionsMenuViewBottomEndConstraintConstant: CGFloat?
private var hiddenToolbarViewBottomConstant: CGFloat {
let bottomSafeAreaHeight: CGFloat
@ -58,31 +69,41 @@ final class RoomContextualMenuViewController: UIViewController, Themable {
// MARK: Public
var contentToReactFrame: CGRect?
var shouldPerformTappedReactionAnimation: Bool {
return self.reactionsMenuView?.reactionHasBeenTapped ?? false
}
weak var delegate: RoomContextualMenuViewControllerDelegate?
// MARK: - Setup
class func instantiate(with contextualMenuItems: [RoomContextualMenuItem]) -> RoomContextualMenuViewController {
class func instantiate(with contextualMenuItems: [RoomContextualMenuItem],
reactionsMenuViewModel: ReactionsMenuViewModel?) -> RoomContextualMenuViewController {
let viewController = StoryboardScene.RoomContextualMenuViewController.initialScene.instantiate()
viewController.theme = ThemeService.shared().theme
viewController.contextualMenuItems = contextualMenuItems
viewController.reactionsMenuViewModel = reactionsMenuViewModel
return viewController
}
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.reactionsMenuView.isHidden = true
self.reactionsMenuContainerView.isHidden = true
if let reactionsMenuViewModel = self.reactionsMenuViewModel {
self.setupReactionsMenu(with: reactionsMenuViewModel)
}
self.backgroundOverlayView.isUserInteractionEnabled = true
self.menuToolbarView.fill(contextualMenuItems: self.contextualMenuItems)
self.setupBackgroundOverlayGestureRecognizers()
self.errorPresenter = MXKErrorAlertPresentation()
self.registerThemeServiceDidChangeThemeNotification()
self.update(theme: self.theme)
}
@ -91,31 +112,87 @@ final class RoomContextualMenuViewController: UIViewController, Themable {
func showMenuToolbar() {
self.menuToolbarViewBottomConstraint.constant = 0
self.menuToolbarView.alpha = 1
}
func hideMenuToolbar() {
self.menuToolbarViewBottomConstraint.constant = self.hiddenToolbarViewBottomConstant
self.menuToolbarView.alpha = 0
}
func showReactionsMenu(withViewModel viewModel: ReactionsMenuViewModel, aroundFrame frame: CGRect) {
self.reactionsMenuView.viewModel = viewModel
self.reactionsMenuView.isHidden = false
func prepareReactionsMenuAnimations() {
guard let frame = self.contentToReactFrame else {
return
}
let menuHeight = self.reactionsMenuViewHeightConstraint.constant
let verticalMargin = Constants.reactionsMenuViewVerticalMargin
let reactionsMenuViewBottomStartConstraintConstant: CGFloat?
let reactionsMenuViewBottomEndConstraintConstant: CGFloat?
// Try to display the menu at the top of the message first
// Then, try at the bottom
// Else, keep the position defined in the storyboard
if frame.origin.y >= self.reactionsMenuViewHeightConstraint.constant {
self.reactionsMenuViewBottomConstraint.constant = frame.origin.y
if frame.origin.y - verticalMargin >= menuHeight {
let menuViewBottomY = frame.origin.y - verticalMargin
reactionsMenuViewBottomStartConstraintConstant = menuViewBottomY + menuHeight/2
reactionsMenuViewBottomEndConstraintConstant = menuViewBottomY
} else {
let frameBottomY = frame.origin.y + frame.size.height
let frameBottomY = frame.origin.y + frame.size.height + verticalMargin
let visibleViewHeight = self.view.frame.size.height - self.menuToolbarView.frame.size.height
if frameBottomY + menuHeight < visibleViewHeight {
self.reactionsMenuViewBottomConstraint.constant = frameBottomY + menuHeight
let menuViewBottomY = frameBottomY + menuHeight
reactionsMenuViewBottomEndConstraintConstant = menuViewBottomY
reactionsMenuViewBottomStartConstraintConstant = menuViewBottomY - menuHeight/2
} else {
reactionsMenuViewBottomEndConstraintConstant = nil
reactionsMenuViewBottomStartConstraintConstant = nil
}
}
self.reactionsMenuViewBottomStartConstraintConstant = reactionsMenuViewBottomStartConstraintConstant
self.reactionsMenuViewBottomEndConstraintConstant = reactionsMenuViewBottomEndConstraintConstant
self.reactionsMenuContainerView.isHidden = false
}
func showReactionsMenu() {
guard let reactionsMenuView = self.reactionsMenuView else {
return
}
if let reactionsMenuViewBottomEndConstraintConstant = self.reactionsMenuViewBottomEndConstraintConstant {
self.reactionsMenuViewBottomConstraint.constant = reactionsMenuViewBottomEndConstraintConstant
}
reactionsMenuView.alpha = 1
reactionsMenuContainerView.transform = CGAffineTransform.identity
}
func hideReactionsMenu() {
guard let reactionsMenuView = self.reactionsMenuView else {
return
}
if let reactionsMenuViewBottomStartConstraintConstant = self.reactionsMenuViewBottomStartConstraintConstant {
self.reactionsMenuViewBottomConstraint.constant = reactionsMenuViewBottomStartConstraintConstant
}
reactionsMenuView.alpha = 0
let transformScale = Constants.reactionsMenuViewHiddenScale
self.reactionsMenuContainerView.transform = CGAffineTransform(scaleX: transformScale, y: transformScale)
}
func selectedReactionAnimationsIntructionsPart1() {
self.reactionsMenuView?.selectionAnimationInstructionPart1()
}
func selectedReactionAnimationsIntructionsPart2() {
self.reactionsMenuView?.selectionAnimationInstructionPart2()
}
func update(theme: Theme) {
@ -124,6 +201,13 @@ final class RoomContextualMenuViewController: UIViewController, Themable {
// MARK: - Private
private func setupReactionsMenu(with viewModel: ReactionsMenuViewModel) {
let reactionsMenuView = ReactionsMenuView.loadFromNib()
self.reactionsMenuContainerView.vc_addSubViewMatchingParent(reactionsMenuView)
reactionsMenuView.viewModel = viewModel
self.reactionsMenuView = reactionsMenuView
}
private func setupBackgroundOverlayGestureRecognizers() {
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handle(gestureRecognizer:)))
@ -155,6 +239,6 @@ extension RoomContextualMenuViewController: UIGestureRecognizerDelegate {
// Avoid triggering background overlay gesture recognizers when touching reactions menu
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
return touch.vc_isInside(view: self.reactionsMenuView) == false
return touch.vc_isInside(view: self.reactionsMenuContainerView) == false
}
}

View file

@ -5158,21 +5158,13 @@
return;
}
[self selectEventWithId:event.eventId];
NSString *selectedEventId = event.eventId;
[self selectEventWithId:selectedEventId];
NSArray<RoomContextualMenuItem*>* contextualMenuItems = [self contextualMenuItemsForEvent:event andCell:cell];
RoomContextualMenuViewController *roomContextualMenuViewController = [RoomContextualMenuViewController instantiateWith:contextualMenuItems];
roomContextualMenuViewController.delegate = self;
[self enableOverlayContainerUserInteractions:YES];
[self.roomContextualMenuPresenter presentWithRoomContextualMenuViewController:roomContextualMenuViewController
from:self
on:self.overlayContainerView
animated:YES
completion:^{
}];
ReactionsMenuViewModel *reactionsMenuViewModel;
CGRect bubbleComponentFrameInOverlayView = CGRectNull;
if (RiotSettings.shared.messageReaction && [cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && [self.roomDataSource canReactToEventWithId:event.eventId])
{
@ -5181,7 +5173,7 @@
NSArray *bubbleComponents = bubbleCellData.bubbleComponents;
NSInteger foundComponentIndex = [bubbleComponents indexOfObjectPassingTest:^BOOL(MXKRoomBubbleComponent * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (obj.event.eventId == event.eventId)
if (obj.event.eventId == selectedEventId)
{
*stop = YES;
return YES;
@ -5201,16 +5193,28 @@
bubbleComponentFrame = roomBubbleTableViewCell.frame;
}
CGRect bubbleComponentFrameInOverlayView = [self.bubblesTableView convertRect:bubbleComponentFrame toView:self.overlayContainerView];
bubbleComponentFrameInOverlayView = [self.bubblesTableView convertRect:bubbleComponentFrame toView:self.overlayContainerView];
NSString *roomId = self.roomDataSource.roomId;
MXAggregations *aggregations = self.mainSession.aggregations;
MXAggregatedReactions *aggregatedReactions = [aggregations aggregatedReactionsOnEvent:selectedEventId inRoom:roomId];
ReactionsMenuViewModel *reactionsMenuViewModel = [[ReactionsMenuViewModel alloc] initWithAggregations:aggregations roomId:roomId eventId:event.eventId];
reactionsMenuViewModel = [[ReactionsMenuViewModel alloc] initWithAggregatedReactions:aggregatedReactions eventId:selectedEventId];
reactionsMenuViewModel.coordinatorDelegate = self;
[self.roomContextualMenuPresenter showReactionsMenuWithReactionsMenuViewModel:reactionsMenuViewModel aroundFrame:bubbleComponentFrameInOverlayView];
}
RoomContextualMenuViewController *roomContextualMenuViewController = [RoomContextualMenuViewController instantiateWith:contextualMenuItems reactionsMenuViewModel:reactionsMenuViewModel];
roomContextualMenuViewController.delegate = self;
[self enableOverlayContainerUserInteractions:YES];
[self.roomContextualMenuPresenter presentWithRoomContextualMenuViewController:roomContextualMenuViewController
from:self
on:self.overlayContainerView
contentToReactFrame:bubbleComponentFrameInOverlayView
animated:YES
completion:^{
}];
}
- (void)hideContextualMenuAnimated:(BOOL)animated
@ -5259,17 +5263,14 @@
[self hideContextualMenuAnimated:YES];
}
- (void)roomContextualMenuViewControllerDidReaction:(RoomContextualMenuViewController *)viewController
{
[self hideContextualMenuAnimated:YES];
}
#pragma mark - ReactionsMenuViewModelCoordinatorDelegate
- (void)reactionsMenuViewModel:(ReactionsMenuViewModel *)viewModel didAddReaction:(NSString *)reaction forEventId:(NSString *)eventId
{
MXWeakify(self);
[self hideContextualMenuAnimated:YES completion:^{
[self.roomDataSource addReaction:reaction forEventId:eventId success:^{
} failure:^(NSError *error) {
@ -5277,14 +5278,15 @@
[self.errorPresenter presentErrorFromViewController:self forError:error animated:YES handler:nil];
}];
[self hideContextualMenuAnimated:YES];
}];
}
- (void)reactionsMenuViewModel:(ReactionsMenuViewModel *)viewModel didRemoveReaction:(NSString *)reaction forEventId:(NSString *)eventId
{
MXWeakify(self);
[self hideContextualMenuAnimated:YES completion:^{
[self.roomDataSource removeReaction:reaction forEventId:eventId success:^{
} failure:^(NSError *error) {
@ -5293,7 +5295,7 @@
[self.errorPresenter presentErrorFromViewController:self forError:error animated:YES handler:nil];
}];
[self hideContextualMenuAnimated:YES];
}];
}
@end