-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add reaction send support for internal
- Loading branch information
1 parent
7ee4a1e
commit 4412a47
Showing
7 changed files
with
369 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
171 changes: 171 additions & 0 deletions
171
Signal/Calls/UserInterface/CallControlsOverflowView.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
// | ||
// Copyright 2024 Signal Messenger, LLC | ||
// SPDX-License-Identifier: AGPL-3.0-only | ||
// | ||
|
||
import SignalRingRTC | ||
import SignalServiceKit | ||
|
||
class CallControlsOverflowView: UIView { | ||
private lazy var reactionPicker: MessageReactionPicker = { | ||
let picker = MessageReactionPicker( | ||
selectedEmoji: nil, | ||
delegate: self, | ||
style: .contextMenu, | ||
forceDarkTheme: true | ||
) | ||
picker.isHidden = true | ||
return picker | ||
}() | ||
|
||
private var reactionSender: ReactionSender? | ||
private var reactionReceiver: ReactionReceiver? | ||
|
||
private weak var emojiPickerSheetPresenter: EmojiPickerSheetPresenter? | ||
|
||
private weak var callControlsOverflowPresenter: CallControlsOverflowPresenter? | ||
|
||
private override init(frame: CGRect) { | ||
super.init(frame: frame) | ||
|
||
if FeatureFlags.callReactionSendSupport { | ||
self.addSubview(reactionPicker) | ||
reactionPicker.autoPinEdgesToSuperviewEdges() | ||
} | ||
|
||
// TODO: Add Raise Hand button | ||
} | ||
|
||
convenience init( | ||
reactionSender: ReactionSender, | ||
reactionReceiver: ReactionReceiver, | ||
emojiPickerSheetPresenter: EmojiPickerSheetPresenter, | ||
callControlsOverflowPresenter: CallControlsOverflowPresenter | ||
) { | ||
self.init(frame: .zero) | ||
self.reactionSender = reactionSender | ||
self.reactionReceiver = reactionReceiver | ||
self.emojiPickerSheetPresenter = emojiPickerSheetPresenter | ||
self.callControlsOverflowPresenter = callControlsOverflowPresenter | ||
} | ||
|
||
// MARK: - Animations | ||
|
||
private var isAnimating = false | ||
|
||
func animateIn() { | ||
self.isHidden = false | ||
guard !isAnimating else { | ||
return | ||
} | ||
isAnimating = true | ||
|
||
self.callControlsOverflowPresenter?.callControlsOverflowWillAppear() | ||
|
||
self.reactionPicker.isHidden = false | ||
// `playPresentationAnimation` is built to be called on new, rather | ||
// than reused, reaction pickers. Since we're reusing here, we need | ||
// to reset the alpha. | ||
self.reactionPicker.alpha = 1 | ||
self.reactionPicker.playPresentationAnimation(duration: 0.2) { [weak self] in | ||
self?.isAnimating = false | ||
} | ||
|
||
// TODO: Animate Raise Hand menu item | ||
} | ||
|
||
func animateOut() { | ||
guard !isAnimating else { | ||
return | ||
} | ||
isAnimating = true | ||
|
||
self.reactionPicker.isHidden = true | ||
self.reactionPicker.playDismissalAnimation( | ||
duration: 0.2, | ||
completion: { [weak self] in | ||
self?.isAnimating = false | ||
self?.isHidden = true | ||
self?.callControlsOverflowPresenter?.callControlsOverflowDidDisappear() | ||
} | ||
) | ||
|
||
// TODO: Animate Raise Hand menu item | ||
} | ||
|
||
required init?(coder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
} | ||
|
||
// MARK: - MessageReactionPickerDelegate | ||
|
||
extension CallControlsOverflowView: MessageReactionPickerDelegate { | ||
func didSelectReaction(reaction: String, isRemoving: Bool, inPosition position: Int) { | ||
self.react(with: reaction) | ||
} | ||
|
||
func didSelectAnyEmoji() { | ||
let sheet = EmojiPickerSheet( | ||
message: nil, | ||
allowReactionConfiguration: false, | ||
forceDarkTheme: true | ||
) { [weak self] selectedEmoji in | ||
guard let selectedEmoji else { return } | ||
self?.react(with: selectedEmoji.rawValue) | ||
} | ||
emojiPickerSheetPresenter?.present( | ||
sheet: sheet, | ||
animated: true | ||
) | ||
} | ||
|
||
private func react(with reaction: String) { | ||
self.callControlsOverflowPresenter?.willSendReaction() | ||
self.reactionSender?.react(value: reaction) | ||
// Locally-sent reactions do not come in via the API, so we add them here. | ||
self.reactionReceiver?.addReactions( | ||
reactions: [ | ||
Reaction( | ||
emoji: reaction, | ||
name: CommonStrings.you, | ||
timestamp: Date.timeIntervalSinceReferenceDate | ||
) | ||
] | ||
) | ||
} | ||
} | ||
|
||
// MARK: ReactionSender | ||
|
||
protocol ReactionSender { | ||
func react(value: String) | ||
} | ||
|
||
extension GroupCall: ReactionSender {} | ||
|
||
// MARK: ReactionReceiver | ||
|
||
protocol ReactionReceiver { | ||
func addReactions(reactions: [Reaction]) | ||
} | ||
|
||
// MARK: - EmojiPickerSheetPresenter | ||
|
||
protocol EmojiPickerSheetPresenter: AnyObject { | ||
func present(sheet: EmojiPickerSheet, animated: Bool) | ||
} | ||
|
||
extension GroupCallViewController: EmojiPickerSheetPresenter { | ||
func present(sheet: EmojiPickerSheet, animated: Bool) { | ||
self.present(sheet, animated: animated) | ||
} | ||
} | ||
|
||
// MARK: - CallControlsOverflowPresenter | ||
|
||
protocol CallControlsOverflowPresenter: AnyObject { | ||
func callControlsOverflowWillAppear() | ||
func callControlsOverflowDidDisappear() | ||
func willSendReaction() | ||
} |
Oops, something went wrong.