mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Merge pull request #7059 from vector-im/mauroromito/fullscreen_mode_2
Rich Text Editor: Fullscreen mode
This commit is contained in:
commit
b5dc19d930
12 changed files with 274 additions and 15 deletions
|
@ -47,6 +47,7 @@ typedef enum : NSUInteger
|
|||
|
||||
|
||||
@class MXKRoomInputToolbarView;
|
||||
@class MXKImageView;
|
||||
@protocol MXKRoomInputToolbarViewDelegate <NSObject>
|
||||
|
||||
/**
|
||||
|
@ -381,4 +382,6 @@ typedef enum : NSUInteger
|
|||
*/
|
||||
@property (nonatomic) NSAttributedString *attributedTextMessage;
|
||||
|
||||
- (void)dismissValidationView:(MXKImageView*)validationView;
|
||||
|
||||
@end
|
||||
|
|
|
@ -214,14 +214,14 @@ typedef NS_ENUM(NSUInteger, MXKRoomViewControllerJoinRoomResult) {
|
|||
|
||||
@property (weak, nonatomic) IBOutlet UITableView *bubblesTableView;
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomTitleViewContainer;
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomInputToolbarContainer;
|
||||
@property (strong, nonatomic) IBOutlet UIView *roomInputToolbarContainer;
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomActivitiesContainer;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bubblesTableViewTopConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bubblesTableViewBottomConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *roomActivitiesContainerHeightConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *roomInputToolbarContainerHeightConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *roomInputToolbarContainerBottomConstraint;
|
||||
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *roomInputToolbarContainerBottomConstraint;
|
||||
|
||||
#pragma mark - Class methods
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ extern NSTimeInterval const kResizeComposerAnimationDuration;
|
|||
@property (weak, nonatomic, nullable) IBOutlet UIView *inputBackgroundView;
|
||||
@property (weak, nonatomic, nullable) IBOutlet UIButton *scrollToBottomButton;
|
||||
@property (weak, nonatomic, nullable) IBOutlet BadgeLabel *scrollToBottomBadgeLabel;
|
||||
@property (nonatomic, strong) IBOutlet UIView *overlayContainerView;
|
||||
|
||||
// Remove Jitsi widget container
|
||||
@property (weak, nonatomic, nullable) IBOutlet UIView *removeJitsiWidgetContainer;
|
||||
|
@ -115,6 +116,11 @@ extern NSTimeInterval const kResizeComposerAnimationDuration;
|
|||
// The voice broadcast service
|
||||
@property (nonatomic, nullable) VoiceBroadcastService *voiceBroadcastService;
|
||||
|
||||
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray<NSLayoutConstraint*> *toolbarContainerConstraints;
|
||||
|
||||
@property (strong, nonatomic, nullable) UIView* maximisedToolbarDimmingView;
|
||||
|
||||
|
||||
/**
|
||||
Retrieve the live data source in cases where the timeline is not live.
|
||||
|
||||
|
|
|
@ -187,7 +187,6 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
MXTaskProfile *notificationTaskProfile;
|
||||
}
|
||||
|
||||
@property (nonatomic, weak) IBOutlet UIView *overlayContainerView;
|
||||
@property (nonatomic, strong) RemoveJitsiWidgetView *removeJitsiWidgetView;
|
||||
|
||||
|
||||
|
@ -470,6 +469,9 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
self.jumpToLastUnreadBanner.backgroundColor = ThemeService.shared.theme.colors.navigation;
|
||||
[self.jumpToLastUnreadBanner vc_removeShadow];
|
||||
self.resetReadMarkerButton.tintColor = ThemeService.shared.theme.colors.quarterlyContent;
|
||||
if (self.maximisedToolbarDimmingView) {
|
||||
self.maximisedToolbarDimmingView.backgroundColor = [UIColor.blackColor colorWithAlphaComponent:0.29];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -481,6 +483,9 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
radius:8
|
||||
opacity:0.1];
|
||||
self.resetReadMarkerButton.tintColor = ThemeService.shared.theme.colors.tertiaryContent;
|
||||
if (self.maximisedToolbarDimmingView) {
|
||||
self.maximisedToolbarDimmingView.backgroundColor = [UIColor.blackColor colorWithAlphaComponent:0.12];
|
||||
}
|
||||
}
|
||||
|
||||
self.scrollToBottomBadgeLabel.badgeColor = ThemeService.shared.theme.tintColor;
|
||||
|
@ -603,6 +608,8 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
|
||||
// Stop the loading indicator even if the session is still in progress
|
||||
[self stopLoadingUserIndicator];
|
||||
|
||||
[self setMaximisedToolbarIsHiddenIfNeeded: YES];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
|
@ -678,6 +685,8 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
// Note: We have to wait for viewDidAppear before updating growingTextView (viewWillAppear is too early)
|
||||
self.inputToolbarView.attributedTextMessage = self.roomDataSource.partialAttributedTextMessage;
|
||||
}
|
||||
|
||||
[self setMaximisedToolbarIsHiddenIfNeeded: NO];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
|
@ -1212,8 +1221,6 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
if (!self.inputToolbarView || ![self.inputToolbarView isMemberOfClass:roomInputToolbarViewClass])
|
||||
{
|
||||
[super setRoomInputToolbarViewClass:roomInputToolbarViewClass];
|
||||
|
||||
|
||||
if ([self.inputToolbarView.class conformsToProtocol:@protocol(RoomInputToolbarViewProtocol)]) {
|
||||
id<RoomInputToolbarViewProtocol> inputToolbar = (id<RoomInputToolbarViewProtocol>)self.inputToolbarView;
|
||||
[inputToolbar setVoiceMessageToolbarView:self.voiceMessageController.voiceMessageToolbarView];
|
||||
|
|
|
@ -154,6 +154,85 @@ extension RoomViewController {
|
|||
RiotSettings.shared.enableWysiwygTextFormatting.toggle()
|
||||
wysiwygInputToolbar?.textFormattingEnabled.toggle()
|
||||
}
|
||||
|
||||
@objc func didChangeMaximisedState(_ isMaximised: Bool) {
|
||||
guard let wysiwygInputToolbar = wysiwygInputToolbar else { return }
|
||||
if isMaximised {
|
||||
var view: UIView!
|
||||
// iPhone
|
||||
if let navView = self.navigationController?.navigationController?.view {
|
||||
view = navView
|
||||
// iPad
|
||||
} else if let navView = self.navigationController?.view {
|
||||
view = navView
|
||||
} else {
|
||||
return
|
||||
}
|
||||
var originalRect = roomInputToolbarContainer.convert(roomInputToolbarContainer.frame, to: view)
|
||||
var textView: UITextView?
|
||||
if wysiwygInputToolbar.isFocused {
|
||||
textView = UITextView()
|
||||
self.view.window?.addSubview(textView!)
|
||||
textView?.becomeFirstResponder()
|
||||
originalRect = wysiwygInputToolbar.convert(wysiwygInputToolbar.frame, to: view)
|
||||
}
|
||||
wysiwygInputToolbar.showKeyboard()
|
||||
roomInputToolbarContainer.removeFromSuperview()
|
||||
let dimmingView = UIView()
|
||||
dimmingView.translatesAutoresizingMaskIntoConstraints = false
|
||||
// Same as the system dimming background color
|
||||
dimmingView.backgroundColor = .black.withAlphaComponent(ThemeService.shared().isCurrentThemeDark() ? 0.29 : 0.12)
|
||||
maximisedToolbarDimmingView = dimmingView
|
||||
view.addSubview(dimmingView)
|
||||
dimmingView.frame = view.bounds
|
||||
NSLayoutConstraint.activate(
|
||||
[
|
||||
dimmingView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
dimmingView.leftAnchor.constraint(equalTo: view.leftAnchor),
|
||||
dimmingView.rightAnchor.constraint(equalTo: view.rightAnchor),
|
||||
dimmingView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
|
||||
]
|
||||
)
|
||||
dimmingView.addSubview(self.roomInputToolbarContainer)
|
||||
roomInputToolbarContainer.frame = originalRect
|
||||
roomInputToolbarContainer.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
|
||||
roomInputToolbarContainer.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
|
||||
roomInputToolbarContainer.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
|
||||
UIView.animate(withDuration: kResizeComposerAnimationDuration, delay: 0, options: [.curveEaseInOut]) {
|
||||
view.layoutIfNeeded()
|
||||
}
|
||||
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(didPanRoomToolbarContainer(_ :)))
|
||||
roomInputToolbarContainer.addGestureRecognizer(panGesture)
|
||||
textView?.removeFromSuperview()
|
||||
} else {
|
||||
let originalRect = wysiwygInputToolbar.convert(wysiwygInputToolbar.frame, to: view)
|
||||
var textView: UITextView?
|
||||
if wysiwygInputToolbar.isFocused {
|
||||
textView = UITextView()
|
||||
self.view.window?.addSubview(textView!)
|
||||
textView?.becomeFirstResponder()
|
||||
wysiwygInputToolbar.showKeyboard()
|
||||
}
|
||||
self.roomInputToolbarContainer.removeFromSuperview()
|
||||
maximisedToolbarDimmingView?.removeFromSuperview()
|
||||
maximisedToolbarDimmingView = nil
|
||||
self.view.insertSubview(self.roomInputToolbarContainer, belowSubview: self.overlayContainerView)
|
||||
roomInputToolbarContainer.frame = originalRect
|
||||
NSLayoutConstraint.activate(self.toolbarContainerConstraints)
|
||||
self.roomInputToolbarContainerBottomConstraint.isActive = true
|
||||
UIView.animate(withDuration: kResizeComposerAnimationDuration, delay: 0, options: [.curveEaseInOut]) {
|
||||
self.view.layoutIfNeeded()
|
||||
}
|
||||
roomInputToolbarContainer.gestureRecognizers?.removeAll()
|
||||
textView?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func setMaximisedToolbarIsHiddenIfNeeded(_ isHidden: Bool) {
|
||||
if wysiwygInputToolbar?.isMaximised == true {
|
||||
roomInputToolbarContainer.superview?.isHidden = isHidden
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private Helpers
|
||||
|
@ -165,4 +244,28 @@ private extension RoomViewController {
|
|||
var wysiwygInputToolbar: WysiwygInputToolbarView? {
|
||||
return self.inputToolbarView as? WysiwygInputToolbarView
|
||||
}
|
||||
|
||||
@objc private func didPanRoomToolbarContainer(_ sender: UIPanGestureRecognizer) {
|
||||
guard let wysiwygInputToolbar = wysiwygInputToolbar else { return }
|
||||
switch sender.state {
|
||||
case .began:
|
||||
break
|
||||
case .changed:
|
||||
let translation = sender.translation(in: view)
|
||||
let translatedValue = wysiwygInputToolbar.maxExpandedHeight - translation.y
|
||||
guard translatedValue <= wysiwygInputToolbar.maxExpandedHeight, translatedValue >= wysiwygInputToolbar.compressedHeight else { return }
|
||||
wysiwygInputToolbar.idealHeight = translatedValue
|
||||
case .ended:
|
||||
if wysiwygInputToolbar.idealHeight <= wysiwygInputToolbar.maxCompressedHeight {
|
||||
wysiwygInputToolbar.minimise()
|
||||
} else {
|
||||
wysiwygInputToolbar.idealHeight = wysiwygInputToolbar.maxExpandedHeight
|
||||
}
|
||||
case .cancelled:
|
||||
wysiwygInputToolbar.idealHeight = wysiwygInputToolbar.maxExpandedHeight
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
|
@ -35,6 +35,11 @@
|
|||
<outlet property="userSuggestionContainerHeightConstraint" destination="1Cd-cT-gOr" id="au5-3q-r54"/>
|
||||
<outlet property="userSuggestionContainerView" destination="oni-F4-X1U" id="0js-Ji-8Mm"/>
|
||||
<outlet property="view" destination="iN0-l3-epB" id="ieV-u7-rXU"/>
|
||||
<outletCollection property="toolbarContainerConstraints" destination="T1Y-r9-bYV" id="wax-9P-KGn"/>
|
||||
<outletCollection property="toolbarContainerConstraints" destination="ave-fu-X1D" id="gBl-7F-srT"/>
|
||||
<outletCollection property="toolbarContainerConstraints" destination="pRw-S0-6WL" id="q4S-0g-sqQ"/>
|
||||
<outletCollection property="toolbarContainerConstraints" destination="QO8-nF-xys" id="aQe-20-4Pq"/>
|
||||
<outletCollection property="toolbarContainerConstraints" destination="acJ-g8-R7x" id="uEo-Ez-seV"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
|
|
|
@ -75,6 +75,8 @@ typedef NS_ENUM(NSUInteger, RoomInputToolbarViewSendMode)
|
|||
*/
|
||||
- (void)roomInputToolbarView:(RoomInputToolbarView *)toolbarView sendAttributedTextMessage:(NSAttributedString *)attributedTextMessage;
|
||||
|
||||
- (void)didChangeMaximisedState: (BOOL) isMaximised;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,9 +32,15 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
|
|||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
private var keyboardHeight: CGFloat = .zero {
|
||||
didSet {
|
||||
updateTextViewHeight()
|
||||
}
|
||||
}
|
||||
private var voiceMessageToolbarView: VoiceMessageToolbarView?
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var heightConstraint: NSLayoutConstraint!
|
||||
private var voiceMessageBottomConstraint: NSLayoutConstraint?
|
||||
private var hostingViewController: VectorHostingController!
|
||||
private var wysiwygViewModel = WysiwygComposerViewModel(textColor: ThemeService.shared().theme.colors.primaryContent)
|
||||
private var viewModel: ComposerViewModelProtocol = ComposerViewModel(
|
||||
|
@ -52,6 +58,35 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
|
|||
}
|
||||
}
|
||||
|
||||
override var isFocused: Bool {
|
||||
viewModel.isFocused
|
||||
}
|
||||
|
||||
var isMaximised: Bool {
|
||||
wysiwygViewModel.maximised
|
||||
}
|
||||
|
||||
var idealHeight: CGFloat {
|
||||
get {
|
||||
wysiwygViewModel.idealHeight
|
||||
}
|
||||
set {
|
||||
wysiwygViewModel.idealHeight = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var compressedHeight: CGFloat {
|
||||
wysiwygViewModel.compressedHeight
|
||||
}
|
||||
|
||||
var maxExpandedHeight: CGFloat {
|
||||
wysiwygViewModel.maxExpandedHeight
|
||||
}
|
||||
|
||||
var maxCompressedHeight: CGFloat {
|
||||
wysiwygViewModel.maxCompressedHeight
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
override class func instantiate() -> MXKRoomInputToolbarView! {
|
||||
|
@ -115,11 +150,32 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
|
|||
.removeDuplicates()
|
||||
.sink { [weak hostingViewController] _ in
|
||||
hostingViewController?.view.setNeedsLayout()
|
||||
},
|
||||
|
||||
wysiwygViewModel.$maximised
|
||||
.removeDuplicates()
|
||||
.sink { [weak self] value in
|
||||
guard let self = self else { return }
|
||||
self.toolbarViewDelegate?.didChangeMaximisedState(value)
|
||||
self.hostingViewController.view.layer.cornerRadius = value ? 20 : 0
|
||||
}
|
||||
]
|
||||
|
||||
update(theme: ThemeService.shared().theme)
|
||||
registerThemeServiceDidChangeThemeNotification()
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(keyboardWillShow),
|
||||
name: UIResponder.keyboardWillShowNotification,
|
||||
object: nil
|
||||
)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(keyboardWillHide),
|
||||
name: UIResponder.keyboardWillHideNotification,
|
||||
object: nil
|
||||
)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(deviceDidRotate), name: UIDevice.orientationDidChangeNotification, object: nil)
|
||||
}
|
||||
|
||||
override func customizeRendering() {
|
||||
|
@ -131,8 +187,53 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
|
|||
self.viewModel.dismissKeyboard()
|
||||
}
|
||||
|
||||
override func dismissValidationView(_ validationView: MXKImageView!) {
|
||||
super.dismissValidationView(validationView)
|
||||
if isMaximised {
|
||||
showKeyboard()
|
||||
}
|
||||
}
|
||||
|
||||
func showKeyboard() {
|
||||
self.viewModel.showKeyboard()
|
||||
}
|
||||
|
||||
func minimise() {
|
||||
wysiwygViewModel.maximised = false
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
@objc private func keyboardWillShow(_ notification: Notification) {
|
||||
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
|
||||
let keyboardRectangle = keyboardFrame.cgRectValue
|
||||
keyboardHeight = keyboardRectangle.height
|
||||
UIView.performWithoutAnimation {
|
||||
if self.isMaximised {
|
||||
self.voiceMessageBottomConstraint?.constant = keyboardHeight - (window?.safeAreaInsets.bottom ?? 0) + 4
|
||||
} else {
|
||||
self.voiceMessageBottomConstraint?.constant = 4
|
||||
}
|
||||
self.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func keyboardWillHide(_ notification: Notification) {
|
||||
if self.isMaximised {
|
||||
UIView.performWithoutAnimation {
|
||||
self.voiceMessageBottomConstraint?.constant = 4
|
||||
self.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func deviceDidRotate(_ notification: Notification) {
|
||||
DispatchQueue.main.async {
|
||||
self.updateTextViewHeight()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateToolbarHeight(wysiwygHeight: CGFloat) {
|
||||
self.heightConstraint.constant = wysiwygHeight
|
||||
toolbarViewDelegate?.roomInputToolbarView?(self, heightDidChanged: wysiwygHeight, completion: nil)
|
||||
|
@ -140,6 +241,9 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
|
|||
|
||||
private func sendWysiwygMessage(content: WysiwygComposerContent) {
|
||||
delegate?.roomInputToolbarView?(self, sendFormattedTextMessage: content.html, withRawText: content.markdown)
|
||||
if isMaximised {
|
||||
minimise()
|
||||
}
|
||||
}
|
||||
|
||||
private func showSendMediaActions() {
|
||||
|
@ -179,6 +283,14 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
|
|||
wysiwygViewModel.textColor = theme.colors.primaryContent
|
||||
}
|
||||
|
||||
private func updateTextViewHeight() {
|
||||
let height = UIScreen.main.bounds.height
|
||||
let barOffset: CGFloat = 68
|
||||
let toolbarHeight: CGFloat = 82
|
||||
let finalHeight = height - keyboardHeight - toolbarHeight - barOffset
|
||||
wysiwygViewModel.maxExpandedHeight = finalHeight
|
||||
}
|
||||
|
||||
// MARK: - HtmlRoomInputToolbarViewProtocol
|
||||
var isEncryptionEnabled = false {
|
||||
didSet {
|
||||
|
@ -239,17 +351,21 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
|
|||
voiceMessageToolbarView.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.deactivate(voiceMessageToolbarView.containersTopConstraints)
|
||||
addSubview(voiceMessageToolbarView)
|
||||
let bottomConstraint = hostingViewController.view.bottomAnchor.constraint(equalTo: voiceMessageToolbarView.bottomAnchor, constant: 4)
|
||||
voiceMessageBottomConstraint = bottomConstraint
|
||||
NSLayoutConstraint.activate(
|
||||
[
|
||||
hostingViewController.view.topAnchor.constraint(equalTo: voiceMessageToolbarView.topAnchor),
|
||||
hostingViewController.view.leftAnchor.constraint(equalTo: voiceMessageToolbarView.leftAnchor),
|
||||
hostingViewController.view.bottomAnchor.constraint(equalTo: voiceMessageToolbarView.bottomAnchor, constant: 4),
|
||||
hostingViewController.view.rightAnchor.constraint(equalTo: voiceMessageToolbarView.rightAnchor)
|
||||
hostingViewController.view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: voiceMessageToolbarView.topAnchor),
|
||||
hostingViewController.view.safeAreaLayoutGuide.leftAnchor.constraint(equalTo: voiceMessageToolbarView.leftAnchor),
|
||||
bottomConstraint,
|
||||
hostingViewController.view.safeAreaLayoutGuide.rightAnchor.constraint(equalTo: voiceMessageToolbarView.rightAnchor)
|
||||
]
|
||||
)
|
||||
} else {
|
||||
self.voiceMessageToolbarView?.removeFromSuperview()
|
||||
self.voiceMessageToolbarView = nil
|
||||
self.voiceMessageBottomConstraint?.isActive = false
|
||||
self.voiceMessageBottomConstraint = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ struct Composer: View {
|
|||
|
||||
private let horizontalPadding: CGFloat = 12
|
||||
private let borderHeight: CGFloat = 40
|
||||
private var minTextViewHeight: CGFloat = 22
|
||||
private let minTextViewHeight: CGFloat = 22
|
||||
private var verticalPadding: CGFloat {
|
||||
(borderHeight - minTextViewHeight) / 2
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ struct Composer: View {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var composerContainer: some View {
|
||||
let rect = RoundedRectangle(cornerRadius: cornerRadius)
|
||||
return VStack(spacing: 12) {
|
||||
|
@ -147,7 +147,7 @@ struct Composer: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var sendMediaButton: some View {
|
||||
return Button {
|
||||
showSendMediaActions()
|
||||
|
@ -162,7 +162,7 @@ struct Composer: View {
|
|||
.padding(.trailing, 8)
|
||||
.accessibilityLabel(VectorL10n.create)
|
||||
}
|
||||
|
||||
|
||||
private var sendButton: some View {
|
||||
return Button {
|
||||
sendMessageAction(wysiwygViewModel.content)
|
||||
|
@ -204,6 +204,12 @@ struct Composer: View {
|
|||
|
||||
var body: some View {
|
||||
VStack(spacing: 8) {
|
||||
if wysiwygViewModel.maximised {
|
||||
RoundedRectangle(cornerRadius: 4)
|
||||
.fill(theme.colors.quinaryContent)
|
||||
.frame(width: 36, height: 5)
|
||||
.padding(.top, 10)
|
||||
}
|
||||
HStack(alignment: .bottom, spacing: 0) {
|
||||
if !viewModel.viewState.textFormattingEnabled {
|
||||
sendMediaButton
|
||||
|
|
|
@ -63,6 +63,10 @@ final class ComposerViewModel: ComposerViewModelType, ComposerViewModelProtocol
|
|||
}
|
||||
}
|
||||
|
||||
var isFocused: Bool {
|
||||
state.bindings.focused
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
override func process(viewAction: ComposerViewAction) {
|
||||
|
@ -77,4 +81,8 @@ final class ComposerViewModel: ComposerViewModelType, ComposerViewModelProtocol
|
|||
func dismissKeyboard() {
|
||||
state.bindings.focused = false
|
||||
}
|
||||
|
||||
func showKeyboard() {
|
||||
state.bindings.focused = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ protocol ComposerViewModelProtocol {
|
|||
var textFormattingEnabled: Bool { get set }
|
||||
var eventSenderDisplayName: String? { get set }
|
||||
var placeholder: String? { get set }
|
||||
var isFocused: Bool { get }
|
||||
|
||||
func dismissKeyboard()
|
||||
func showKeyboard()
|
||||
}
|
||||
|
|
1
changelog.d/7058.change
Normal file
1
changelog.d/7058.change
Normal file
|
@ -0,0 +1 @@
|
|||
Rich Text Composer: Fullscreen mode now is matching the design requirements.
|
Loading…
Reference in a new issue