mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-19 16:13:42 +02:00
Merge branch 'gil/SP1_space_creation' into gil/5231_SP3-1_Update_room_settings_for_Spaces
# Conflicts: # Podfile.lock
This commit is contained in:
@@ -125,7 +125,7 @@
|
||||
"notice_room_history_visible_to_members_from_joined_point" = "%@ hat den zukünftigen Raumverlauf für alle Raumteilnehmer ab deren Einladung sichtbar gemacht.";
|
||||
"notice_room_history_visible_to_members_from_joined_point_for_dm" = "%@ hat den zukünftigen Verlauf für alle Raumteilnehmer ab deren Einladung sichtbar gemacht.";
|
||||
"notice_crypto_unable_to_decrypt" = "** Entschlüsselung nicht möglich: %@ **";
|
||||
"notice_crypto_error_unknown_inbound_session_id" = "Die Sitzung des Absenders hat uns keine Schlüssel für diese Nachricht gesendet.";
|
||||
"notice_crypto_error_unknown_inbound_session_id" = "Die absendende Sitzung hat uns keine Schlüssel für diese Nachricht gesendet.";
|
||||
"notice_sticker" = "Aufkleber";
|
||||
"notice_in_reply_to" = "Als Antwort auf";
|
||||
// room display name
|
||||
|
||||
@@ -367,7 +367,7 @@
|
||||
"notice_room_join" = "%@ joined";
|
||||
"notice_room_leave" = "%@ left";
|
||||
"notice_room_reject" = "%@ rejected the invitation";
|
||||
"notice_room_kick" = "%@ kicked %@";
|
||||
"notice_room_kick" = "%@ removed %@";
|
||||
"notice_room_unban" = "%@ unbanned %@";
|
||||
"notice_room_ban" = "%@ banned %@";
|
||||
"notice_room_withdraw" = "%@ withdrew %@'s invitation";
|
||||
@@ -399,7 +399,7 @@
|
||||
"notice_room_join_by_you" = "You joined";
|
||||
"notice_room_leave_by_you" = "You left";
|
||||
"notice_room_reject_by_you" = "You rejected the invitation";
|
||||
"notice_room_kick_by_you" = "You kicked %@";
|
||||
"notice_room_kick_by_you" = "You removed %@";
|
||||
"notice_room_unban_by_you" = "You unbanned %@";
|
||||
"notice_room_ban_by_you" = "You banned %@";
|
||||
"notice_room_withdraw_by_you" = "You withdrew %@'s invitation";
|
||||
@@ -479,7 +479,7 @@
|
||||
"num_members_one" = "%@ user";
|
||||
"num_members_other" = "%@ users";
|
||||
"invite" = "Invite";
|
||||
"kick" = "Kick";
|
||||
"kick" = "Remove from chat";
|
||||
"ban" = "Ban";
|
||||
"unban" = "Un-ban";
|
||||
"message_unsaved_changes" = "There are unsaved changes. Leaving will discard them.";
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
"notice_image_attachment" = "anexo de imagem";
|
||||
"notice_audio_attachment" = "anexo de áudio";
|
||||
"notice_video_attachment" = "anexo de vídeo";
|
||||
"notice_location_attachment" = "anexo de local";
|
||||
"notice_location_attachment" = "anexo de localização";
|
||||
"notice_file_attachment" = "anexo de arquivo";
|
||||
"notice_invalid_attachment" = "anexo inválido";
|
||||
"notice_unsupported_attachment" = "Anexo insuportado: %@";
|
||||
|
||||
@@ -476,3 +476,6 @@
|
||||
"auth_username_in_use" = "Имя пользователя занято";
|
||||
"rename" = "Переименовать";
|
||||
"room_displayname_all_other_members_left" = "%@ (Вышел)";
|
||||
"attachment_unsupported_preview_message" = "Этот тип файла не поддерживается.";
|
||||
"attachment_unsupported_preview_title" = "Не удалось показать предварительный просмотр";
|
||||
"message_reply_to_sender_sent_their_location" = "поделились своим местоположением.";
|
||||
|
||||
@@ -119,9 +119,9 @@
|
||||
|
||||
// Others
|
||||
"user_id_title" = "ID používateľa:";
|
||||
"e2e_passphrase_create" = "Vytvoriť heslo";
|
||||
"e2e_passphrase_confirm" = "Potvrďte heslo";
|
||||
"e2e_passphrase_enter" = "Zadajte heslo";
|
||||
"e2e_passphrase_create" = "Vytvoriť prístupovú frázu";
|
||||
"e2e_passphrase_confirm" = "Potvrďte prístupovú frázu";
|
||||
"e2e_passphrase_enter" = "Zadajte prístupovú frázu";
|
||||
|
||||
// Search
|
||||
"search_no_results" = "Žiadne výsledky";
|
||||
@@ -547,3 +547,4 @@
|
||||
"account_link_email" = "Prepojený email";
|
||||
"account_linked_emails" = "Prepojené e-maily";
|
||||
"room_event_encryption_info_event_fingerprint_key" = "Deklarovaný kľúč odtlačkov prstov Ed25519\n";
|
||||
"notification_settings_notify_all_other" = "Oznámiť pre všetky ostatné správy/miestnosti";
|
||||
|
||||
@@ -259,9 +259,9 @@
|
||||
"user_id_placeholder" = "t.ex.: @sven:hemserver";
|
||||
"ssl_homeserver_url" = "Hemserver-URL: %@";
|
||||
// Permissions
|
||||
"camera_access_not_granted_for_call" = "Videosamtal kräver åtkomst till kameran men %@ har inte behörighet att använda den";
|
||||
"microphone_access_not_granted_for_call" = "Samtal kräver åtkomst till mikrofonen men %@ har inte behörighet att använda den";
|
||||
"local_contacts_access_not_granted" = "Upptäckt av användare från lokala kontakter kräver åtkomst till dina kontakter men %@ har inte behörighet att komma åt dem";
|
||||
"camera_access_not_granted_for_call" = "Videosamtal kräver åtkomst till kameran men %@ är inte behörig att använda den";
|
||||
"microphone_access_not_granted_for_call" = "Samtal kräver åtkomst till mikrofonen men %@ är inte behörig att använda den";
|
||||
"local_contacts_access_not_granted" = "Upptäckt av användare från lokala kontakter kräver åtkomst till dina kontakter men %@ är inte behörig att komma åt dem";
|
||||
"local_contacts_access_discovery_warning_title" = "Användarupptäckt";
|
||||
"local_contacts_access_discovery_warning" = "För att upptäcka kontakter som redan använder Matrix kan %@ skicka e-postadresser och telefonnummer i din adressbok till din valda Matrixidentitetsserver. Där det stöds hashas personuppgifter innan de skickas - kontrollera din identitetsservers integritetspolicy för mer information.";
|
||||
// Country picker
|
||||
@@ -462,7 +462,7 @@
|
||||
"call_voice_with_user" = "Röstsamtal med %@";
|
||||
"call_ringing" = "Ringer…";
|
||||
"e2e_passphrase_too_short" = "Lösenfras för kort (den måste vara minst %d tecken långt)";
|
||||
"microphone_access_not_granted_for_voice_message" = "Röstmeddelanden kräver åtkomst till mikrofonen, men %@ har inte behörighet att använda den";
|
||||
"microphone_access_not_granted_for_voice_message" = "Röstmeddelanden kräver åtkomst till mikrofonen, men %@ är inte behörig att använda den";
|
||||
"message_reply_to_sender_sent_a_voice_message" = "skickade ett röstmeddelande.";
|
||||
"attachment_large_with_resolution" = "Stor %@ (~%@)";
|
||||
"attachment_medium_with_resolution" = "Mellan %@ (~%@)";
|
||||
|
||||
@@ -29,6 +29,11 @@
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
if ([self providesCustomActivityIndicator]) {
|
||||
// If a subclass provides custom activity indicator, the default one will not even be initialized.
|
||||
return;
|
||||
}
|
||||
|
||||
// Add default activity indicator
|
||||
activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
|
||||
activityIndicator.backgroundColor = [UIColor colorWithRed:0.8 green:0.8 blue:0.8 alpha:1.0];
|
||||
@@ -56,9 +61,13 @@
|
||||
|
||||
#pragma mark - Activity indicator
|
||||
|
||||
- (BOOL)providesCustomActivityIndicator {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)startActivityIndicator
|
||||
{
|
||||
if (activityIndicator)
|
||||
if (activityIndicator && ![self providesCustomActivityIndicator])
|
||||
{
|
||||
[self.view bringSubviewToFront:activityIndicator];
|
||||
[activityIndicator startAnimating];
|
||||
@@ -79,5 +88,4 @@
|
||||
[activityIndicator stopAnimating];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -175,7 +175,11 @@
|
||||
_attachmentsCollection.hidden = YES;
|
||||
|
||||
// Display collection cell in full screen
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
self.automaticallyAdjustsScrollViewInsets = NO;
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
- (BOOL)prefersStatusBarHidden
|
||||
|
||||
@@ -215,6 +215,8 @@
|
||||
// and report the inputAccessoryView.superview of the firstResponder in self.keyboardView.
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
- (void)setKeyboardHeight:(CGFloat)keyboardHeight
|
||||
{
|
||||
// Deduce the bottom inset for the scroll view (Don't forget the potential tabBar)
|
||||
@@ -229,6 +231,7 @@
|
||||
insets.bottom = scrollViewInsetBottom;
|
||||
self.authenticationScrollView.contentInset = insets;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
@@ -981,7 +984,10 @@
|
||||
{
|
||||
// Trigger here a register request in order to associate the filled userId and password to the current session id
|
||||
// This will check the availability of the userId at the same time
|
||||
NSDictionary *parameters = @{@"auth": @{},
|
||||
NSDictionary *parameters = @{@"auth": @{
|
||||
@"session": self.authInputsView.authSession.session,
|
||||
@"type": kMXLoginFlowTypeDummy
|
||||
},
|
||||
@"username": self.authInputsView.userId,
|
||||
@"password": self.authInputsView.password,
|
||||
@"bind_email": @(NO),
|
||||
|
||||
@@ -1422,29 +1422,31 @@ static const CGFloat kLocalPreviewMargin = 20;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateProximityAndSleep
|
||||
{
|
||||
BOOL inCall = (mxCall.state == MXCallStateConnected || mxCall.state == MXCallStateRinging || mxCall.state == MXCallStateInviteSent || mxCall.state == MXCallStateConnecting || mxCall.state == MXCallStateCreateOffer || mxCall.state == MXCallStateCreateAnswer);
|
||||
|
||||
if (inCall)
|
||||
{
|
||||
BOOL isBuiltInReceiverUsed = self.isBuiltInReceiverAudioOuput;
|
||||
|
||||
// Enable the proximity monitoring when the built in receiver is used as the audio output.
|
||||
BOOL enableProxMonitoring = isBuiltInReceiverUsed;
|
||||
[[UIDevice currentDevice] setProximityMonitoringEnabled:enableProxMonitoring];
|
||||
|
||||
// Disable the idle timer during a video call, or during a voice call which is performed with the built-in receiver.
|
||||
// Note: if the device is locked, VoIP calling get dropped if an incoming GSM call is received.
|
||||
BOOL disableIdleTimer = mxCall.isVideoCall || isBuiltInReceiverUsed;
|
||||
|
||||
UIApplication *sharedApplication = [UIApplication performSelector:@selector(sharedApplication)];
|
||||
if (sharedApplication)
|
||||
{
|
||||
sharedApplication.idleTimerDisabled = disableIdleTimer;
|
||||
}
|
||||
}
|
||||
}
|
||||
- (void)updateProximityAndSleep
|
||||
{
|
||||
BOOL inCall = (mxCall.state == MXCallStateConnected || mxCall.state == MXCallStateRinging || mxCall.state == MXCallStateInviteSent || mxCall.state == MXCallStateConnecting || mxCall.state == MXCallStateCreateOffer || mxCall.state == MXCallStateCreateAnswer);
|
||||
|
||||
BOOL isBuiltInReceiverUsed = self.isBuiltInReceiverAudioOuput;
|
||||
|
||||
// Enable the proximity monitoring when the built in receiver is used as the audio output.
|
||||
BOOL enableProxMonitoring = inCall && isBuiltInReceiverUsed;
|
||||
|
||||
UIDevice *device = [UIDevice currentDevice];
|
||||
if (device && device.isProximityMonitoringEnabled != enableProxMonitoring)
|
||||
{
|
||||
[device setProximityMonitoringEnabled:enableProxMonitoring];
|
||||
}
|
||||
|
||||
// Disable the idle timer during a video call, or during a voice call which is performed with the built-in receiver.
|
||||
// Note: if the device is locked, VoIP calling get dropped if an incoming GSM call is received.
|
||||
BOOL disableIdleTimer = inCall && (mxCall.isVideoCall || isBuiltInReceiverUsed);
|
||||
|
||||
UIApplication *sharedApplication = [UIApplication performSelector:@selector(sharedApplication)];
|
||||
if (sharedApplication && sharedApplication.isIdleTimerDisabled != disableIdleTimer)
|
||||
{
|
||||
sharedApplication.idleTimerDisabled = disableIdleTimer;
|
||||
}
|
||||
}
|
||||
|
||||
- (UIView *)createIncomingCallView
|
||||
{
|
||||
|
||||
@@ -152,7 +152,7 @@ NSString* const kMXKCountryPickerViewControllerCountryCellId = @"kMXKCountryPick
|
||||
{
|
||||
UISearchController *searchController = [[UISearchController alloc]
|
||||
initWithSearchResultsController:nil];
|
||||
searchController.dimsBackgroundDuringPresentation = NO;
|
||||
searchController.obscuresBackgroundDuringPresentation = NO;
|
||||
searchController.hidesNavigationBarDuringPresentation = NO;
|
||||
searchController.searchResultsUpdater = self;
|
||||
|
||||
|
||||
@@ -92,6 +92,8 @@
|
||||
[[[self class] nib] instantiateWithOwner:self options:nil];
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
// Adjust search bar Top constraint to take into account potential navBar.
|
||||
if (_groupsSearchBarTopConstraint)
|
||||
{
|
||||
@@ -123,6 +125,7 @@
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[_groupsTableViewBottomConstraint]];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
// Hide search bar by default
|
||||
[self hideSearchBar:YES];
|
||||
@@ -209,6 +212,8 @@
|
||||
self.keyboardView = _groupsSearchBar.inputAccessoryView.superview;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
- (void)setKeyboardHeight:(CGFloat)keyboardHeight
|
||||
{
|
||||
// Deduce the bottom constraint for the table view (Don't forget the potential tabBar)
|
||||
@@ -225,6 +230,7 @@
|
||||
// Force layout immediately to take into account new constraint
|
||||
[self.view layoutIfNeeded];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
|
||||
@@ -160,7 +160,7 @@ NSString* const kMXKLanguagePickerCellDataKeyLanguage = @"language";
|
||||
{
|
||||
UISearchController *searchController = [[UISearchController alloc]
|
||||
initWithSearchResultsController:nil];
|
||||
searchController.dimsBackgroundDuringPresentation = NO;
|
||||
searchController.obscuresBackgroundDuringPresentation = NO;
|
||||
searchController.hidesNavigationBarDuringPresentation = NO;
|
||||
searchController.searchResultsUpdater = self;
|
||||
|
||||
|
||||
@@ -97,6 +97,8 @@
|
||||
[[[self class] nib] instantiateWithOwner:self options:nil];
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
// Adjust search bar Top constraint to take into account potential navBar.
|
||||
if (_recentsSearchBarTopConstraint)
|
||||
{
|
||||
@@ -126,6 +128,7 @@
|
||||
|
||||
_recentsTableViewBottomConstraint.active = YES;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
// Hide search bar by default
|
||||
[self hideSearchBar:YES];
|
||||
@@ -217,7 +220,7 @@
|
||||
if ([MXKRoomDataSourceManager sharedManagerForMatrixSession:mxSession].isServerSyncInProgress)
|
||||
{
|
||||
// sync is in progress for at least one data source, keep running the loading wheel
|
||||
[self.activityIndicator startAnimating];
|
||||
[self startActivityIndicator];
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -229,6 +232,8 @@
|
||||
self.keyboardView = _recentsSearchBar.inputAccessoryView.superview;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
- (void)setKeyboardHeight:(CGFloat)keyboardHeight
|
||||
{
|
||||
// Deduce the bottom constraint for the table view (Don't forget the potential tabBar)
|
||||
@@ -245,6 +250,7 @@
|
||||
// Force layout immediately to take into account new constraint
|
||||
[self.view layoutIfNeeded];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
@@ -421,7 +427,9 @@
|
||||
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
// Let dataSource provide the section header.
|
||||
return [dataSource viewForHeaderInSection:section withFrame:[tableView rectForHeaderInSection:section]];
|
||||
return [dataSource viewForHeaderInSection:section
|
||||
withFrame:[tableView rectForHeaderInSection:section]
|
||||
inTableView:tableView];
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
|
||||
@@ -105,6 +105,8 @@
|
||||
// Adjust Top and Bottom constraints to take into account potential navBar and tabBar.
|
||||
[NSLayoutConstraint deactivateConstraints:@[_membersSearchBarTopConstraint, _membersTableViewBottomConstraint]];
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
_membersSearchBarTopConstraint = [NSLayoutConstraint constraintWithItem:self.topLayoutGuide
|
||||
attribute:NSLayoutAttributeBottom
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
@@ -120,6 +122,7 @@
|
||||
attribute:NSLayoutAttributeBottom
|
||||
multiplier:1.0f
|
||||
constant:0.0f];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[_membersSearchBarTopConstraint, _membersTableViewBottomConstraint]];
|
||||
|
||||
@@ -225,6 +228,8 @@
|
||||
self.keyboardView = _membersSearchBar.inputAccessoryView.superview;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
- (void)setKeyboardHeight:(CGFloat)keyboardHeight
|
||||
{
|
||||
// Deduce the bottom constraint for the table view (Don't forget the potential tabBar)
|
||||
@@ -241,6 +246,7 @@
|
||||
// Force layout immediately to take into account new constraint
|
||||
[self.view layoutIfNeeded];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
|
||||
@@ -76,6 +76,8 @@
|
||||
// Adjust Top and Bottom constraints to take into account potential navBar and tabBar.
|
||||
[NSLayoutConstraint deactivateConstraints:@[_searchSearchBarTopConstraint, _searchTableViewBottomConstraint]];
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
_searchSearchBarTopConstraint = [NSLayoutConstraint constraintWithItem:self.topLayoutGuide
|
||||
attribute:NSLayoutAttributeBottom
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
@@ -91,6 +93,7 @@
|
||||
attribute:NSLayoutAttributeBottom
|
||||
multiplier:1.0f
|
||||
constant:0.0f];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[_searchSearchBarTopConstraint, _searchTableViewBottomConstraint]];
|
||||
|
||||
@@ -140,6 +143,8 @@
|
||||
self.keyboardView = _searchSearchBar.inputAccessoryView.superview;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
- (void)setKeyboardHeight:(CGFloat)keyboardHeight
|
||||
{
|
||||
// Deduce the bottom constraint for the table view (Don't forget the potential tabBar)
|
||||
@@ -156,6 +161,7 @@
|
||||
// Force layout immediately to take into account new constraint
|
||||
[self.view layoutIfNeeded];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
|
||||
@@ -35,6 +35,14 @@
|
||||
*/
|
||||
@property (nonatomic) UIActivityIndicatorView *activityIndicator;
|
||||
|
||||
/**
|
||||
A view controller may choose to implement a completely custom activity indicator (e.g. shared toast notification),
|
||||
|
||||
In this case the default `activityIndicator` will be hidden, and the view controller is responsible for overriding
|
||||
`startActivityIndicator` and `stopActivityIndicator` methods to show / hide the custom activity indicator.
|
||||
*/
|
||||
@property (nonatomic, readonly) BOOL providesCustomActivityIndicator;
|
||||
|
||||
/**
|
||||
Bring the activity indicator to the front and start it.
|
||||
*/
|
||||
|
||||
@@ -133,6 +133,9 @@ NSString *const kMXKWebViewViewControllerJavaScriptEnableLog =
|
||||
multiplier:1.0
|
||||
constant:0];
|
||||
// Force webview in full height
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:webView
|
||||
attribute:NSLayoutAttributeTop
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
@@ -147,6 +150,7 @@ NSString *const kMXKWebViewViewControllerJavaScriptEnableLog =
|
||||
attribute:NSLayoutAttributeTop
|
||||
multiplier:1.0
|
||||
constant:0];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[leftConstraint, rightConstraint, topConstraint, bottomConstraint]];
|
||||
|
||||
|
||||
@@ -104,7 +104,9 @@ public class UTI: RawRepresentable, Equatable {
|
||||
// UTTypeCreatePreferredIdentifierForTag only returns nil if the tag class is unknwown, which can't happen to us since we use an
|
||||
// enum of known values. Hence we can force-cast the result.
|
||||
|
||||
// swiftlint:disable force_unwrapping
|
||||
let identifier = (unmanagedIdentifier?.takeRetainedValue() as String?)!
|
||||
// swiftlint:enable force_unwrapping
|
||||
|
||||
self.init(rawValue: identifier)
|
||||
}
|
||||
@@ -122,7 +124,7 @@ public class UTI: RawRepresentable, Equatable {
|
||||
|
||||
public convenience init(withExtension fileExtension: String, conformingTo conforming: UTI? = nil) {
|
||||
|
||||
self.init(withTagClass:.fileExtension, value: fileExtension, conformingTo: conforming)
|
||||
self.init(withTagClass: .fileExtension, value: fileExtension, conformingTo: conforming)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,7 +140,7 @@ public class UTI: RawRepresentable, Equatable {
|
||||
|
||||
public convenience init(withMimeType mimeType: String, conformingTo conforming: UTI? = nil) {
|
||||
|
||||
self.init(withTagClass:.mimeType, value: mimeType, conformingTo: conforming)
|
||||
self.init(withTagClass: .mimeType, value: mimeType, conformingTo: conforming)
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
@@ -146,7 +148,9 @@ public class UTI: RawRepresentable, Equatable {
|
||||
/**
|
||||
|
||||
Initialize an UTI with a pasteboard type.
|
||||
- Important: **This function is de-facto deprecated!** The old cocoa pasteboard types ( `NSStringPboardType`, `NSPDFPboardType`, etc) have been deprecated in favour of actual UTIs, and the constants are not available anymore in Swift. This function only works correctly with the values of these old constants, but _not_ with the replacement values (like `NSPasteboardTypeString` etc), since these already are UTIs.
|
||||
- Important: **This function is de-facto deprecated!** The old cocoa pasteboard types ( `NSStringPboardType`, `NSPDFPboardType`, etc) have been deprecated in favour of actual UTIs, and the
|
||||
constants are not available anymore in Swift. This function only works correctly with the values of these old constants, but _not_ with the replacement values (like `NSPasteboardTypeString` etc), since these
|
||||
already are UTIs.
|
||||
- Parameters:
|
||||
- pbType: The pasteboard type (e.g. NSPDFPboardType).
|
||||
- conformingTo: If specified, the returned UTI must conform to this UTI. If nil is specified, this parameter is ignored. The default is nil.
|
||||
@@ -155,7 +159,7 @@ public class UTI: RawRepresentable, Equatable {
|
||||
*/
|
||||
public convenience init(withPBType pbType: String, conformingTo conforming: UTI? = nil) {
|
||||
|
||||
self.init(withTagClass:.pbType, value: pbType, conformingTo: conforming)
|
||||
self.init(withTagClass: .pbType, value: pbType, conformingTo: conforming)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,7 +176,7 @@ public class UTI: RawRepresentable, Equatable {
|
||||
|
||||
public convenience init(withOSType osType: String, conformingTo conforming: UTI? = nil) {
|
||||
|
||||
self.init(withTagClass:.osType, value: osType, conformingTo: conforming)
|
||||
self.init(withTagClass: .osType, value: osType, conformingTo: conforming)
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -297,7 +301,7 @@ public class UTI: RawRepresentable, Equatable {
|
||||
return UTTypeConformsTo(self.rawCFValue, otherUTI.rawCFValue) as Bool
|
||||
}
|
||||
|
||||
public static func ==(lhs: UTI, rhs: UTI) -> Bool {
|
||||
public static func == (lhs: UTI, rhs: UTI) -> Bool {
|
||||
|
||||
return UTTypeEqual(lhs.rawCFValue, rhs.rawCFValue) as Bool
|
||||
}
|
||||
@@ -319,11 +323,11 @@ public class UTI: RawRepresentable, Equatable {
|
||||
|
||||
/// Returns a uniform type’s declaration as a Dictionary, or nil if if no declaration for that type can be found.
|
||||
|
||||
public var declaration: [AnyHashable:Any]? {
|
||||
public var declaration: [AnyHashable: Any]? {
|
||||
|
||||
let unmanagedDeclaration = UTTypeCopyDeclaration(self.rawCFValue)
|
||||
|
||||
guard let declaration = unmanagedDeclaration?.takeRetainedValue() as? [AnyHashable:Any] else {
|
||||
guard let declaration = unmanagedDeclaration?.takeRetainedValue() as? [AnyHashable: Any] else {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -356,137 +360,137 @@ public class UTI: RawRepresentable, Equatable {
|
||||
|
||||
public extension UTI {
|
||||
|
||||
static let item = UTI(rawValue: kUTTypeItem as String)
|
||||
static let content = UTI(rawValue: kUTTypeContent as String)
|
||||
static let compositeContent = UTI(rawValue: kUTTypeCompositeContent as String)
|
||||
static let message = UTI(rawValue: kUTTypeMessage as String)
|
||||
static let contact = UTI(rawValue: kUTTypeContact as String)
|
||||
static let archive = UTI(rawValue: kUTTypeArchive as String)
|
||||
static let diskImage = UTI(rawValue: kUTTypeDiskImage as String)
|
||||
static let data = UTI(rawValue: kUTTypeData as String)
|
||||
static let directory = UTI(rawValue: kUTTypeDirectory as String)
|
||||
static let resolvable = UTI(rawValue: kUTTypeResolvable as String)
|
||||
static let symLink = UTI(rawValue: kUTTypeSymLink as String)
|
||||
static let executable = UTI(rawValue: kUTTypeExecutable as String)
|
||||
static let mountPoint = UTI(rawValue: kUTTypeMountPoint as String)
|
||||
static let aliasFile = UTI(rawValue: kUTTypeAliasFile as String)
|
||||
static let aliasRecord = UTI(rawValue: kUTTypeAliasRecord as String)
|
||||
static let urlBookmarkData = UTI(rawValue: kUTTypeURLBookmarkData as String)
|
||||
static let url = UTI(rawValue: kUTTypeURL as String)
|
||||
static let fileURL = UTI(rawValue: kUTTypeFileURL as String)
|
||||
static let text = UTI(rawValue: kUTTypeText as String)
|
||||
static let plainText = UTI(rawValue: kUTTypePlainText as String)
|
||||
static let utf8PlainText = UTI(rawValue: kUTTypeUTF8PlainText as String)
|
||||
static let utf16ExternalPlainText = UTI(rawValue: kUTTypeUTF16ExternalPlainText as String)
|
||||
static let utf16PlainText = UTI(rawValue: kUTTypeUTF16PlainText as String)
|
||||
static let delimitedText = UTI(rawValue: kUTTypeDelimitedText as String)
|
||||
static let commaSeparatedText = UTI(rawValue: kUTTypeCommaSeparatedText as String)
|
||||
static let tabSeparatedText = UTI(rawValue: kUTTypeTabSeparatedText as String)
|
||||
static let utf8TabSeparatedText = UTI(rawValue: kUTTypeUTF8TabSeparatedText as String)
|
||||
static let rtf = UTI(rawValue: kUTTypeRTF as String)
|
||||
static let html = UTI(rawValue: kUTTypeHTML as String)
|
||||
static let xml = UTI(rawValue: kUTTypeXML as String)
|
||||
static let sourceCode = UTI(rawValue: kUTTypeSourceCode as String)
|
||||
static let assemblyLanguageSource = UTI(rawValue: kUTTypeAssemblyLanguageSource as String)
|
||||
static let cSource = UTI(rawValue: kUTTypeCSource as String)
|
||||
static let objectiveCSource = UTI(rawValue: kUTTypeObjectiveCSource as String)
|
||||
static let item = UTI(rawValue: kUTTypeItem as String)
|
||||
static let content = UTI(rawValue: kUTTypeContent as String)
|
||||
static let compositeContent = UTI(rawValue: kUTTypeCompositeContent as String)
|
||||
static let message = UTI(rawValue: kUTTypeMessage as String)
|
||||
static let contact = UTI(rawValue: kUTTypeContact as String)
|
||||
static let archive = UTI(rawValue: kUTTypeArchive as String)
|
||||
static let diskImage = UTI(rawValue: kUTTypeDiskImage as String)
|
||||
static let data = UTI(rawValue: kUTTypeData as String)
|
||||
static let directory = UTI(rawValue: kUTTypeDirectory as String)
|
||||
static let resolvable = UTI(rawValue: kUTTypeResolvable as String)
|
||||
static let symLink = UTI(rawValue: kUTTypeSymLink as String)
|
||||
static let executable = UTI(rawValue: kUTTypeExecutable as String)
|
||||
static let mountPoint = UTI(rawValue: kUTTypeMountPoint as String)
|
||||
static let aliasFile = UTI(rawValue: kUTTypeAliasFile as String)
|
||||
static let aliasRecord = UTI(rawValue: kUTTypeAliasRecord as String)
|
||||
static let urlBookmarkData = UTI(rawValue: kUTTypeURLBookmarkData as String)
|
||||
static let url = UTI(rawValue: kUTTypeURL as String)
|
||||
static let fileURL = UTI(rawValue: kUTTypeFileURL as String)
|
||||
static let text = UTI(rawValue: kUTTypeText as String)
|
||||
static let plainText = UTI(rawValue: kUTTypePlainText as String)
|
||||
static let utf8PlainText = UTI(rawValue: kUTTypeUTF8PlainText as String)
|
||||
static let utf16ExternalPlainText = UTI(rawValue: kUTTypeUTF16ExternalPlainText as String)
|
||||
static let utf16PlainText = UTI(rawValue: kUTTypeUTF16PlainText as String)
|
||||
static let delimitedText = UTI(rawValue: kUTTypeDelimitedText as String)
|
||||
static let commaSeparatedText = UTI(rawValue: kUTTypeCommaSeparatedText as String)
|
||||
static let tabSeparatedText = UTI(rawValue: kUTTypeTabSeparatedText as String)
|
||||
static let utf8TabSeparatedText = UTI(rawValue: kUTTypeUTF8TabSeparatedText as String)
|
||||
static let rtf = UTI(rawValue: kUTTypeRTF as String)
|
||||
static let html = UTI(rawValue: kUTTypeHTML as String)
|
||||
static let xml = UTI(rawValue: kUTTypeXML as String)
|
||||
static let sourceCode = UTI(rawValue: kUTTypeSourceCode as String)
|
||||
static let assemblyLanguageSource = UTI(rawValue: kUTTypeAssemblyLanguageSource as String)
|
||||
static let cSource = UTI(rawValue: kUTTypeCSource as String)
|
||||
static let objectiveCSource = UTI(rawValue: kUTTypeObjectiveCSource as String)
|
||||
@available( OSX 10.11, iOS 9.0, * )
|
||||
static let swiftSource = UTI(rawValue: kUTTypeSwiftSource as String)
|
||||
static let cPlusPlusSource = UTI(rawValue: kUTTypeCPlusPlusSource as String)
|
||||
static let objectiveCPlusPlusSource = UTI(rawValue: kUTTypeObjectiveCPlusPlusSource as String)
|
||||
static let cHeader = UTI(rawValue: kUTTypeCHeader as String)
|
||||
static let cPlusPlusHeader = UTI(rawValue: kUTTypeCPlusPlusHeader as String)
|
||||
static let javaSource = UTI(rawValue: kUTTypeJavaSource as String)
|
||||
static let script = UTI(rawValue: kUTTypeScript as String)
|
||||
static let appleScript = UTI(rawValue: kUTTypeAppleScript as String)
|
||||
static let osaScript = UTI(rawValue: kUTTypeOSAScript as String)
|
||||
static let osaScriptBundle = UTI(rawValue: kUTTypeOSAScriptBundle as String)
|
||||
static let javaScript = UTI(rawValue: kUTTypeJavaScript as String)
|
||||
static let shellScript = UTI(rawValue: kUTTypeShellScript as String)
|
||||
static let perlScript = UTI(rawValue: kUTTypePerlScript as String)
|
||||
static let pythonScript = UTI(rawValue: kUTTypePythonScript as String)
|
||||
static let rubyScript = UTI(rawValue: kUTTypeRubyScript as String)
|
||||
static let phpScript = UTI(rawValue: kUTTypePHPScript as String)
|
||||
static let json = UTI(rawValue: kUTTypeJSON as String)
|
||||
static let propertyList = UTI(rawValue: kUTTypePropertyList as String)
|
||||
static let xmlPropertyList = UTI(rawValue: kUTTypeXMLPropertyList as String)
|
||||
static let binaryPropertyList = UTI(rawValue: kUTTypeBinaryPropertyList as String)
|
||||
static let pdf = UTI(rawValue: kUTTypePDF as String)
|
||||
static let rtfd = UTI(rawValue: kUTTypeRTFD as String)
|
||||
static let flatRTFD = UTI(rawValue: kUTTypeFlatRTFD as String)
|
||||
static let txnTextAndMultimediaData = UTI(rawValue: kUTTypeTXNTextAndMultimediaData as String)
|
||||
static let webArchive = UTI(rawValue: kUTTypeWebArchive as String)
|
||||
static let image = UTI(rawValue: kUTTypeImage as String)
|
||||
static let jpeg = UTI(rawValue: kUTTypeJPEG as String)
|
||||
static let jpeg2000 = UTI(rawValue: kUTTypeJPEG2000 as String)
|
||||
static let tiff = UTI(rawValue: kUTTypeTIFF as String)
|
||||
static let pict = UTI(rawValue: kUTTypePICT as String)
|
||||
static let gif = UTI(rawValue: kUTTypeGIF as String)
|
||||
static let png = UTI(rawValue: kUTTypePNG as String)
|
||||
static let quickTimeImage = UTI(rawValue: kUTTypeQuickTimeImage as String)
|
||||
static let appleICNS = UTI(rawValue: kUTTypeAppleICNS as String)
|
||||
static let bmp = UTI(rawValue: kUTTypeBMP as String)
|
||||
static let ico = UTI(rawValue: kUTTypeICO as String)
|
||||
static let rawImage = UTI(rawValue: kUTTypeRawImage as String)
|
||||
static let scalableVectorGraphics = UTI(rawValue: kUTTypeScalableVectorGraphics as String)
|
||||
static let swiftSource = UTI(rawValue: kUTTypeSwiftSource as String)
|
||||
static let cPlusPlusSource = UTI(rawValue: kUTTypeCPlusPlusSource as String)
|
||||
static let objectiveCPlusPlusSource = UTI(rawValue: kUTTypeObjectiveCPlusPlusSource as String)
|
||||
static let cHeader = UTI(rawValue: kUTTypeCHeader as String)
|
||||
static let cPlusPlusHeader = UTI(rawValue: kUTTypeCPlusPlusHeader as String)
|
||||
static let javaSource = UTI(rawValue: kUTTypeJavaSource as String)
|
||||
static let script = UTI(rawValue: kUTTypeScript as String)
|
||||
static let appleScript = UTI(rawValue: kUTTypeAppleScript as String)
|
||||
static let osaScript = UTI(rawValue: kUTTypeOSAScript as String)
|
||||
static let osaScriptBundle = UTI(rawValue: kUTTypeOSAScriptBundle as String)
|
||||
static let javaScript = UTI(rawValue: kUTTypeJavaScript as String)
|
||||
static let shellScript = UTI(rawValue: kUTTypeShellScript as String)
|
||||
static let perlScript = UTI(rawValue: kUTTypePerlScript as String)
|
||||
static let pythonScript = UTI(rawValue: kUTTypePythonScript as String)
|
||||
static let rubyScript = UTI(rawValue: kUTTypeRubyScript as String)
|
||||
static let phpScript = UTI(rawValue: kUTTypePHPScript as String)
|
||||
static let json = UTI(rawValue: kUTTypeJSON as String)
|
||||
static let propertyList = UTI(rawValue: kUTTypePropertyList as String)
|
||||
static let xmlPropertyList = UTI(rawValue: kUTTypeXMLPropertyList as String)
|
||||
static let binaryPropertyList = UTI(rawValue: kUTTypeBinaryPropertyList as String)
|
||||
static let pdf = UTI(rawValue: kUTTypePDF as String)
|
||||
static let rtfd = UTI(rawValue: kUTTypeRTFD as String)
|
||||
static let flatRTFD = UTI(rawValue: kUTTypeFlatRTFD as String)
|
||||
static let txnTextAndMultimediaData = UTI(rawValue: kUTTypeTXNTextAndMultimediaData as String)
|
||||
static let webArchive = UTI(rawValue: kUTTypeWebArchive as String)
|
||||
static let image = UTI(rawValue: kUTTypeImage as String)
|
||||
static let jpeg = UTI(rawValue: kUTTypeJPEG as String)
|
||||
static let jpeg2000 = UTI(rawValue: kUTTypeJPEG2000 as String)
|
||||
static let tiff = UTI(rawValue: kUTTypeTIFF as String)
|
||||
static let pict = UTI(rawValue: kUTTypePICT as String)
|
||||
static let gif = UTI(rawValue: kUTTypeGIF as String)
|
||||
static let png = UTI(rawValue: kUTTypePNG as String)
|
||||
static let quickTimeImage = UTI(rawValue: kUTTypeQuickTimeImage as String)
|
||||
static let appleICNS = UTI(rawValue: kUTTypeAppleICNS as String)
|
||||
static let bmp = UTI(rawValue: kUTTypeBMP as String)
|
||||
static let ico = UTI(rawValue: kUTTypeICO as String)
|
||||
static let rawImage = UTI(rawValue: kUTTypeRawImage as String)
|
||||
static let scalableVectorGraphics = UTI(rawValue: kUTTypeScalableVectorGraphics as String)
|
||||
@available(OSX 10.12, iOS 9.1, watchOS 2.1, *)
|
||||
static let livePhoto = UTI(rawValue: kUTTypeLivePhoto as String)
|
||||
static let livePhoto = UTI(rawValue: kUTTypeLivePhoto as String)
|
||||
@available(OSX 10.12, iOS 9.1, *)
|
||||
static let audiovisualContent = UTI(rawValue: kUTTypeAudiovisualContent as String)
|
||||
static let movie = UTI(rawValue: kUTTypeMovie as String)
|
||||
static let video = UTI(rawValue: kUTTypeVideo as String)
|
||||
static let audio = UTI(rawValue: kUTTypeAudio as String)
|
||||
static let quickTimeMovie = UTI(rawValue: kUTTypeQuickTimeMovie as String)
|
||||
static let mpeg = UTI(rawValue: kUTTypeMPEG as String)
|
||||
static let mpeg2Video = UTI(rawValue: kUTTypeMPEG2Video as String)
|
||||
static let mpeg2TransportStream = UTI(rawValue: kUTTypeMPEG2TransportStream as String)
|
||||
static let mp3 = UTI(rawValue: kUTTypeMP3 as String)
|
||||
static let mpeg4 = UTI(rawValue: kUTTypeMPEG4 as String)
|
||||
static let mpeg4Audio = UTI(rawValue: kUTTypeMPEG4Audio as String)
|
||||
static let appleProtectedMPEG4Audio = UTI(rawValue: kUTTypeAppleProtectedMPEG4Audio as String)
|
||||
static let appleProtectedMPEG4Video = UTI(rawValue: kUTTypeAppleProtectedMPEG4Video as String)
|
||||
static let aviMovie = UTI(rawValue: kUTTypeAVIMovie as String)
|
||||
static let audioInterchangeFileFormat = UTI(rawValue: kUTTypeAudioInterchangeFileFormat as String)
|
||||
static let waveformAudio = UTI(rawValue: kUTTypeWaveformAudio as String)
|
||||
static let midiAudio = UTI(rawValue: kUTTypeMIDIAudio as String)
|
||||
static let playlist = UTI(rawValue: kUTTypePlaylist as String)
|
||||
static let m3UPlaylist = UTI(rawValue: kUTTypeM3UPlaylist as String)
|
||||
static let folder = UTI(rawValue: kUTTypeFolder as String)
|
||||
static let volume = UTI(rawValue: kUTTypeVolume as String)
|
||||
static let package = UTI(rawValue: kUTTypePackage as String)
|
||||
static let bundle = UTI(rawValue: kUTTypeBundle as String)
|
||||
static let pluginBundle = UTI(rawValue: kUTTypePluginBundle as String)
|
||||
static let spotlightImporter = UTI(rawValue: kUTTypeSpotlightImporter as String)
|
||||
static let quickLookGenerator = UTI(rawValue: kUTTypeQuickLookGenerator as String)
|
||||
static let xpcService = UTI(rawValue: kUTTypeXPCService as String)
|
||||
static let framework = UTI(rawValue: kUTTypeFramework as String)
|
||||
static let application = UTI(rawValue: kUTTypeApplication as String)
|
||||
static let applicationBundle = UTI(rawValue: kUTTypeApplicationBundle as String)
|
||||
static let applicationFile = UTI(rawValue: kUTTypeApplicationFile as String)
|
||||
static let unixExecutable = UTI(rawValue: kUTTypeUnixExecutable as String)
|
||||
static let windowsExecutable = UTI(rawValue: kUTTypeWindowsExecutable as String)
|
||||
static let javaClass = UTI(rawValue: kUTTypeJavaClass as String)
|
||||
static let javaArchive = UTI(rawValue: kUTTypeJavaArchive as String)
|
||||
static let systemPreferencesPane = UTI(rawValue: kUTTypeSystemPreferencesPane as String)
|
||||
static let gnuZipArchive = UTI(rawValue: kUTTypeGNUZipArchive as String)
|
||||
static let bzip2Archive = UTI(rawValue: kUTTypeBzip2Archive as String)
|
||||
static let zipArchive = UTI(rawValue: kUTTypeZipArchive as String)
|
||||
static let spreadsheet = UTI(rawValue: kUTTypeSpreadsheet as String)
|
||||
static let presentation = UTI(rawValue: kUTTypePresentation as String)
|
||||
static let database = UTI(rawValue: kUTTypeDatabase as String)
|
||||
static let vCard = UTI(rawValue: kUTTypeVCard as String)
|
||||
static let toDoItem = UTI(rawValue: kUTTypeToDoItem as String)
|
||||
static let calendarEvent = UTI(rawValue: kUTTypeCalendarEvent as String)
|
||||
static let emailMessage = UTI(rawValue: kUTTypeEmailMessage as String)
|
||||
static let internetLocation = UTI(rawValue: kUTTypeInternetLocation as String)
|
||||
static let inkText = UTI(rawValue: kUTTypeInkText as String)
|
||||
static let font = UTI(rawValue: kUTTypeFont as String)
|
||||
static let bookmark = UTI(rawValue: kUTTypeBookmark as String)
|
||||
static let _3DContent = UTI(rawValue: kUTType3DContent as String)
|
||||
static let pkcs12 = UTI(rawValue: kUTTypePKCS12 as String)
|
||||
static let x509Certificate = UTI(rawValue: kUTTypeX509Certificate as String)
|
||||
static let electronicPublication = UTI(rawValue: kUTTypeElectronicPublication as String)
|
||||
static let log = UTI(rawValue: kUTTypeLog as String)
|
||||
static let audiovisualContent = UTI(rawValue: kUTTypeAudiovisualContent as String)
|
||||
static let movie = UTI(rawValue: kUTTypeMovie as String)
|
||||
static let video = UTI(rawValue: kUTTypeVideo as String)
|
||||
static let audio = UTI(rawValue: kUTTypeAudio as String)
|
||||
static let quickTimeMovie = UTI(rawValue: kUTTypeQuickTimeMovie as String)
|
||||
static let mpeg = UTI(rawValue: kUTTypeMPEG as String)
|
||||
static let mpeg2Video = UTI(rawValue: kUTTypeMPEG2Video as String)
|
||||
static let mpeg2TransportStream = UTI(rawValue: kUTTypeMPEG2TransportStream as String)
|
||||
static let mp3 = UTI(rawValue: kUTTypeMP3 as String)
|
||||
static let mpeg4 = UTI(rawValue: kUTTypeMPEG4 as String)
|
||||
static let mpeg4Audio = UTI(rawValue: kUTTypeMPEG4Audio as String)
|
||||
static let appleProtectedMPEG4Audio = UTI(rawValue: kUTTypeAppleProtectedMPEG4Audio as String)
|
||||
static let appleProtectedMPEG4Video = UTI(rawValue: kUTTypeAppleProtectedMPEG4Video as String)
|
||||
static let aviMovie = UTI(rawValue: kUTTypeAVIMovie as String)
|
||||
static let audioInterchangeFileFormat = UTI(rawValue: kUTTypeAudioInterchangeFileFormat as String)
|
||||
static let waveformAudio = UTI(rawValue: kUTTypeWaveformAudio as String)
|
||||
static let midiAudio = UTI(rawValue: kUTTypeMIDIAudio as String)
|
||||
static let playlist = UTI(rawValue: kUTTypePlaylist as String)
|
||||
static let m3UPlaylist = UTI(rawValue: kUTTypeM3UPlaylist as String)
|
||||
static let folder = UTI(rawValue: kUTTypeFolder as String)
|
||||
static let volume = UTI(rawValue: kUTTypeVolume as String)
|
||||
static let package = UTI(rawValue: kUTTypePackage as String)
|
||||
static let bundle = UTI(rawValue: kUTTypeBundle as String)
|
||||
static let pluginBundle = UTI(rawValue: kUTTypePluginBundle as String)
|
||||
static let spotlightImporter = UTI(rawValue: kUTTypeSpotlightImporter as String)
|
||||
static let quickLookGenerator = UTI(rawValue: kUTTypeQuickLookGenerator as String)
|
||||
static let xpcService = UTI(rawValue: kUTTypeXPCService as String)
|
||||
static let framework = UTI(rawValue: kUTTypeFramework as String)
|
||||
static let application = UTI(rawValue: kUTTypeApplication as String)
|
||||
static let applicationBundle = UTI(rawValue: kUTTypeApplicationBundle as String)
|
||||
static let applicationFile = UTI(rawValue: kUTTypeApplicationFile as String)
|
||||
static let unixExecutable = UTI(rawValue: kUTTypeUnixExecutable as String)
|
||||
static let windowsExecutable = UTI(rawValue: kUTTypeWindowsExecutable as String)
|
||||
static let javaClass = UTI(rawValue: kUTTypeJavaClass as String)
|
||||
static let javaArchive = UTI(rawValue: kUTTypeJavaArchive as String)
|
||||
static let systemPreferencesPane = UTI(rawValue: kUTTypeSystemPreferencesPane as String)
|
||||
static let gnuZipArchive = UTI(rawValue: kUTTypeGNUZipArchive as String)
|
||||
static let bzip2Archive = UTI(rawValue: kUTTypeBzip2Archive as String)
|
||||
static let zipArchive = UTI(rawValue: kUTTypeZipArchive as String)
|
||||
static let spreadsheet = UTI(rawValue: kUTTypeSpreadsheet as String)
|
||||
static let presentation = UTI(rawValue: kUTTypePresentation as String)
|
||||
static let database = UTI(rawValue: kUTTypeDatabase as String)
|
||||
static let vCard = UTI(rawValue: kUTTypeVCard as String)
|
||||
static let toDoItem = UTI(rawValue: kUTTypeToDoItem as String)
|
||||
static let calendarEvent = UTI(rawValue: kUTTypeCalendarEvent as String)
|
||||
static let emailMessage = UTI(rawValue: kUTTypeEmailMessage as String)
|
||||
static let internetLocation = UTI(rawValue: kUTTypeInternetLocation as String)
|
||||
static let inkText = UTI(rawValue: kUTTypeInkText as String)
|
||||
static let font = UTI(rawValue: kUTTypeFont as String)
|
||||
static let bookmark = UTI(rawValue: kUTTypeBookmark as String)
|
||||
static let _3DContent = UTI(rawValue: kUTType3DContent as String)
|
||||
static let pkcs12 = UTI(rawValue: kUTTypePKCS12 as String)
|
||||
static let x509Certificate = UTI(rawValue: kUTTypeX509Certificate as String)
|
||||
static let electronicPublication = UTI(rawValue: kUTTypeElectronicPublication as String)
|
||||
static let log = UTI(rawValue: kUTTypeLog as String)
|
||||
}
|
||||
|
||||
#if os(OSX)
|
||||
|
||||
@@ -69,7 +69,6 @@
|
||||
#import "MXKRoomCreationView.h"
|
||||
|
||||
#import "MXKRoomInputToolbarView.h"
|
||||
#import "MXKRoomInputToolbarViewWithHPGrowingText.h"
|
||||
|
||||
#import "MXKRoomDataSourceManager.h"
|
||||
|
||||
|
||||
@@ -75,10 +75,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
|
||||
|
||||
// Internal list of ignored rooms
|
||||
NSMutableArray* ignoredRooms;
|
||||
|
||||
// If a server sync is in progress, the pause is delayed at the end of sync (except if resume is called).
|
||||
BOOL isPauseRequested;
|
||||
|
||||
|
||||
// Background sync management
|
||||
MXOnBackgroundSyncDone backgroundSyncDone;
|
||||
MXOnBackgroundSyncFail backgroundSyncFails;
|
||||
@@ -91,7 +88,9 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
|
||||
id NSCurrentLocaleDidChangeNotificationObserver;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) id<MXBackgroundTask> backgroundTask;
|
||||
/// Will be true if the session is not in a pauseable state or we requested for the session to pause but not finished yet. Will be reverted to false again after `resume` called.
|
||||
@property (nonatomic, assign, getter=isPauseRequested) BOOL pauseRequested;
|
||||
@property (nonatomic, strong) id<MXBackgroundTask> pauseBackgroundTask;
|
||||
@property (nonatomic, strong) id<MXBackgroundTask> backgroundSyncBgTask;
|
||||
|
||||
@end
|
||||
@@ -515,6 +514,32 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
|
||||
return _others;
|
||||
}
|
||||
|
||||
- (void)setPauseRequested:(BOOL)pauseRequested
|
||||
{
|
||||
if (_pauseRequested != pauseRequested)
|
||||
{
|
||||
_pauseRequested = pauseRequested;
|
||||
|
||||
if (_pauseRequested)
|
||||
{
|
||||
// Make sure the SDK finish its work before the app goes sleeping in background
|
||||
id<MXBackgroundModeHandler> handler = [MXSDKOptions sharedInstance].backgroundModeHandler;
|
||||
if (handler)
|
||||
{
|
||||
if (!self.pauseBackgroundTask.isRunning)
|
||||
{
|
||||
self.pauseBackgroundTask = [handler startBackgroundTaskWithName:@"[MXKAccount] pauseInBackgroundTask"
|
||||
expirationHandler:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self cancelPauseBackgroundTask];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Matrix user's profile
|
||||
|
||||
- (void)setUserDisplayName:(NSString*)displayname success:(void (^)(void))success failure:(void (^)(NSError *error))failure
|
||||
@@ -966,38 +991,25 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
|
||||
|
||||
- (void)pauseInBackgroundTask
|
||||
{
|
||||
// Reset internal flag
|
||||
isPauseRequested = NO;
|
||||
|
||||
if (mxSession && mxSession.isPauseable)
|
||||
if (mxSession == nil)
|
||||
{
|
||||
// no session to pause
|
||||
return;
|
||||
}
|
||||
|
||||
// mark that we want to pause when possible
|
||||
self.pauseRequested = YES;
|
||||
|
||||
if (mxSession.isPauseable)
|
||||
{
|
||||
id<MXBackgroundModeHandler> handler = [MXSDKOptions sharedInstance].backgroundModeHandler;
|
||||
if (handler)
|
||||
{
|
||||
if (!self.backgroundTask.isRunning)
|
||||
{
|
||||
self.backgroundTask = [handler startBackgroundTaskWithName:@"[MXKAccount] pauseInBackgroundTask" expirationHandler:nil];
|
||||
}
|
||||
}
|
||||
|
||||
// Pause SDK
|
||||
[mxSession pause];
|
||||
|
||||
// Update user presence
|
||||
__weak typeof(self) weakSelf = self;
|
||||
MXWeakify(self);
|
||||
[self setUserPresence:MXPresenceUnavailable andStatusMessage:nil completion:^{
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
|
||||
if (self.backgroundTask.isRunning)
|
||||
{
|
||||
[self.backgroundTask stop];
|
||||
self.backgroundTask = nil;
|
||||
}
|
||||
}
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
[self cancelPauseBackgroundTask];
|
||||
}];
|
||||
}
|
||||
else
|
||||
@@ -1007,65 +1019,61 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
|
||||
reachabilityObserver = nil;
|
||||
[initialServerSyncTimer invalidate];
|
||||
initialServerSyncTimer = nil;
|
||||
|
||||
if (mxSession.state == MXSessionStateSyncInProgress || mxSession.state == MXSessionStateInitialised || mxSession.state == MXSessionStateStoreDataReady)
|
||||
{
|
||||
// Make sure the SDK finish its work before the app goes sleeping in background
|
||||
id<MXBackgroundModeHandler> handler = [MXSDKOptions sharedInstance].backgroundModeHandler;
|
||||
if (handler)
|
||||
{
|
||||
if (!self.backgroundTask.isRunning)
|
||||
{
|
||||
self.backgroundTask = [handler startBackgroundTaskWithName:@"[MXKAccount] pauseInBackgroundTask" expirationHandler:nil];
|
||||
}
|
||||
}
|
||||
|
||||
MXLogDebug(@"[MXKAccount] Pause is delayed at the end of sync (current state %tu)", mxSession.state);
|
||||
isPauseRequested = YES;
|
||||
}
|
||||
|
||||
MXLogDebug(@"[MXKAccount] Pause is delayed due to the session state: %@", [MXTools readableSessionState: mxSession.state]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resume
|
||||
{
|
||||
isPauseRequested = NO;
|
||||
|
||||
if (mxSession)
|
||||
if (mxSession == nil)
|
||||
{
|
||||
MXLogVerbose(@"[MXKAccount] resume with session state: %tu", mxSession.state);
|
||||
|
||||
[self cancelBackgroundSync];
|
||||
|
||||
if (mxSession.state == MXSessionStatePaused || mxSession.state == MXSessionStatePauseRequested)
|
||||
// no session to resume
|
||||
return;
|
||||
}
|
||||
|
||||
// mark that we don't want to pause anymore
|
||||
self.pauseRequested = NO;
|
||||
|
||||
MXLogVerbose(@"[MXKAccount] resume: with session state: %@", [MXTools readableSessionState:mxSession.state]);
|
||||
|
||||
[self cancelBackgroundSync];
|
||||
|
||||
switch (mxSession.state)
|
||||
{
|
||||
case MXSessionStatePaused:
|
||||
case MXSessionStatePauseRequested:
|
||||
{
|
||||
// Resume SDK and update user presence
|
||||
[mxSession resume:^{
|
||||
[self setUserPresence:MXPresenceOnline andStatusMessage:nil completion:nil];
|
||||
|
||||
|
||||
[self refreshAPNSPusher];
|
||||
[self refreshPushKitPusher];
|
||||
}];
|
||||
|
||||
break;
|
||||
}
|
||||
else if (mxSession.state == MXSessionStateStoreDataReady || mxSession.state == MXSessionStateInitialSyncFailed)
|
||||
case MXSessionStateStoreDataReady:
|
||||
case MXSessionStateInitialSyncFailed:
|
||||
{
|
||||
// The session initialisation was uncompleted, we try to complete it here.
|
||||
[self launchInitialServerSync];
|
||||
|
||||
|
||||
[self refreshAPNSPusher];
|
||||
[self refreshPushKitPusher];
|
||||
|
||||
break;
|
||||
}
|
||||
else if (mxSession.state == MXSessionStateSyncInProgress)
|
||||
case MXSessionStateSyncInProgress:
|
||||
{
|
||||
[self refreshAPNSPusher];
|
||||
[self refreshPushKitPusher];
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Cancel background task
|
||||
if (self.backgroundTask.isRunning)
|
||||
{
|
||||
[self.backgroundTask stop];
|
||||
self.backgroundTask = nil;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1616,16 +1624,16 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
|
||||
|
||||
- (void)onMatrixSessionStateChange
|
||||
{
|
||||
// Check if pause has been requested
|
||||
if (self.isPauseRequested && mxSession.isPauseable)
|
||||
{
|
||||
MXLogDebug(@"[MXKAccount] Apply the pending pause.");
|
||||
[self pauseInBackgroundTask];
|
||||
return;
|
||||
}
|
||||
|
||||
if (mxSession.state == MXSessionStateRunning)
|
||||
{
|
||||
// Check if pause has been requested
|
||||
if (isPauseRequested)
|
||||
{
|
||||
MXLogDebug(@"[MXKAccount] Apply the pending pause.");
|
||||
[self pauseInBackgroundTask];
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether the session was not already running
|
||||
if (!userUpdateListener)
|
||||
{
|
||||
@@ -1662,7 +1670,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
|
||||
}
|
||||
else if (mxSession.state == MXSessionStatePaused)
|
||||
{
|
||||
isPauseRequested = NO;
|
||||
self.pauseRequested = NO;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1754,6 +1762,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
|
||||
|
||||
dispatch_group_leave(dispatchGroup);
|
||||
} failure:^(NSError *error) {
|
||||
MXLogError(@"[MXKAccount] onDateTimeFormatUpdate: event fetch failed: %@", error);
|
||||
dispatch_group_leave(dispatchGroup);
|
||||
}];
|
||||
}
|
||||
@@ -1774,6 +1783,16 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cancelPauseBackgroundTask
|
||||
{
|
||||
// Cancel background task
|
||||
if (self.pauseBackgroundTask.isRunning)
|
||||
{
|
||||
[self.pauseBackgroundTask stop];
|
||||
self.pauseBackgroundTask = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Crypto
|
||||
- (void)resetDeviceId
|
||||
{
|
||||
@@ -1890,7 +1909,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXLogDebug(@"[MXKAccount] cannot start background Sync (invalid state %tu)", mxSession.state);
|
||||
MXLogDebug(@"[MXKAccount] cannot start background Sync (invalid state %@)", [MXTools readableSessionState:mxSession.state]);
|
||||
failure([NSError errorWithDomain:kMXKAccountErrorDomain code:0 userInfo:nil]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,8 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa
|
||||
}
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
- (void)saveAccounts
|
||||
{
|
||||
NSDate *startDate = [NSDate date];
|
||||
@@ -123,6 +125,7 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa
|
||||
|
||||
MXLogDebug(@"[MXKAccountManager] saveAccounts. Done (result: %@) in %.0fms", @(result), [[NSDate date] timeIntervalSinceDate:startDate] * 1000);
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (void)addAccount:(MXKAccount *)account andOpenSession:(BOOL)openSession
|
||||
{
|
||||
@@ -608,6 +611,8 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa
|
||||
return [matrixKitCacheFolder stringByAppendingPathComponent:kMXKAccountsKey];
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
- (void)loadAccounts
|
||||
{
|
||||
MXLogDebug(@"[MXKAccountManager] loadAccounts");
|
||||
@@ -675,6 +680,7 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa
|
||||
mxAccounts = [NSMutableArray array];
|
||||
}
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (NSData*)encryptData:(NSData*)data
|
||||
{
|
||||
@@ -708,6 +714,8 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa
|
||||
return data;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
- (void)migrateAccounts
|
||||
{
|
||||
NSString *pathOld = [[MXKAppSettings cacheFolder] stringByAppendingPathComponent:kMXKAccountsKeyOld];
|
||||
@@ -727,6 +735,7 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa
|
||||
[fileManager removeItemAtPath:pathOld error:nil];
|
||||
}
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (void)readAndWriteCredentials:(void (^)(NSArray<MXCredentials*> * _Nullable readData, void (^completion)(BOOL didUpdateCredentials)))readAnWriteHandler
|
||||
{
|
||||
|
||||
@@ -93,6 +93,8 @@ extern NSString *const kMXKContactDefaultContactPrefixId;
|
||||
*/
|
||||
- (void)resetMatrixThumbnail;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
/**
|
||||
The contact ID from native phonebook record
|
||||
*/
|
||||
@@ -105,6 +107,7 @@ extern NSString *const kMXKContactDefaultContactPrefixId;
|
||||
@return MXKContact instance
|
||||
*/
|
||||
- (id)initLocalContactWithABRecord:(ABRecordRef)record;
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
/**
|
||||
Create a matrix contact with the dedicated info
|
||||
|
||||
@@ -40,6 +40,8 @@ NSString *const kMXKContactDefaultContactPrefixId = @"Default_";
|
||||
@implementation MXKContact
|
||||
@synthesize isMatrixContact, isThirdPartyInvite;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
+ (NSString*)contactID:(ABRecordRef)record
|
||||
{
|
||||
return [NSString stringWithFormat:@"%@%d", kMXKContactLocalContactPrefixId, ABRecordGetRecordID(record)];
|
||||
@@ -217,6 +219,7 @@ NSString *const kMXKContactDefaultContactPrefixId = @"Default_";
|
||||
}
|
||||
return self;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (id)initMatrixContactWithDisplayName:(NSString*)displayName andMatrixID:(NSString*)matrixID
|
||||
{
|
||||
|
||||
@@ -537,6 +537,8 @@ NSString *const MXKContactManagerDataType = @"org.matrix.kit.MXKContactManagerDa
|
||||
}
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
- (void)refreshLocalContacts
|
||||
{
|
||||
MXLogDebug(@"[MXKContactManager] refreshLocalContacts : Started");
|
||||
@@ -721,6 +723,7 @@ NSString *const MXKContactManagerDataType = @"org.matrix.kit.MXKContactManagerDa
|
||||
});
|
||||
}
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (void)updateMatrixIDsForLocalContact:(MXKContact *)contact
|
||||
{
|
||||
@@ -1582,6 +1585,9 @@ static NSString *matrixIDsDictFile = @"matrixIDsDictV2";
|
||||
static NSString *localContactsFile = @"localContactsV2";
|
||||
static NSString *contactsBookInfoFile = @"contactsV2";
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
- (NSString*)dataFilePathForComponent:(NSString*)component
|
||||
{
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
@@ -1857,6 +1863,8 @@ static NSString *contactsBookInfoFile = @"contactsV2";
|
||||
}
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (BOOL)encryptAndSaveData:(NSData*)data toFile:(NSString*)fileName
|
||||
{
|
||||
NSError *error = nil;
|
||||
@@ -1865,6 +1873,10 @@ static NSString *contactsBookInfoFile = @"contactsV2";
|
||||
if (error == nil)
|
||||
{
|
||||
[cipher writeToFile:[self dataFilePathForComponent:fileName] atomically:YES];
|
||||
[[NSFileManager defaultManager] excludeItemFromBackupAt:[NSURL fileURLWithPath:fileName] error:&error];
|
||||
if (error) {
|
||||
MXLogDebug(@"[MXKContactManager] Cannot exclude item from backup %@", error.localizedDescription);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -29,6 +29,10 @@
|
||||
@property (nonatomic) NSString *sid;
|
||||
@property (nonatomic) MXIdentityService *identityService;
|
||||
@property (nonatomic) NSString *submitUrl;
|
||||
/**
|
||||
HTTP client dedicated to sending MSISDN token to custom URLs.
|
||||
*/
|
||||
@property (nonatomic, strong) MXHTTPClient *msisdnSubmissionHttpClient;
|
||||
|
||||
@end
|
||||
|
||||
@@ -255,14 +259,23 @@
|
||||
@"token": token
|
||||
};
|
||||
|
||||
MXHTTPClient *httpClient = [[MXHTTPClient alloc] initWithBaseURL:nil andOnUnrecognizedCertificateBlock:nil];
|
||||
return [httpClient requestWithMethod:@"POST"
|
||||
path:url
|
||||
parameters:parameters
|
||||
success:^(NSDictionary *JSONResponse) {
|
||||
success();
|
||||
}
|
||||
failure:failure];
|
||||
self.msisdnSubmissionHttpClient = [[MXHTTPClient alloc] initWithBaseURL:nil andOnUnrecognizedCertificateBlock:nil];
|
||||
|
||||
MXWeakify(self);
|
||||
return [self.msisdnSubmissionHttpClient requestWithMethod:@"POST"
|
||||
path:url
|
||||
parameters:parameters
|
||||
success:^(NSDictionary *JSONResponse) {
|
||||
success();
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self.msisdnSubmissionHttpClient = nil;
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
failure(error);
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self.msisdnSubmissionHttpClient = nil;
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
- (void)add3PIDToUser:(BOOL)bind
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#import "MXKAppSettings.h"
|
||||
|
||||
#import "MXKTools.h"
|
||||
@import MatrixSDK;
|
||||
|
||||
|
||||
// get ISO country name
|
||||
@@ -66,11 +67,10 @@ static NSString *const kMXAppGroupID = @"group.org.matrix";
|
||||
{
|
||||
NSString *cacheFolder;
|
||||
|
||||
// Check for a potential application group id
|
||||
NSString *applicationGroupIdentifier = [MXSDKOptions sharedInstance].applicationGroupIdentifier;
|
||||
if (applicationGroupIdentifier)
|
||||
// Check for a potential application group container
|
||||
NSURL *sharedContainerURL = [[NSFileManager defaultManager] applicationGroupContainerURL];
|
||||
if (sharedContainerURL)
|
||||
{
|
||||
NSURL *sharedContainerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:applicationGroupIdentifier];
|
||||
cacheFolder = [sharedContainerURL path];
|
||||
}
|
||||
else
|
||||
@@ -86,7 +86,7 @@ static NSString *const kMXAppGroupID = @"group.org.matrix";
|
||||
if (cacheFolder && ![[NSFileManager defaultManager] fileExistsAtPath:cacheFolder])
|
||||
{
|
||||
NSError *error;
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:cacheFolder withIntermediateDirectories:YES attributes:nil error:&error];
|
||||
[[NSFileManager defaultManager] createDirectoryExcludedFromBackupAtPath:cacheFolder error:&error];
|
||||
if (error)
|
||||
{
|
||||
MXLogDebug(@"[MXKAppSettings] cacheFolder: Error: Cannot create MatrixKit folder at %@. Error: %@", cacheFolder, error);
|
||||
@@ -670,6 +670,8 @@ static NSString *const kMXAppGroupID = @"group.org.matrix";
|
||||
}
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
- (NSString*)phonebookCountryCode
|
||||
{
|
||||
NSString* res = phonebookCountryCode;
|
||||
@@ -699,6 +701,7 @@ static NSString *const kMXAppGroupID = @"group.org.matrix";
|
||||
|
||||
return res;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (void)setPhonebookCountryCode:(NSString *)stringValue
|
||||
{
|
||||
|
||||
@@ -46,6 +46,11 @@
|
||||
*/
|
||||
NSAttributedString *attributedTextMessage;
|
||||
|
||||
/**
|
||||
Same as attributedTextMessage but without vertical positioning vertical blank space.
|
||||
*/
|
||||
NSAttributedString *attributedTextMessageWithoutPositioningSpace;
|
||||
|
||||
/**
|
||||
The optional text pattern to be highlighted in the body of the message.
|
||||
*/
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
@implementation MXKRoomBubbleCellData
|
||||
@synthesize senderId, targetId, roomId, senderDisplayName, senderAvatarUrl, senderAvatarPlaceholder, targetDisplayName, targetAvatarUrl, targetAvatarPlaceholder, isEncryptedRoom, isPaginationFirstBubble, shouldHideSenderInformation, date, isIncoming, isAttachmentWithThumbnail, isAttachmentWithIcon, attachment, senderFlair;
|
||||
@synthesize textMessage, attributedTextMessage;
|
||||
@synthesize textMessage, attributedTextMessage, attributedTextMessageWithoutPositioningSpace;
|
||||
@synthesize shouldHideSenderName, isTyping, showBubbleDateTime, showBubbleReceipts, useCustomDateTimeLabel, useCustomReceipts, useCustomUnsentButton, hasNoDisplay;
|
||||
@synthesize tag;
|
||||
@synthesize collapsable, collapsed, collapsedAttributedTextMessage, prevCollapsableCellData, nextCollapsableCellData, collapseState;
|
||||
|
||||
@@ -163,6 +163,10 @@
|
||||
*/
|
||||
@property (nonatomic) NSAttributedString *attributedTextMessage;
|
||||
|
||||
/**
|
||||
Same as attributedTextMessage but without vertical positioning blank space
|
||||
*/
|
||||
@property (nonatomic) NSAttributedString *attributedTextMessageWithoutPositioningSpace;
|
||||
/**
|
||||
The raw text message (without attributes)
|
||||
*/
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#import "MXKEventFormatter.h"
|
||||
#import "MXKURLPreviewDataProtocol.h"
|
||||
|
||||
@class MXThread;
|
||||
@protocol MXThreadProtocol;
|
||||
|
||||
/**
|
||||
Flags to indicate if a fix is required at the display time.
|
||||
@@ -108,7 +108,7 @@ typedef enum : NSUInteger {
|
||||
/**
|
||||
Thread for the bubble component. Should only exist for thread root events.
|
||||
*/
|
||||
@property (nonatomic, readonly) MXThread *thread;
|
||||
@property (nonatomic, readonly) id<MXThreadProtocol> thread;
|
||||
|
||||
/**
|
||||
Create a new `MXKRoomBubbleComponent` object based on a `MXEvent` instance.
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
@interface MXKRoomBubbleComponent ()
|
||||
|
||||
@property (nonatomic, readwrite) MXThread *thread;
|
||||
@property (nonatomic, readwrite) id<MXThreadProtocol> thread;
|
||||
|
||||
@end
|
||||
|
||||
@@ -69,8 +69,17 @@
|
||||
_showEncryptionBadge = [self shouldShowWarningBadgeForEvent:event roomState:(MXRoomState*)roomState session:session];
|
||||
|
||||
[self updateLinkWithRoomState:roomState];
|
||||
|
||||
self.thread = [session.threadingService threadWithId:event.eventId];
|
||||
|
||||
if (event.unsignedData.relations.thread)
|
||||
{
|
||||
self.thread = [[MXThreadModel alloc] initWithRootEvent:event
|
||||
notificationCount:0
|
||||
highlightCount:0];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.thread = [session.threadingService threadWithId:event.eventId];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -1448,6 +1448,16 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
if (!USE_THREAD_TIMELINE && direction == MXTimelineDirectionBackwards && self.threadId)
|
||||
{
|
||||
// when not using a thread timeline, data source will desperately fill the screen with events by filtering them locally.
|
||||
// we can stop when we see the thread root event when paginating backwards
|
||||
if ([event.eventId isEqualToString:self.threadId])
|
||||
{
|
||||
self.shouldStopBackPagination = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
|
||||
#pragma mark - Override MXKRecentsDataSource
|
||||
|
||||
- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame
|
||||
- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame inTableView:(UITableView*)tableView
|
||||
{
|
||||
UIView *sectionHeader = nil;
|
||||
|
||||
|
||||
@@ -88,9 +88,10 @@
|
||||
|
||||
@param section the section index
|
||||
@param frame the drawing area for the header of the specified section.
|
||||
@param tableView the table view
|
||||
@return the section header.
|
||||
*/
|
||||
- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame;
|
||||
- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame inTableView:(UITableView*)tableView;
|
||||
|
||||
/**
|
||||
Get the data for the cell at the given index path.
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame
|
||||
- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame inTableView:(UITableView*)tableView
|
||||
{
|
||||
UIView *sectionHeader = nil;
|
||||
|
||||
|
||||
@@ -30,8 +30,8 @@ extern NSString *const kMXKRecentCellIdentifier;
|
||||
|
||||
/**
|
||||
The recents data source based on a unique matrix session.
|
||||
Deprecated: Please see MXSession.roomListDataManager
|
||||
*/
|
||||
MXK_DEPRECATED_ATTRIBUTE_WITH_MSG("See MXSession.roomListDataManager")
|
||||
@interface MXKSessionRecentsDataSource : MXKDataSource {
|
||||
|
||||
@protected
|
||||
|
||||
@@ -955,7 +955,7 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
|
||||
}
|
||||
if (event.content[@"kick"])
|
||||
{
|
||||
displayText = [NSString stringWithFormat:@"%@\n\u2022 kick: %@", displayText, event.content[@"kick"]];
|
||||
displayText = [NSString stringWithFormat:@"%@\n\u2022 remove: %@", displayText, event.content[@"kick"]];
|
||||
}
|
||||
if (event.content[@"redact"])
|
||||
{
|
||||
|
||||
@@ -36,7 +36,17 @@ public class MarkdownToHTMLRenderer: NSObject {
|
||||
extension MarkdownToHTMLRenderer: MarkdownToHTMLRendererProtocol {
|
||||
|
||||
public func renderToHTML(markdown: String) -> String? {
|
||||
return try? Down(markdownString: markdown).toHTML(options)
|
||||
do {
|
||||
let ast = try DownASTRenderer.stringToAST(markdown, options: options)
|
||||
defer {
|
||||
cmark_node_free(ast)
|
||||
}
|
||||
ast.repairLinks()
|
||||
return try DownHTMLRenderer.astToHTML(ast, options: options)
|
||||
} catch {
|
||||
MXLog.error("[MarkdownToHTMLRenderer] renderToHTML failed with string: \(markdown)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,3 +60,116 @@ public class MarkdownToHTMLRendererHardBreaks: MarkdownToHTMLRenderer {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - AST-handling private extensions
|
||||
private extension CMarkNode {
|
||||
/// Formatting symbol associated with given note type
|
||||
/// Note: this is only defined for node types that are handled in repairLinks
|
||||
var formattingSymbol: String {
|
||||
switch self.type {
|
||||
case CMARK_NODE_EMPH:
|
||||
return "_"
|
||||
case CMARK_NODE_STRONG:
|
||||
return "__"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
/// Repairs links that were broken down by markdown formatting.
|
||||
/// Should be used on the first node of libcmark's AST
|
||||
/// (e.g. the object returned by DownASTRenderer.stringToAST).
|
||||
func repairLinks() {
|
||||
let iterator = cmark_iter_new(self)
|
||||
var text = ""
|
||||
var isInParagraph = false
|
||||
var previousNode: CMarkNode?
|
||||
var orphanNodes: [CMarkNode] = []
|
||||
var shouldUnlinkFormattingMode = false
|
||||
var event: cmark_event_type?
|
||||
while event != CMARK_EVENT_DONE {
|
||||
event = cmark_iter_next(iterator)
|
||||
|
||||
guard let node = cmark_iter_get_node(iterator) else { return }
|
||||
|
||||
if node.type == CMARK_NODE_PARAGRAPH {
|
||||
if event == CMARK_EVENT_ENTER {
|
||||
isInParagraph = true
|
||||
} else {
|
||||
isInParagraph = false
|
||||
text = ""
|
||||
}
|
||||
}
|
||||
|
||||
if isInParagraph {
|
||||
switch node.type {
|
||||
case CMARK_NODE_SOFTBREAK,
|
||||
CMARK_NODE_LINEBREAK:
|
||||
text = ""
|
||||
case CMARK_NODE_TEXT:
|
||||
if let literal = node.literal {
|
||||
text += literal
|
||||
// Reset text if it ends up with a whitespace.
|
||||
if text.last?.isWhitespace == true {
|
||||
text = ""
|
||||
}
|
||||
// Only the last part could be a link conflicting with next node.
|
||||
text = String(text.split(separator: " ").last ?? "")
|
||||
}
|
||||
case CMARK_NODE_EMPH where previousNode?.type == CMARK_NODE_TEXT,
|
||||
CMARK_NODE_STRONG where previousNode?.type == CMARK_NODE_TEXT:
|
||||
if event == CMARK_EVENT_ENTER {
|
||||
if !text.containedUrls.isEmpty,
|
||||
let childLiteral = node.pointee.first_child.literal {
|
||||
// If current text is a link, the formatted text is reverted back to a
|
||||
// plain text as a part of the link.
|
||||
let symbol = node.formattingSymbol
|
||||
let nonFormattedText = "\(symbol)\(childLiteral)\(symbol)"
|
||||
let replacementTextNode = cmark_node_new(CMARK_NODE_TEXT)
|
||||
cmark_node_set_literal(replacementTextNode, nonFormattedText)
|
||||
cmark_node_insert_after(previousNode, replacementTextNode)
|
||||
// Set child literal to empty string so we dont read it.
|
||||
// This avoids having to re-create the main
|
||||
// iterator in the middle of the process.
|
||||
cmark_node_set_literal(node.pointee.first_child, "")
|
||||
let newIterator = cmark_iter_new(node)
|
||||
_ = cmark_iter_next(newIterator)
|
||||
cmark_node_unlink(node)
|
||||
orphanNodes.append(node)
|
||||
let nextNode = cmark_iter_get_node(newIterator)
|
||||
cmark_node_insert_after(previousNode, nextNode)
|
||||
shouldUnlinkFormattingMode = true
|
||||
}
|
||||
} else {
|
||||
if shouldUnlinkFormattingMode {
|
||||
cmark_node_unlink(node)
|
||||
orphanNodes.append(node)
|
||||
shouldUnlinkFormattingMode = false
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
previousNode = node
|
||||
}
|
||||
|
||||
// Free all nodes removed from the AST.
|
||||
// This is done as a last step to avoid messing
|
||||
// up with the main itertor.
|
||||
for orphanNode in orphanNodes {
|
||||
cmark_node_free(orphanNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension String {
|
||||
/// Returns array of URLs detected inside the String.
|
||||
var containedUrls: [NSTextCheckingResult] {
|
||||
guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else {
|
||||
return []
|
||||
}
|
||||
|
||||
return detector.matches(in: self, options: [], range: NSRange(location: 0, length: self.utf16.count))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#define MXK_DEPRECATED_ATTRIBUTE __attribute__((deprecated))
|
||||
#define MXK_DEPRECATED_ATTRIBUTE_WITH_MSG(msg) __attribute((deprecated((msg))))
|
||||
|
||||
/**
|
||||
The Matrix iOS Kit version.
|
||||
*/
|
||||
|
||||
@@ -24,7 +24,8 @@ import MobileCoreServices
|
||||
|
||||
/// MXKDocumentPickerPresenter presents a controller that provides access to documents or destinations outside the app’s sandbox.
|
||||
/// Internally presents a UIDocumentPickerViewController in UIDocumentPickerMode.import.
|
||||
/// Note: You must turn on the iCloud Documents capabilities in Xcode (see https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/DocumentPickerProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40014451)
|
||||
/// Note: You must turn on the iCloud Documents capabilities in Xcode
|
||||
/// (see https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/DocumentPickerProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40014451)
|
||||
@objcMembers
|
||||
public class MXKDocumentPickerPresenter: NSObject {
|
||||
|
||||
|
||||
@@ -1112,7 +1112,7 @@ manualChangeMessageForVideo:(NSString*)manualChangeMessageForVideo
|
||||
// Caution: We need here to escape the non-ASCII characters (like '#' in room alias)
|
||||
// to convert the link into a legal URL string.
|
||||
NSString *link = [attributedString.string substringWithRange:match.range];
|
||||
link = [link stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||
link = [link stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
|
||||
[*mutableAttributedString addAttribute:NSLinkAttributeName value:link range:match.range];
|
||||
}
|
||||
}];
|
||||
|
||||
@@ -20,7 +20,7 @@ import MobileCoreServices
|
||||
|
||||
// We do not use the SwiftUTI pod anymore
|
||||
// The library is embedded in MatrixKit. See Libs/SwiftUTI/README.md for more details
|
||||
//import SwiftUTI
|
||||
// import SwiftUTI
|
||||
|
||||
/// MXKUTI represents a Universal Type Identifier (e.g. kUTTypePNG).
|
||||
/// See https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/understanding_utis/understand_utis_conc/understand_utis_conc.html#//apple_ref/doc/uid/TP40001319-CH202-SW5 for more information.
|
||||
@@ -134,6 +134,8 @@ extension MXKUTI {
|
||||
self.init(rawValue: uti as String)
|
||||
}
|
||||
|
||||
// swiftlint:disable unused_optional_binding
|
||||
|
||||
/// Initialize with local file URL.
|
||||
/// This method is currently applicable only to URLs for file system resources.
|
||||
///
|
||||
@@ -153,6 +155,8 @@ extension MXKUTI {
|
||||
}
|
||||
}
|
||||
|
||||
// swiftlint:enable unused_optional_binding
|
||||
|
||||
public convenience init?(localFileURL: URL) {
|
||||
self.init(localFileURL: localFileURL, loadResourceValues: true)
|
||||
}
|
||||
@@ -173,6 +177,8 @@ extension MXKUTI {
|
||||
}
|
||||
}
|
||||
|
||||
// swiftlint:disable force_unwrapping
|
||||
|
||||
// MARK: - Some system defined UTIs
|
||||
extension MXKUTI {
|
||||
public static let data = MXKUTI(cfRawValue: kUTTypeData)!
|
||||
@@ -190,6 +196,8 @@ extension MXKUTI {
|
||||
public static let xml = MXKUTI(cfRawValue: kUTTypeXML)!
|
||||
}
|
||||
|
||||
// swiftlint:enable force_unwrapping
|
||||
|
||||
// MARK: - Convenience static methods
|
||||
extension MXKUTI {
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ sendObjectMessage({ \
|
||||
MXLogDebug(@"[MXKAuthenticationFallbackWebView] URL has js: prefix");
|
||||
|
||||
// Listen only to scheme of the JS-WKWebView bridge
|
||||
NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
|
||||
NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByRemovingPercentEncoding];
|
||||
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSError *error;
|
||||
|
||||
@@ -102,7 +102,7 @@ var onloadCallback = function() { \
|
||||
if ([urlString hasPrefix:@"js:"])
|
||||
{
|
||||
// Listen only to scheme of the JS-WKWebView bridge
|
||||
NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
|
||||
NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByRemovingPercentEncoding];
|
||||
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSError *error;
|
||||
|
||||
@@ -417,7 +417,7 @@ static NSAttributedString *verticalWhitespace = nil;
|
||||
[_mxSession.crypto setDeviceVerification:MXDeviceVerified forDevice:_mxDeviceInfo.deviceId ofUser:_mxDeviceInfo.userId success:^{
|
||||
|
||||
// Refresh data
|
||||
_mxDeviceInfo = [self.mxSession.crypto eventDeviceInfo:self.mxEvent];
|
||||
self->_mxDeviceInfo = [self.mxSession.crypto eventDeviceInfo:self.mxEvent];
|
||||
if (self->_delegate)
|
||||
{
|
||||
[self->_delegate encryptionInfoView:self didDeviceInfoVerifiedChange:self.mxDeviceInfo];
|
||||
@@ -473,7 +473,7 @@ static NSAttributedString *verticalWhitespace = nil;
|
||||
[_mxSession.crypto setDeviceVerification:verificationStatus forDevice:_mxDeviceInfo.deviceId ofUser:_mxDeviceInfo.userId success:^{
|
||||
|
||||
// Refresh data
|
||||
_mxDeviceInfo = [self.mxSession.crypto eventDeviceInfo:self.mxEvent];
|
||||
self->_mxDeviceInfo = [self.mxSession.crypto eventDeviceInfo:self.mxEvent];
|
||||
|
||||
if (self->_delegate)
|
||||
{
|
||||
|
||||
@@ -75,11 +75,9 @@
|
||||
- (MXKCellData*)renderedCellData;
|
||||
|
||||
/**
|
||||
Reset the cell.
|
||||
Stop processes no more needed when cell is not visible.
|
||||
|
||||
The cell is no more displayed. This is time to release resources and removing listeners.
|
||||
In case of UITableViewCell or UIContentViewCell object, the cell must reset in a state
|
||||
that it can be reusable.
|
||||
The cell is no more displayed but still recycled. This is time to stop animation.
|
||||
*/
|
||||
- (void)didEndDisplay;
|
||||
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
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 <MatrixSDK/MatrixSDK.h>
|
||||
|
||||
#import "MXKView.h"
|
||||
|
||||
typedef NS_ENUM(NSInteger, ReadReceiptsAlignment)
|
||||
{
|
||||
/**
|
||||
The latest receipt is displayed on left
|
||||
*/
|
||||
ReadReceiptAlignmentLeft = 0,
|
||||
|
||||
/**
|
||||
The latest receipt is displayed on right
|
||||
*/
|
||||
ReadReceiptAlignmentRight = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
`MXKReceiptSendersContainer` is a view dedicated to display receipt senders by using their avatars.
|
||||
|
||||
This container handles automatically the number of visible avatars. A label is added when avatars are not all visible (see 'moreLabel' property).
|
||||
*/
|
||||
@interface MXKReceiptSendersContainer : MXKView
|
||||
|
||||
/**
|
||||
The maximum number of avatars displayed in the container. 3 by default.
|
||||
*/
|
||||
@property (nonatomic) NSInteger maxDisplayedAvatars;
|
||||
|
||||
/**
|
||||
The space between avatars. 2.0 points by default.
|
||||
*/
|
||||
@property (nonatomic) CGFloat avatarMargin;
|
||||
|
||||
/**
|
||||
The label added beside avatars when avatars are not all visible.
|
||||
*/
|
||||
@property (nonatomic) UILabel* moreLabel;
|
||||
|
||||
/**
|
||||
The more label text color (If set to nil `moreLabel.textColor` use `UIColor.blackColor` as default color).
|
||||
*/
|
||||
@property (nonatomic) UIColor* moreLabelTextColor;
|
||||
|
||||
/*
|
||||
The read receipt objects for details required in the details view
|
||||
*/
|
||||
@property (nonatomic) NSArray <MXReceiptData *> *readReceipts;
|
||||
|
||||
/*
|
||||
The array of the room members that will be displayed in the container
|
||||
*/
|
||||
@property (nonatomic, readonly) NSArray <MXRoomMember *> *roomMembers;
|
||||
|
||||
/*
|
||||
The placeholders of the room members that will be shown if the users don't have avatars
|
||||
*/
|
||||
@property (nonatomic, readonly) NSArray <UIImage *> *placeholders;
|
||||
|
||||
/**
|
||||
Initializes an `MXKReceiptSendersContainer` object with a frame and a media manager.
|
||||
|
||||
This is the designated initializer.
|
||||
|
||||
@param frame the container frame. Note that avatar will be displayed in full height in this container.
|
||||
@param mediaManager the media manager used to download the matrix user's avatar.
|
||||
@return The newly-initialized MXKReceiptSendersContainer instance
|
||||
*/
|
||||
- (instancetype)initWithFrame:(CGRect)frame andMediaManager:(MXMediaManager*)mediaManager;
|
||||
|
||||
/**
|
||||
Refresh the container content by using the provided room members.
|
||||
|
||||
@param roomMembers list of room members sorted from the latest receipt to the oldest receipt.
|
||||
@param placeHolders list of placeholders, one by room member. Used when url is nil, or during avatar download.
|
||||
@param alignment (see ReadReceiptsAlignment).
|
||||
*/
|
||||
- (void)refreshReceiptSenders:(NSArray<MXRoomMember*>*)roomMembers withPlaceHolders:(NSArray<UIImage*>*)placeHolders andAlignment:(ReadReceiptsAlignment)alignment;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKReceiptSendersContainer.h"
|
||||
|
||||
#import "MXKImageView.h"
|
||||
|
||||
static UIColor* kMoreLabelDefaultcolor;
|
||||
|
||||
@interface MXKReceiptSendersContainer ()
|
||||
|
||||
@property (nonatomic, readwrite) NSArray <MXRoomMember *> *roomMembers;
|
||||
@property (nonatomic, readwrite) NSArray <UIImage *> *placeholders;
|
||||
@property (nonatomic) MXMediaManager *mediaManager;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation MXKReceiptSendersContainer
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self == [MXKReceiptSendersContainer class])
|
||||
{
|
||||
kMoreLabelDefaultcolor = [UIColor blackColor];
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame andMediaManager:(MXMediaManager*)mediaManager
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
_mediaManager = mediaManager;
|
||||
_maxDisplayedAvatars = 3;
|
||||
_avatarMargin = 2.0;
|
||||
_moreLabel = nil;
|
||||
_moreLabelTextColor = kMoreLabelDefaultcolor;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)refreshReceiptSenders:(NSArray<MXRoomMember*>*)roomMembers withPlaceHolders:(NSArray<UIImage*>*)placeHolders andAlignment:(ReadReceiptsAlignment)alignment
|
||||
{
|
||||
// Store the room members and placeholders for showing in the details view controller
|
||||
self.roomMembers = roomMembers;
|
||||
self.placeholders = placeHolders;
|
||||
|
||||
// Remove all previous content
|
||||
for (UIView* view in self.subviews)
|
||||
{
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
if (_moreLabel)
|
||||
{
|
||||
[_moreLabel removeFromSuperview];
|
||||
_moreLabel = nil;
|
||||
}
|
||||
|
||||
CGRect globalFrame = self.frame;
|
||||
CGFloat side = globalFrame.size.height;
|
||||
CGFloat defaultMoreLabelWidth = side < 20 ? 20 : side;
|
||||
unsigned long count;
|
||||
unsigned long maxDisplayableItems = (int)((globalFrame.size.width - defaultMoreLabelWidth - _avatarMargin) / (side + _avatarMargin));
|
||||
|
||||
maxDisplayableItems = MIN(maxDisplayableItems, _maxDisplayedAvatars);
|
||||
count = MIN(roomMembers.count, maxDisplayableItems);
|
||||
|
||||
int index;
|
||||
|
||||
CGFloat xOff = 0;
|
||||
|
||||
if (alignment == ReadReceiptAlignmentRight)
|
||||
{
|
||||
xOff = globalFrame.size.width - (side + _avatarMargin);
|
||||
}
|
||||
|
||||
for (index = 0; index < count; index++)
|
||||
{
|
||||
MXRoomMember *roomMember = [roomMembers objectAtIndex:index];
|
||||
UIImage *preview = index < placeHolders.count ? placeHolders[index] : nil;
|
||||
|
||||
MXKImageView *imageView = [[MXKImageView alloc] initWithFrame:CGRectMake(xOff, 0, side, side)];
|
||||
imageView.defaultBackgroundColor = [UIColor clearColor];
|
||||
imageView.autoresizingMask = UIViewAutoresizingNone;
|
||||
|
||||
if (alignment == ReadReceiptAlignmentRight)
|
||||
{
|
||||
xOff -= side + _avatarMargin;
|
||||
}
|
||||
else
|
||||
{
|
||||
xOff += side + _avatarMargin;
|
||||
}
|
||||
|
||||
[self addSubview:imageView];
|
||||
imageView.enableInMemoryCache = YES;
|
||||
|
||||
[imageView setImageURI:roomMember.avatarUrl
|
||||
withType:nil
|
||||
andImageOrientation:UIImageOrientationUp
|
||||
toFitViewSize:CGSizeMake(side, side)
|
||||
withMethod:MXThumbnailingMethodCrop
|
||||
previewImage:preview
|
||||
mediaManager:_mediaManager];
|
||||
|
||||
[imageView.layer setCornerRadius:imageView.frame.size.width / 2];
|
||||
imageView.clipsToBounds = YES;
|
||||
}
|
||||
|
||||
// Check whether there are more than expected read receipts
|
||||
if (roomMembers.count > maxDisplayableItems)
|
||||
{
|
||||
// Add a more indicator
|
||||
|
||||
// In case of right alignment, adjust the current position by considering the default label width
|
||||
if (alignment == ReadReceiptAlignmentRight && side < defaultMoreLabelWidth)
|
||||
{
|
||||
xOff -= (defaultMoreLabelWidth - side);
|
||||
}
|
||||
|
||||
_moreLabel = [[UILabel alloc] initWithFrame:CGRectMake(xOff, 0, defaultMoreLabelWidth, side)];
|
||||
_moreLabel.text = [NSString stringWithFormat:(alignment == ReadReceiptAlignmentRight) ? @"%tu+" : @"+%tu", roomMembers.count - maxDisplayableItems];
|
||||
_moreLabel.font = [UIFont systemFontOfSize:11];
|
||||
_moreLabel.adjustsFontSizeToFitWidth = YES;
|
||||
_moreLabel.minimumScaleFactor = 0.6;
|
||||
|
||||
// In case of right alignment, adjust the horizontal position according to the actual label width
|
||||
if (alignment == ReadReceiptAlignmentRight)
|
||||
{
|
||||
[_moreLabel sizeToFit];
|
||||
CGRect frame = _moreLabel.frame;
|
||||
if (frame.size.width < defaultMoreLabelWidth)
|
||||
{
|
||||
frame.origin.x += (defaultMoreLabelWidth - frame.size.width);
|
||||
_moreLabel.frame = frame;
|
||||
}
|
||||
}
|
||||
|
||||
_moreLabel.textColor = self.moreLabelTextColor ?: kMoreLabelDefaultcolor;
|
||||
[self addSubview:_moreLabel];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
NSArray* subviews = self.subviews;
|
||||
for (UIView* view in subviews)
|
||||
{
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
|
||||
if (_moreLabel)
|
||||
{
|
||||
[_moreLabel removeFromSuperview];
|
||||
_moreLabel = nil;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
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 "MXKRoomInputToolbarView.h"
|
||||
|
||||
#import <HPGrowingTextView/HPGrowingTextView.h>
|
||||
|
||||
/**
|
||||
`MXKRoomInputToolbarViewWithHPGrowingText` is a MXKRoomInputToolbarView-inherited class in which message
|
||||
composer is based on `HPGrowingTextView`.
|
||||
|
||||
Toolbar buttons are not overridden by this class. We keep the default implementation.
|
||||
*/
|
||||
@interface MXKRoomInputToolbarViewWithHPGrowingText : MXKRoomInputToolbarView <HPGrowingTextViewDelegate>
|
||||
{
|
||||
@protected
|
||||
HPGrowingTextView *growingTextView;
|
||||
}
|
||||
|
||||
@end
|
||||
-187
@@ -1,187 +0,0 @@
|
||||
/*
|
||||
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 "MXKRoomInputToolbarViewWithHPGrowingText.h"
|
||||
|
||||
@interface MXKRoomInputToolbarViewWithHPGrowingText()
|
||||
{
|
||||
// HPGrowingTextView triggers growingTextViewDidChange event when it recomposes itself
|
||||
// Save the last edited text to prevent unexpected typing events
|
||||
NSString* lastEditedText;
|
||||
}
|
||||
|
||||
/**
|
||||
Message composer defined in `messageComposerContainer`.
|
||||
*/
|
||||
@property (nonatomic) IBOutlet HPGrowingTextView *growingTextView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MXKRoomInputToolbarViewWithHPGrowingText
|
||||
@synthesize growingTextView;
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
return [UINib nibWithNibName:NSStringFromClass([MXKRoomInputToolbarViewWithHPGrowingText class])
|
||||
bundle:[NSBundle bundleForClass:[MXKRoomInputToolbarViewWithHPGrowingText class]]];
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
|
||||
// Handle message composer based on HPGrowingTextView use
|
||||
growingTextView.delegate = self;
|
||||
|
||||
[growingTextView setTranslatesAutoresizingMaskIntoConstraints: NO];
|
||||
|
||||
// Add an accessory view to the text view in order to retrieve keyboard view.
|
||||
inputAccessoryView = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
growingTextView.internalTextView.inputAccessoryView = self.inputAccessoryView;
|
||||
|
||||
// on IOS 8, the growing textview animation could trigger weird UI animations
|
||||
// indeed, the messages tableView can be refreshed while its height is updated (e.g. when setting a message)
|
||||
growingTextView.animateHeightChange = NO;
|
||||
|
||||
lastEditedText = nil;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self destroy];
|
||||
}
|
||||
|
||||
-(void)customizeViewRendering
|
||||
{
|
||||
[super customizeViewRendering];
|
||||
|
||||
// set text input font
|
||||
growingTextView.font = [UIFont systemFontOfSize:14];
|
||||
|
||||
// draw a rounded border around the textView
|
||||
growingTextView.layer.cornerRadius = 5;
|
||||
growingTextView.layer.borderWidth = 1;
|
||||
growingTextView.layer.borderColor = [UIColor lightGrayColor].CGColor;
|
||||
growingTextView.clipsToBounds = YES;
|
||||
growingTextView.backgroundColor = [UIColor whiteColor];
|
||||
}
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
if (growingTextView)
|
||||
{
|
||||
growingTextView.delegate = nil;
|
||||
growingTextView = nil;
|
||||
}
|
||||
|
||||
[super destroy];
|
||||
}
|
||||
|
||||
- (void)setMaxHeight:(CGFloat)maxHeight
|
||||
{
|
||||
growingTextView.maxHeight = maxHeight - (self.messageComposerContainerTopConstraint.constant + self.messageComposerContainerBottomConstraint.constant);
|
||||
[growingTextView refreshHeight];
|
||||
|
||||
super.maxHeight = maxHeight;
|
||||
}
|
||||
|
||||
- (NSString*)textMessage
|
||||
{
|
||||
return growingTextView.text;
|
||||
}
|
||||
|
||||
- (void)setTextMessage:(NSString *)textMessage
|
||||
{
|
||||
growingTextView.text = textMessage;
|
||||
self.rightInputToolbarButton.enabled = textMessage.length;
|
||||
}
|
||||
|
||||
- (void)pasteText:(NSString *)text
|
||||
{
|
||||
self.textMessage = [growingTextView.text stringByReplacingCharactersInRange:growingTextView.selectedRange withString:text];
|
||||
}
|
||||
|
||||
- (void)setPlaceholder:(NSString *)inPlaceholder
|
||||
{
|
||||
[super setPlaceholder:inPlaceholder];
|
||||
growingTextView.placeholder = inPlaceholder;
|
||||
}
|
||||
|
||||
- (BOOL)becomeFirstResponder
|
||||
{
|
||||
return [growingTextView becomeFirstResponder];
|
||||
}
|
||||
|
||||
- (void)dismissKeyboard
|
||||
{
|
||||
[growingTextView resignFirstResponder];
|
||||
}
|
||||
|
||||
#pragma mark - HPGrowingTextView delegate
|
||||
|
||||
- (void)growingTextViewDidEndEditing:(HPGrowingTextView *)sender
|
||||
{
|
||||
if ([self.delegate respondsToSelector:@selector(roomInputToolbarView:isTyping:)])
|
||||
{
|
||||
[self.delegate roomInputToolbarView:self isTyping:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)growingTextViewDidChange:(HPGrowingTextView *)sender
|
||||
{
|
||||
NSString *msg = growingTextView.text;
|
||||
|
||||
// HPGrowingTextView triggers growingTextViewDidChange event when it recomposes itself.
|
||||
// Save the last edited text to prevent unexpected typing events
|
||||
if (![lastEditedText isEqualToString:msg])
|
||||
{
|
||||
lastEditedText = msg;
|
||||
if (msg.length)
|
||||
{
|
||||
if ([self.delegate respondsToSelector:@selector(roomInputToolbarView:isTyping:)])
|
||||
{
|
||||
[self.delegate roomInputToolbarView:self isTyping:YES];
|
||||
}
|
||||
self.rightInputToolbarButton.enabled = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([self.delegate respondsToSelector:@selector(roomInputToolbarView:isTyping:)])
|
||||
{
|
||||
[self.delegate roomInputToolbarView:self isTyping:NO];
|
||||
}
|
||||
self.rightInputToolbarButton.enabled = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)growingTextView:(HPGrowingTextView *)growingTextView willChangeHeight:(float)height
|
||||
{
|
||||
// Update growing text's superview (toolbar view)
|
||||
CGFloat updatedHeight = height + (self.messageComposerContainerTopConstraint.constant + self.messageComposerContainerBottomConstraint.constant);
|
||||
if ([self.delegate respondsToSelector:@selector(roomInputToolbarView:heightDidChanged:completion:)])
|
||||
{
|
||||
[self.delegate roomInputToolbarView:self heightDidChanged:updatedHeight completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)growingTextView:(HPGrowingTextView *)growingTextView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
|
||||
{
|
||||
return self.isEditable;
|
||||
}
|
||||
|
||||
@end
|
||||
-85
@@ -1,85 +0,0 @@
|
||||
<?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="iN0-l3-epB" customClass="MXKRoomInputToolbarViewWithHPGrowingText">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="41"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="contactAdd" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Hga-l8-Wua" userLabel="left Button">
|
||||
<rect key="frame" x="8" y="0.0" width="35" height="41"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="35" id="ptO-BQ-NhS"/>
|
||||
</constraints>
|
||||
<state key="normal">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onTouchUpInside:" destination="iN0-l3-epB" eventType="touchUpInside" id="jVG-We-DmS"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Owf-M8-qJi" userLabel="right Button">
|
||||
<rect key="frame" x="552" y="0.0" width="44" height="41"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="44" id="9FZ-CI-diT"/>
|
||||
</constraints>
|
||||
<state key="normal" title="Send">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onTouchUpInside:" destination="iN0-l3-epB" eventType="touchUpInside" id="jed-Mz-rxe"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QWp-NV-uh5" userLabel="Message Composer Container">
|
||||
<rect key="frame" x="51" y="4" width="497" height="33"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="pkf-YH-tco" customClass="HPGrowingTextView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="497" height="33"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="pkf-YH-tco" secondAttribute="bottom" id="L3A-Oo-Ml2"/>
|
||||
<constraint firstItem="pkf-YH-tco" firstAttribute="top" secondItem="QWp-NV-uh5" secondAttribute="top" id="VPn-k0-0vc"/>
|
||||
<constraint firstItem="pkf-YH-tco" firstAttribute="leading" secondItem="QWp-NV-uh5" secondAttribute="leading" id="mXj-f3-DcT"/>
|
||||
<constraint firstAttribute="trailing" secondItem="pkf-YH-tco" secondAttribute="trailing" id="n4K-Do-gHr"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.89720267057418823" green="0.89720267057418823" blue="0.89720267057418823" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="Owf-M8-qJi" secondAttribute="trailing" constant="4" id="2M8-Gu-0f6"/>
|
||||
<constraint firstItem="QWp-NV-uh5" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="4" id="570-8j-VYY"/>
|
||||
<constraint firstAttribute="bottom" secondItem="QWp-NV-uh5" secondAttribute="bottom" constant="4" id="9Ya-0H-03W"/>
|
||||
<constraint firstItem="Hga-l8-Wua" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="8" id="Bc8-T7-wmA"/>
|
||||
<constraint firstItem="Hga-l8-Wua" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="Cvk-xZ-ODy"/>
|
||||
<constraint firstItem="Owf-M8-qJi" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="SV8-U3-8dd"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Hga-l8-Wua" secondAttribute="bottom" id="Slr-2H-laO"/>
|
||||
<constraint firstItem="Owf-M8-qJi" firstAttribute="leading" secondItem="QWp-NV-uh5" secondAttribute="trailing" constant="4" id="UEd-gb-jgR"/>
|
||||
<constraint firstItem="QWp-NV-uh5" firstAttribute="leading" secondItem="Hga-l8-Wua" secondAttribute="trailing" constant="8" id="cCr-Am-M7d"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Owf-M8-qJi" secondAttribute="bottom" id="ycc-x9-PAv"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="growingTextView" destination="pkf-YH-tco" id="VeP-WI-6Xh"/>
|
||||
<outlet property="leftInputToolbarButton" destination="Hga-l8-Wua" id="zbm-3b-hoY"/>
|
||||
<outlet property="messageComposerContainer" destination="QWp-NV-uh5" id="7EX-Un-ZIe"/>
|
||||
<outlet property="messageComposerContainerBottomConstraint" destination="9Ya-0H-03W" id="226-iu-6tU"/>
|
||||
<outlet property="messageComposerContainerTopConstraint" destination="570-8j-VYY" id="VKv-Qh-PCs"/>
|
||||
<outlet property="rightInputToolbarButton" destination="Owf-M8-qJi" id="seO-ly-Bgg"/>
|
||||
</connections>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
Reference in New Issue
Block a user