Merge MatrixKit develop with commit hash: b85b736313bec0592bd1cabc68035d97f5331137

This commit is contained in:
SBiOSoftWhare
2021-12-03 11:47:24 +01:00
parent af65f71177
commit f57941177e
475 changed files with 87437 additions and 0 deletions
@@ -0,0 +1,120 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations 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 <MatrixSDK/MatrixSDK.h>
#import "MXKView.h"
@class MXKRoomTitleView;
@protocol MXKRoomTitleViewDelegate <NSObject>
/**
Tells the delegate that an alert must be presented.
@param titleView the room title view.
@param alertController the alert to present.
*/
- (void)roomTitleView:(MXKRoomTitleView*)titleView presentAlertController:(UIAlertController*)alertController;
/**
Asks the delegate if editing should begin
@param titleView the room title view.
@return YES if an editing session should be initiated; otherwise, NO to disallow editing.
*/
- (BOOL)roomTitleViewShouldBeginEditing:(MXKRoomTitleView*)titleView;
@optional
/**
Tells the delegate that the saving of user's changes is in progress or is finished.
@param titleView the room title view.
@param saving YES if a request is running to save user's changes.
*/
- (void)roomTitleView:(MXKRoomTitleView*)titleView isSaving:(BOOL)saving;
@end
/**
'MXKRoomTitleView' instance displays editable room display name.
*/
@interface MXKRoomTitleView : MXKView <UITextFieldDelegate>
{
@protected
/**
Potential alert.
*/
UIAlertController *currentAlert;
/**
Test fields input accessory.
*/
UIView *inputAccessoryView;
}
@property (weak, nonatomic) IBOutlet UITextField *displayNameTextField;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *displayNameTextFieldTopConstraint;
@property (strong, nonatomic) MXRoom *mxRoom;
@property (nonatomic) BOOL editable;
@property (nonatomic) BOOL isEditing;
/**
* Returns the `UINib` object initialized for the room title view.
*
* @return The initialized `UINib` object or `nil` if there were errors during
* initialization or the nib file could not be located.
*/
+ (UINib *)nib;
/**
Creates and returns a new `MXKRoomTitleView-inherited` object.
@discussion This is the designated initializer for programmatic instantiation.
@return An initialized `MXKRoomTitleView-inherited` object if successful, `nil` otherwise.
*/
+ (instancetype)roomTitleView;
/**
The delegate notified when inputs are ready.
*/
@property (weak, nonatomic) id<MXKRoomTitleViewDelegate> delegate;
/**
The custom accessory view associated to all text field of this 'MXKRoomTitleView' instance.
This view is actually used to retrieve the keyboard view. Indeed the keyboard view is the superview of
this accessory view when a text field become the first responder.
*/
@property (readonly) UIView *inputAccessoryView;
/**
Dismiss keyboard.
*/
- (void)dismissKeyboard;
/**
Force title view refresh.
*/
- (void)refreshDisplay;
/**
Dispose view resources and listener.
*/
- (void)destroy;
@end
@@ -0,0 +1,279 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 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 "MXKRoomTitleView.h"
#import "MXKConstants.h"
#import "NSBundle+MatrixKit.h"
#import "MXRoom+Sync.h"
#import "MXKSwiftHeader.h"
@interface MXKRoomTitleView ()
{
// Observer kMXRoomSummaryDidChangeNotification to keep updated the room name.
__weak id mxRoomSummaryDidChangeObserver;
}
@end
@implementation MXKRoomTitleView
@synthesize inputAccessoryView;
+ (UINib *)nib
{
return [UINib nibWithNibName:NSStringFromClass([MXKRoomTitleView class])
bundle:[NSBundle bundleForClass:[MXKRoomTitleView class]]];
}
- (void)awakeFromNib
{
[super awakeFromNib];
[self setTranslatesAutoresizingMaskIntoConstraints: NO];
// Add an accessory view to the text view in order to retrieve keyboard view.
inputAccessoryView = [[UIView alloc] initWithFrame:CGRectZero];
self.displayNameTextField.inputAccessoryView = inputAccessoryView;
self.displayNameTextField.enabled = NO;
self.displayNameTextField.returnKeyType = UIReturnKeyDone;
self.displayNameTextField.hidden = YES;
}
+ (instancetype)roomTitleView
{
return [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
}
- (void)dealloc
{
inputAccessoryView = nil;
}
#pragma mark - Override MXKView
-(void)customizeViewRendering
{
[super customizeViewRendering];
self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
}
#pragma mark -
- (void)refreshDisplay
{
if (_mxRoom)
{
// Replace empty string by nil : avoid having the placeholder 'Room name" when there is no displayname
self.displayNameTextField.text = (_mxRoom.summary.displayname.length) ? _mxRoom.summary.displayname : nil;
}
else
{
self.displayNameTextField.text = [MatrixKitL10n roomPleaseSelect];
self.displayNameTextField.enabled = NO;
}
self.displayNameTextField.hidden = NO;
}
- (void)destroy
{
self.delegate = nil;
self.mxRoom = nil;
if (mxRoomSummaryDidChangeObserver)
{
[NSNotificationCenter.defaultCenter removeObserver:mxRoomSummaryDidChangeObserver];
mxRoomSummaryDidChangeObserver = nil;
}
}
- (void)dismissKeyboard
{
// Hide the keyboard
[self.displayNameTextField resignFirstResponder];
}
#pragma mark -
- (void)setMxRoom:(MXRoom *)mxRoom
{
// Check whether the room is actually changed
if (_mxRoom != mxRoom)
{
// Remove potential listener
if (mxRoomSummaryDidChangeObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:mxRoomSummaryDidChangeObserver];
mxRoomSummaryDidChangeObserver = nil;
}
if (mxRoom)
{
MXWeakify(self);
// Register a listener to handle the room name change
mxRoomSummaryDidChangeObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXRoomSummaryDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
// Check whether the text field is editing before refreshing title view
if (!self.isEditing)
{
[self refreshDisplay];
}
}];
}
_mxRoom = mxRoom;
}
// Force refresh
[self refreshDisplay];
}
- (void)setEditable:(BOOL)editable
{
self.displayNameTextField.enabled = editable;
}
- (BOOL)isEditing
{
return self.displayNameTextField.isEditing;
}
#pragma mark - UITextField delegate
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
// check if the deleaget allows the edition
if (!self.delegate || [self.delegate roomTitleViewShouldBeginEditing:self])
{
NSString *alertMsg = nil;
if (textField == self.displayNameTextField)
{
// Check whether the user has enough power to rename the room
MXRoomPowerLevels *powerLevels = _mxRoom.dangerousSyncState.powerLevels;
NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:_mxRoom.mxSession.myUser.userId];
if (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomName])
{
// Only the room name is edited here, update the text field with the room name
textField.text = _mxRoom.summary.displayname;
textField.backgroundColor = [UIColor whiteColor];
}
else
{
alertMsg = [MatrixKitL10n roomErrorNameEditionNotAuthorized];
}
}
if (alertMsg)
{
// Alert user
__weak typeof(self) weakSelf = self;
if (currentAlert)
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
}
currentAlert = [UIAlertController alertControllerWithTitle:nil message:alertMsg preferredStyle:UIAlertControllerStyleAlert];
[currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n cancel]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
typeof(self) self = weakSelf;
self->currentAlert = nil;
}]];
[self.delegate roomTitleView:self presentAlertController:currentAlert];
return NO;
}
return YES;
}
else
{
return NO;
}
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if (textField == self.displayNameTextField)
{
textField.backgroundColor = [UIColor clearColor];
NSString *roomName = textField.text;
if ((roomName.length || _mxRoom.summary.displayname.length) && [roomName isEqualToString:_mxRoom.summary.displayname] == NO)
{
if ([self.delegate respondsToSelector:@selector(roomTitleView:isSaving:)])
{
[self.delegate roomTitleView:self isSaving:YES];
}
__weak typeof(self) weakSelf = self;
[_mxRoom setName:roomName success:^{
if (weakSelf)
{
typeof(weakSelf)strongSelf = weakSelf;
if ([strongSelf.delegate respondsToSelector:@selector(roomTitleView:isSaving:)])
{
[strongSelf.delegate roomTitleView:strongSelf isSaving:NO];
}
}
} failure:^(NSError *error) {
if (weakSelf)
{
typeof(weakSelf)strongSelf = weakSelf;
if ([strongSelf.delegate respondsToSelector:@selector(roomTitleView:isSaving:)])
{
[strongSelf.delegate roomTitleView:strongSelf isSaving:NO];
}
// Revert change
textField.text = strongSelf.mxRoom.summary.displayname;
MXLogDebug(@"[MXKRoomTitleView] Rename room failed");
// Notify MatrixKit user
NSString *myUserId = strongSelf.mxRoom.mxSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}
}];
}
else
{
// No change on room name, restore title with room displayName
textField.text = _mxRoom.summary.displayname;
}
}
}
- (BOOL)textFieldShouldReturn:(UITextField*) textField
{
// "Done" key has been pressed
[textField resignFirstResponder];
return YES;
}
@end
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="Wek-LA-v7p" customClass="MXKRoomTitleView">
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Room Name" textAlignment="center" minimumFontSize="14" translatesAutoresizingMaskIntoConstraints="NO" id="hag-WW-XxJ">
<rect key="frame" x="0.0" y="5" width="600" height="21"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits" returnKeyType="next"/>
<connections>
<outlet property="delegate" destination="Wek-LA-v7p" id="J7I-R9-BkF"/>
</connections>
</textField>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="hag-WW-XxJ" secondAttribute="trailing" id="LaZ-Yg-BjR"/>
<constraint firstItem="hag-WW-XxJ" firstAttribute="leading" secondItem="Wek-LA-v7p" secondAttribute="leading" id="MyD-YH-Cln"/>
<constraint firstItem="hag-WW-XxJ" firstAttribute="top" secondItem="Wek-LA-v7p" secondAttribute="top" constant="5" id="c4c-HK-oti"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="displayNameTextField" destination="hag-WW-XxJ" id="1AJ-rc-7Rg"/>
<outlet property="displayNameTextFieldTopConstraint" destination="c4c-HK-oti" id="ygV-sQ-MdW"/>
</connections>
</view>
</objects>
</document>
@@ -0,0 +1,36 @@
/*
Copyright 2015 OpenMarket 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 "MXKRoomTitleView.h"
/**
'MXKRoomTitleViewWithTopic' inherits 'MXKRoomTitleView' to add an editable room topic field.
*/
@interface MXKRoomTitleViewWithTopic : MXKRoomTitleView <UIGestureRecognizerDelegate>{
}
@property (weak, nonatomic) IBOutlet UITextField *topicTextField;
@property (nonatomic) BOOL hiddenTopic;
/**
Stop topic animation.
@return YES if the animation has been stopped.
*/
- (BOOL)stopTopicAnimation;
@end
@@ -0,0 +1,520 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 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 "MXKRoomTitleViewWithTopic.h"
#import "MXKConstants.h"
#import "NSBundle+MatrixKit.h"
#import "MXRoom+Sync.h"
#import "MXKSwiftHeader.h"
@interface MXKRoomTitleViewWithTopic ()
{
id roomTopicListener;
// the topic can be animated if it is longer than the screen size
UIScrollView* scrollView;
UILabel* label;
UIView* topicTextFieldMaskView;
// do not start the topic animation asap
NSTimer * animationTimer;
}
@end
@implementation MXKRoomTitleViewWithTopic
+ (UINib *)nib
{
return [UINib nibWithNibName:NSStringFromClass([MXKRoomTitleViewWithTopic class])
bundle:[NSBundle bundleForClass:[MXKRoomTitleViewWithTopic class]]];
}
- (void)awakeFromNib
{
[super awakeFromNib];
// Add an accessory view to the text view in order to retrieve keyboard view.
self.topicTextField.inputAccessoryView = inputAccessoryView;
self.displayNameTextField.returnKeyType = UIReturnKeyNext;
self.topicTextField.enabled = NO;
self.topicTextField.returnKeyType = UIReturnKeyDone;
self.hiddenTopic = YES;
}
- (void)refreshDisplay
{
[super refreshDisplay];
if (self.mxRoom)
{
// Remove new line characters
NSString *topic = [MXTools stripNewlineCharacters:self.mxRoom.summary.topic];
// replace empty string by nil: avoid having the placeholder when there is no topic
self.topicTextField.text = (topic.length ? topic : nil);
}
else
{
self.topicTextField.text = nil;
}
self.hiddenTopic = (!self.topicTextField.text.length);
}
- (void)destroy
{
// stop any animation
[self stopTopicAnimation];
[super destroy];
}
- (void)dismissKeyboard
{
// Hide the keyboard
[self.topicTextField resignFirstResponder];
// restart the animation
[self stopTopicAnimation];
[super dismissKeyboard];
}
#pragma mark -
- (void)setMxRoom:(MXRoom *)mxRoom
{
// Make sure we can access synchronously to self.mxRoom and mxRoom data
// to avoid race conditions
MXWeakify(self);
[mxRoom.mxSession preloadRoomsData:self.mxRoom ? @[self.mxRoom.roomId, mxRoom.roomId] : @[mxRoom.roomId] onComplete:^{
MXStrongifyAndReturnIfNil(self);
// Check whether the room is actually changed
if (self.mxRoom != mxRoom)
{
// Remove potential listener
if (self->roomTopicListener && self.mxRoom)
{
MXWeakify(self);
[self.mxRoom liveTimeline:^(MXEventTimeline *liveTimeline) {
MXStrongifyAndReturnIfNil(self);
[liveTimeline removeListener:self->roomTopicListener];
self->roomTopicListener = nil;
}];
}
if (mxRoom)
{
// Register a listener to handle messages related to room name
self->roomTopicListener = [mxRoom listenToEventsOfTypes:@[kMXEventTypeStringRoomTopic] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) {
// Consider only live events
if (direction == MXTimelineDirectionForwards)
{
[self refreshDisplay];
}
}];
}
}
super.mxRoom = mxRoom;
}];
}
- (void)setEditable:(BOOL)editable
{
self.topicTextField.enabled = editable;
super.editable = editable;
}
- (void)setHiddenTopic:(BOOL)hiddenTopic
{
[self stopTopicAnimation];
if (hiddenTopic)
{
self.topicTextField.hidden = YES;
self.displayNameTextFieldTopConstraint.constant = 10;
}
else
{
self.topicTextField.hidden = NO;
self.displayNameTextFieldTopConstraint.constant = 0;
}
}
- (BOOL)isEditing
{
return (super.isEditing || self.topicTextField.isEditing);
}
#pragma mark -
// start with delay
- (void)startTopicAnimation
{
// stop any pending timer
if (animationTimer)
{
[animationTimer invalidate];
animationTimer = nil;
}
// already animated the topic
if (scrollView)
{
return;
}
// compute the text width
UIFont* font = self.topicTextField.font;
// see font description
if (!font)
{
font = [UIFont systemFontOfSize:12];
}
NSDictionary *attributes = @{NSFontAttributeName: font};
CGSize stringSize = CGSizeMake(CGFLOAT_MAX, self.topicTextField.frame.size.height);
stringSize = [self.topicTextField.text boundingRectWithSize:stringSize
options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading
attributes:attributes
context:nil].size;
// does not need to animate the text
if (stringSize.width < self.topicTextField.frame.size.width)
{
return;
}
// put the text in a scrollView to animat it
scrollView = [[UIScrollView alloc] initWithFrame: self.topicTextField.frame];
label = [[UILabel alloc] initWithFrame:self.topicTextField.frame];
label.text = self.topicTextField.text;
label.textColor = self.topicTextField.textColor;
label.font = self.topicTextField.font;
// move to the top left
CGRect topicTextFieldFrame = self.topicTextField.frame;
topicTextFieldFrame.origin = CGPointZero;
label.frame = topicTextFieldFrame;
self.topicTextField.hidden = YES;
[scrollView addSubview:label];
[self insertSubview:scrollView belowSubview:topicTextFieldMaskView];
// update the size
[label sizeToFit];
// offset
CGPoint offset = scrollView.contentOffset;
offset.x = label.frame.size.width - scrollView.frame.size.width;
// duration (magic computation to give more time if the text is longer)
CGFloat duration = label.frame.size.width / scrollView.frame.size.width * 3;
// animate the topic once to display its full content
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionCurveLinear animations:^{
[self->scrollView setContentOffset:offset animated:NO];
} completion:^(BOOL finished)
{
[self stopTopicAnimation];
}];
}
- (BOOL)stopTopicAnimation
{
// stop running timers
if (animationTimer)
{
[animationTimer invalidate];
animationTimer = nil;
}
// if there is an animation is progress
if (scrollView)
{
self.topicTextField.hidden = NO;
[scrollView.layer removeAllAnimations];
[scrollView removeFromSuperview];
scrollView = nil;
label = nil;
[self addSubview:self.topicTextField];
// must be done to be able to restart the animation
// the Z order is not kept
[self bringSubviewToFront:topicTextFieldMaskView];
return YES;
}
return NO;
}
- (void)editTopic
{
[self stopTopicAnimation];
dispatch_async(dispatch_get_main_queue(), ^{
[self.topicTextField becomeFirstResponder];
});
}
- (void)layoutSubviews
{
// add a mask to trap the tap events
// it is faster (and simpliest) than subclassing the scrollview or the textField
// any other gesture could also be trapped here
if (!topicTextFieldMaskView)
{
topicTextFieldMaskView = [[UIView alloc] initWithFrame:self.topicTextField.frame];
topicTextFieldMaskView.backgroundColor = [UIColor clearColor];
[self addSubview:topicTextFieldMaskView];
// tap -> switch to text edition
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(editTopic)];
[tap setNumberOfTouchesRequired:1];
[tap setNumberOfTapsRequired:1];
[tap setDelegate:self];
[topicTextFieldMaskView addGestureRecognizer:tap];
// long tap -> animate the topic
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(startTopicAnimation)];
[topicTextFieldMaskView addGestureRecognizer:longPress];
}
// mother class call
[super layoutSubviews];
}
- (void)setFrame:(CGRect)frame
{
// mother class call
[super setFrame:frame];
// stop any running animation if the frame is updated (screen rotation for example)
if (!CGRectEqualToRect(CGRectIntegral(frame), CGRectIntegral(self.frame)))
{
// stop any running application
[self stopTopicAnimation];
}
// update the mask frame
if (self.topicTextField.hidden)
{
topicTextFieldMaskView.frame = CGRectZero;
}
else
{
topicTextFieldMaskView.frame = self.topicTextField.frame;
}
// topicTextField switches becomes the first responder or it is not anymore the first responder
if (self.topicTextField.isFirstResponder != (topicTextFieldMaskView.hidden))
{
topicTextFieldMaskView.hidden = self.topicTextField.isFirstResponder;
// move topicTextFieldMaskView to the foreground
// when topicTextField has been the first responder, it lets a view over topicTextFieldMaskView
// so restore the expected Z order
if (!topicTextFieldMaskView.hidden)
{
[self bringSubviewToFront:topicTextFieldMaskView];
}
}
}
#pragma mark - UITextField delegate
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
// check if the deleaget allows the edition
if (!self.delegate || [self.delegate roomTitleViewShouldBeginEditing:self])
{
NSString *alertMsg = nil;
if (textField == self.displayNameTextField)
{
// Check whether the user has enough power to rename the room
MXRoomPowerLevels *powerLevels = self.mxRoom.dangerousSyncState.powerLevels;
NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mxRoom.mxSession.myUser.userId];
if (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomName])
{
// Only the room name is edited here, update the text field with the room name
textField.text = self.mxRoom.summary.displayname;
textField.backgroundColor = [UIColor whiteColor];
}
else
{
alertMsg = [MatrixKitL10n roomErrorNameEditionNotAuthorized];
}
// Check whether the user is allowed to change room topic
if (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomTopic])
{
// Show topic text field even if the current value is nil
self.hiddenTopic = NO;
if (alertMsg)
{
// Here the user can only update the room topic, switch on room topic field (without displaying alert)
alertMsg = nil;
[self.topicTextField becomeFirstResponder];
return NO;
}
}
}
else if (textField == self.topicTextField)
{
// Check whether the user has enough power to edit room topic
MXRoomPowerLevels *powerLevels = self.mxRoom.dangerousSyncState.powerLevels;
NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mxRoom.mxSession.myUser.userId];
if (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomTopic])
{
textField.backgroundColor = [UIColor whiteColor];
[self stopTopicAnimation];
}
else
{
alertMsg = [MatrixKitL10n roomErrorTopicEditionNotAuthorized];
}
}
if (alertMsg)
{
// Alert user
__weak typeof(self) weakSelf = self;
if (currentAlert)
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
}
currentAlert = [UIAlertController alertControllerWithTitle:nil message:alertMsg preferredStyle:UIAlertControllerStyleAlert];
[currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n cancel]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
typeof(self) self = weakSelf;
self->currentAlert = nil;
}]];
[self.delegate roomTitleView:self presentAlertController:currentAlert];
return NO;
}
return YES;
}
else
{
return NO;
}
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if (textField == self.topicTextField)
{
textField.backgroundColor = [UIColor clearColor];
NSString *topic = textField.text;
if ((topic.length || self.mxRoom.summary.topic.length) && [topic isEqualToString:self.mxRoom.summary.topic] == NO)
{
if ([self.delegate respondsToSelector:@selector(roomTitleView:isSaving:)])
{
[self.delegate roomTitleView:self isSaving:YES];
}
__weak typeof(self) weakSelf = self;
[self.mxRoom setTopic:topic success:^{
if (weakSelf)
{
typeof(weakSelf)strongSelf = weakSelf;
if ([strongSelf.delegate respondsToSelector:@selector(roomTitleView:isSaving:)])
{
[strongSelf.delegate roomTitleView:strongSelf isSaving:NO];
}
// Hide topic field if empty
strongSelf.hiddenTopic = !textField.text.length;
}
} failure:^(NSError *error) {
if (weakSelf)
{
typeof(weakSelf)strongSelf = weakSelf;
if ([strongSelf.delegate respondsToSelector:@selector(roomTitleView:isSaving:)])
{
[strongSelf.delegate roomTitleView:strongSelf isSaving:NO];
}
// Revert change
NSString *topic = [MXTools stripNewlineCharacters:strongSelf.mxRoom.summary.topic];
textField.text = (topic.length ? topic : nil);
// Hide topic field if empty
strongSelf.hiddenTopic = !textField.text.length;
MXLogDebug(@"[MXKRoomTitleViewWithTopic] Topic room change failed");
// Notify MatrixKit user
NSString *myUserId = strongSelf.mxRoom.mxSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}
}];
}
else
{
// Hide topic field if empty
self.hiddenTopic = !topic.length;
}
}
else
{
// Let super handle displayName text field
[super textFieldDidEndEditing:textField];
}
}
- (BOOL)textFieldShouldReturn:(UITextField*) textField
{
if (textField == self.displayNameTextField)
{
// "Next" key has been pressed
[self.topicTextField becomeFirstResponder];
}
else
{
// "Done" key has been pressed
[textField resignFirstResponder];
}
return YES;
}
@end
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="BkF-x3-7fX" customClass="MXKRoomTitleViewWithTopic">
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Room Name" textAlignment="center" minimumFontSize="14" translatesAutoresizingMaskIntoConstraints="NO" id="6uH-I3-RQg">
<rect key="frame" x="0.0" y="0.0" width="600" height="21"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits" returnKeyType="next"/>
<connections>
<outlet property="delegate" destination="BkF-x3-7fX" id="xX7-jB-9va"/>
</connections>
</textField>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Topic" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Cb9-hb-6f0">
<rect key="frame" x="0.0" y="24" width="600" height="16"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<textInputTraits key="textInputTraits" returnKeyType="done"/>
<connections>
<outlet property="delegate" destination="BkF-x3-7fX" id="8qe-OH-aKS"/>
</connections>
</textField>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="Cb9-hb-6f0" secondAttribute="bottom" constant="4" id="1N3-4y-mDx"/>
<constraint firstAttribute="trailing" secondItem="Cb9-hb-6f0" secondAttribute="trailing" id="G6O-Le-cFA"/>
<constraint firstItem="Cb9-hb-6f0" firstAttribute="leading" secondItem="BkF-x3-7fX" secondAttribute="leading" id="NJY-h8-w2I"/>
<constraint firstItem="6uH-I3-RQg" firstAttribute="top" secondItem="BkF-x3-7fX" secondAttribute="top" id="Piq-rp-Pae"/>
<constraint firstAttribute="trailing" secondItem="6uH-I3-RQg" secondAttribute="trailing" id="WT3-JF-1GG"/>
<constraint firstItem="6uH-I3-RQg" firstAttribute="leading" secondItem="BkF-x3-7fX" secondAttribute="leading" id="czp-h4-Nl6"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="displayNameTextField" destination="6uH-I3-RQg" id="MfX-LQ-C2K"/>
<outlet property="displayNameTextFieldTopConstraint" destination="Piq-rp-Pae" id="jnL-Hz-TWn"/>
<outlet property="topicTextField" destination="Cb9-hb-6f0" id="uRc-er-1Fq"/>
</connections>
</view>
</objects>
</document>