Merge branch gil/5230_SP2-Adding_Rooms_to_Spaces into gil/5231_SP3-1_Update_room_settings_for_Spaces

This commit is contained in:
Gil Eluard
2022-02-05 21:43:29 +01:00
548 changed files with 20963 additions and 3538 deletions
@@ -504,3 +504,5 @@
"attachment_unsupported_preview_message" = "Dieser Dateityp wird nicht unterstützt.";
"attachment_unsupported_preview_title" = "Vorschau kann nicht angezeigt werden";
"auth_reset_password_error_unauthorized" = "Nicht Authorisiert";
"message_reply_to_sender_sent_their_location" = "hat den eigenen Standort geteilt.";
"room_displayname_all_other_members_left" = "%@ (Verlassen)";
@@ -270,7 +270,7 @@
// Language picker
"language_picker_title" = "Vali keel";
"language_picker_default_language" = "Vaikimisi (%@)";
"notice_room_invite" = "%@ kutsus kasutajat %@";
"notice_room_invite" = "%@ saatis kutse kasutajale %@";
"notice_room_third_party_invite" = "%@ saatis kasutajale %@ kutse jututoaga liitumiseks";
"notice_room_third_party_registered_invite" = "%@ võttis vastu kutse %@ nimel";
"notice_room_third_party_revoked_invite" = "%@ võttis tagasi jututoaga liitumise kutse kasutajalt %@";
@@ -433,7 +433,7 @@
"notice_room_history_visible_to_members_from_invited_point_for_dm" = "%@ määras, et jututoa tulevane ajalugu on nähtav kõikidele selle liikmetele liitumiskutse saatmise hetkest.";
"notice_room_history_visible_to_members_from_joined_point_for_dm" = "%@ määras, et jututoa tulevane ajalugu on nähtav kõikidele selle liikmetele nende liitumise hetkest.";
"room_left_for_dm" = "Sina lahkusid";
"notice_room_third_party_invite_for_dm" = "%@ kutsus kasutajat %@";
"notice_room_third_party_invite_for_dm" = "%@ saatis kutse kasutajale %@";
"notice_room_third_party_revoked_invite_for_dm" = "%@ võttis tagasi kasutaja %@ kutse";
"notice_room_name_changed_for_dm" = "%@ muutis jututoa uueks nimeks %@.";
"notice_room_third_party_invite_by_you_for_dm" = "Sina kutsusid kasutajat %@";
@@ -478,3 +478,4 @@
"room_displayname_all_other_members_left" = "%@ (lahkus(id))";
"attachment_unsupported_preview_message" = "See failitüüp ei ole toetatud.";
"attachment_unsupported_preview_title" = "Eelvaate kuvamine ei õnnestu";
"message_reply_to_sender_sent_their_location" = "on jaganud oma asukohta.";
@@ -275,7 +275,7 @@
"notice_conference_call_started" = "Téléconférence en VoIP démarrée";
"notice_conference_call_finished" = "Téléconférence en VoIP terminée";
// button names
"ok" = "OK";
"ok" = "Ok";
"send" = "Envoyer";
"copy_button_name" = "Copier";
"resend" = "Renvoyer";
@@ -283,7 +283,7 @@
"notice_conference_call_started" = "VoIP konferencia indult";
"notice_conference_call_finished" = "VoIP konferencia befejeződött";
// button names
"ok" = "Rendben";
"ok" = "OK";
"cancel" = "Mégse";
"save" = "Ment";
"send" = "Küld";
@@ -479,3 +479,4 @@
"room_displayname_all_other_members_left" = "%@ (Bal)";
"attachment_unsupported_preview_message" = "Ez a fájl típus nem támogatott.";
"attachment_unsupported_preview_title" = "Az előnézetet nem lehet megjeleníteni";
"message_reply_to_sender_sent_their_location" = "megosztotta a földrajzi helyzetét.";
@@ -249,7 +249,7 @@
// titles
// button names
"ok" = "OK";
"ok" = "OKE";
"notice_room_history_visible_to_members_from_joined_point_by_you_for_dm" = "Anda membuat sejarah pesan di masa mendatang dapat dilihat oleh semuanya, sejak mereka bergabung.";
"notice_room_history_visible_to_members_from_joined_point_by_you" = "Anda membuat sejarah ruangan di masa mendatang dapat dilihat oleh semua anggota ruang, sejak mereka bergabung.";
"notice_encryption_enabled_unknown_algorithm_by_you" = "Anda mengaktifkan enkripsi ujung-ke-ujung (algoritma %@ tidak dikenal).";
@@ -557,3 +557,4 @@
"notice_room_power_level_acting_requirement" = "Tingkat daya minimum yang harus dimiliki pengguna sebelum bertindak adalah:";
"attachment_unsupported_preview_title" = "Tidak dapat ditampilkan";
"attachment_unsupported_preview_message" = "Tipe file ini tidak didukung.";
"message_reply_to_sender_sent_their_location" = "telah membagikan lokasinya.";
@@ -478,3 +478,4 @@
"rename" = "Rinomina";
"attachment_unsupported_preview_message" = "Questo tipo di file non è supportato.";
"attachment_unsupported_preview_title" = "Anteprima non disponibile";
"message_reply_to_sender_sent_their_location" = "ha condiviso la sua posizione.";
@@ -311,7 +311,7 @@
// titles
// button names
"ok" = "O";
"ok" = "OK";
"cancel" = "Annuleren";
"save" = "Opslaan";
"leave" = "Verlaten";
@@ -540,3 +540,4 @@
"room_displayname_all_other_members_left" = "%@ (Vertrok)";
"auth_username_in_use" = "Inlognaam al in gebruik";
"rename" = "Hernoemen";
"message_reply_to_sender_sent_their_location" = "deelde hun locatie.";
@@ -496,3 +496,12 @@
"attachment_small_with_resolution" = "Mały %@ (~%@)";
"attachment_size_prompt_message" = "Możesz to wyłączyć w ustawieniach.";
"attachment_size_prompt_title" = "Potwierdź rozmiar, który chcesz wysłać";
"auth_reset_password_error_not_found" = "Nie znaleziono";
"auth_reset_password_error_unauthorized" = "Brak autoryzacji";
"auth_invalid_user_name" = "Niepoprawna nazwa użytkownika";
"attachment_unsupported_preview_message" = "Ten format pliku nie jest obsługiwany.";
"attachment_unsupported_preview_title" = "Brak podglądu";
"message_reply_to_sender_sent_their_location" = "udostępnił(-a) swoją lokację.";
"room_displayname_all_other_members_left" = "%@ (Opuścił-a)";
"auth_username_in_use" = "Nazwa użytkownika jest już używana";
"rename" = "Zmień nazwę";
@@ -121,7 +121,7 @@
"room_displayname_two_members" = "%@ e %@";
"room_displayname_more_than_two_members" = "%@ e %@ outros";
// Settings
"settings" = "Configurações";
"settings" = "Ajustes";
"settings_enable_inapp_notifications" = "Habilitar notificações Em-App";
"settings_enable_push_notifications" = "Habilitar notificações push";
"settings_enter_validation_token_for" = "Entrar token de validação para %@:";
@@ -157,7 +157,7 @@
"room_event_encryption_info_block" = "Adicionar à lista negra";
"room_event_encryption_info_unblock" = "Remover da lista negra";
"room_event_encryption_verify_title" = "Verificar sessão\n\n";
"room_event_encryption_verify_message" = "Para verificar que esta sessão pode ser confiada, por favor contacte a/o dona(o) dela usando alguma outro meio (e.g. em pessoa ou uma chamada de telefone) e pergunte-lhe se a chave que ela/ele vê em suas Configurações de Usuária(o) para esta sessão bate com a chave abaixo:\n\n\tNome de sessão: %@\n\tID de sessão: %@\n\tChave de sessão: %@\n\nSe ela bate, pressione o botão verificar abaixo. Se não bate, então alguma outra pessoa está interceptando esa sessão e você provavelmente quer pressionar o botão adicionar à lista negra em vez disso.\n\nNo futuro este processo de verificação vai ser mais sofisticado.";
"room_event_encryption_verify_message" = "Para verificar que esta sessão pode ser confiada, por favor contacte a/o dona(o) dela usando alguma outro meio (e.g. em pessoa ou uma chamada de telefone) e pergunte-lhe se a chave que ela/ele vê em seus Ajustes de Usuária(o) para esta sessão corresponde com a chave abaixo:\n\n\tNome de sessão: %@\n\tID de sessão: %@\n\tChave de sessão: %@\n\nSe ela corresponde, pressione o botão verificar abaixo. Se não corresponde, então alguma outra pessoa está interceptando esa sessão e você provavelmente quer pressionar o botão adicionar à lista negra em vez disso.\n\nNo futuro este processo de verificação vai ser mais sofisticado.";
"room_event_encryption_verify_ok" = "Verificar";
// Account
"account_save_changes" = "Salvar mudanças";
@@ -331,9 +331,9 @@
"notification_settings_disable_all" = "Desabilitar todas as notificações";
"notification_settings_enable_notifications" = "Habilitar notificações";
"notification_settings_enable_notifications_warning" = "Todas as notificações estão atualmente desabilitadas para todos os dispositivos.";
"notification_settings_global_info" = "Configurações de notificação são salvas em sua conta de usuária(o) e são compartilhadas entre todos os clientes que as suportam (incluindo notificações de desktop).\n\nRegras são aplicadas em ordem; a primeira regra que corresponde define o resultado da mensagem.\nEntão: Notificações per-palavra são mais importantes que notificações per-sala que são mais importantes que notificações per-enviador(a).\nPara múltiplas regras do mesmo tipo, a primeira na lista que corresponde leva prioridade.";
"notification_settings_global_info" = "Ajustes de notificação são salvas em sua conta de usuária(o) e são compartilhadas entre todos os clientes que as suportam (incluindo notificações de desktop).\n\nRegras são aplicadas em ordem; a primeira regra que corresponde define o resultado da mensagem.\nEntão: Notificações per-palavra são mais importantes que notificações per-sala que são mais importantes que notificações per-enviador(a).\nPara múltiplas regras do mesmo tipo, a primeira na lista que corresponde leva prioridade.";
"notification_settings_per_word_notifications" = "Notificações per-palavra";
"notification_settings_per_word_info" = "Palavras correspondem insensivelmente a maiúsculas e minúsculas, e podem incluir um wildcard *. Então:\nfoo corresponde a string foo rodeado por delimitadores de palavras (e.g., pontuação e whitespace ou início/fim de linha).\nfoo* corresponde a qualquer palavra que começa foo.\n*foo* corresponde a qualquer palavra que inclui as 3 letras foo.";
"notification_settings_per_word_info" = "Palavras correspondem insensivelmente com maiúsculas e minúsculas, e podem incluir um wildcard *. Então:\nfoo corresponde com string foo rodeado por delimitadores de palavras (e.g., pontuação e whitespace ou início/fim de linha).\nfoo* corresponde com qualquer palavra que começa foo.\n*foo* corresponde com qualquer palavra que inclui as 3 letras foo.";
"notification_settings_always_notify" = "Sempre notificar";
"notification_settings_never_notify" = "Nunca notificar";
"notification_settings_word_to_match" = "palavra para corresponder";
@@ -372,10 +372,10 @@
"ssl_fingerprint_hash" = "Impressão digital (%@):";
"ssl_could_not_verify" = "Não foi possível verificar identidade de servidor remoto.";
"ssl_cert_not_trust" = "Isto pode significar que alguém está interceptando maliciosamente seu tráfico, ou que seu telefone não confia no certificado provido pelo servidor remoto.";
"ssl_cert_new_account_expl" = "Se o/a administrador(a) do servidor tem dito que isto é esperado, assegure-se que a impressão digital abaixo bate com a impressão digital provida por ele(a).";
"ssl_cert_new_account_expl" = "Se o/a administrador(a) do servidor tem dito que isto é esperado, assegure-se que a impressão digital abaixo corresponde com a impressão digital provida por ele(a).";
"ssl_unexpected_existing_expl" = "O certificado tem mudado de um que esta confiado por seu telefone. Isto é ALTAMENTE INCOMUM. É recomendado que você NÃO ACEITE este novo certificado.";
"ssl_expected_existing_expl" = "O certificado tem sido mudado de um previamente confiado para um que não é confiado. O servidor pode ter renovado o certificado dele. Contacte o/a administrador(a) do servidor a impressão digital esperada.";
"ssl_only_accept" = "SOMENTE aceite o certificado se o/a administrador(a) do servidor tem publicado uma impressão digital que corresponde à acima.";
"ssl_only_accept" = "SOMENTE aceite o certificado se o/a administrador(a) do servidor tem publicado uma impressão digital que corresponde com a acima.";
"notice_encryption_enabled_ok" = "%@ ativou encriptação ponta-a-ponta.";
"notice_encryption_enabled_unknown_algorithm" = "%1$@ ativou encriptação ponta-a-ponta (algoritmo não-reconhecido %2$@).";
"device_details_rename_prompt_title" = "Nome da Sessão";
@@ -468,7 +468,7 @@
"attachment_large_with_resolution" = "Grande %@ (~%@)";
"attachment_medium_with_resolution" = "Médio %@ (~%@)";
"attachment_small_with_resolution" = "Pequeno %@ (~%@)";
"attachment_size_prompt_message" = "Você pode desligar isto em configurações.";
"attachment_size_prompt_message" = "Você pode desligar isto em ajustes.";
"attachment_size_prompt_title" = "Confirmar tamanho para enviar";
"room_displayname_all_other_participants_left" = "%@ (Saiu)";
"auth_reset_password_error_not_found" = "Não encontrado";
@@ -479,3 +479,4 @@
"room_displayname_all_other_members_left" = "%@ (Saiu)";
"attachment_unsupported_preview_message" = "Este tipo de arquivo não é suportado.";
"attachment_unsupported_preview_title" = "Incapaz de previsualizar";
"message_reply_to_sender_sent_their_location" = "tem compartilhado a localização dela(e).";
@@ -543,3 +543,7 @@
"notice_feedback" = "Udalosť spätnej väzby (id: %@): %@";
"resume_call" = "Pokračovať";
"answer_call" = "Prijať hovor";
"message_reply_to_sender_sent_their_location" = "zdieľal/a svoju polohu.";
"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";
@@ -478,3 +478,4 @@
"attachment_unsupported_preview_message" = "Ky lloj kartele nuk mbulohet.";
"attachment_unsupported_preview_title" = "Sarrihet të bëhet paraparje";
"room_displayname_all_other_members_left" = "%@ (Iku)";
"message_reply_to_sender_sent_their_location" = "ka dhënë vendndodhjen e vet.";
@@ -478,3 +478,4 @@
"attachment_unsupported_preview_message" = "Den här filtypen stöds inte.";
"attachment_unsupported_preview_title" = "Kunde inte förhandsgranska";
"room_displayname_all_other_members_left" = "%@ (Kvar)";
"message_reply_to_sender_sent_their_location" = "har delat sin plats.";
@@ -558,3 +558,4 @@
"local_contacts_access_not_granted" = "Для пошуку користувачів серед локальних контактів потрібен доступ до ваших контактів, але %@ не має такого дозволу";
"e2e_export_prompt" = "Це дає змогу експортувати в локальний файл ключі до повідомлень, отриманих вами в зашифрованих кімнатах. Тоді ви зможете імпортувати файл до іншого клієнта Matrix у майбутньому, і той клієнт також зможе розшифрувати ці повідомлення.\nЕкспортований файл дасть змогу всім, хто його прочитає, розшифрувати всі видимі вам зашифровані повідомлення.";
"e2e_import_prompt" = "Це дає змогу імпортувати ключі шифрування, які ви раніше експортували з іншого клієнта Matrix. Тоді ви зможете розшифрувати всі повідомлення, які міг розшифрувати той клієнт.\nФайл експорту захищений парольною фразою. Введіть парольну фразу сюди, щоб розшифрувати файл.";
"message_reply_to_sender_sent_their_location" = "надсилає дані про своє місцеперебування.";
@@ -193,8 +193,11 @@
/**
Handle supported flows and associated information returned by the homeserver.
@param authSession The session to be handled.
@param fallbackSSOFlow A fallback SSO flow to be shown when the session has none
e.g. A login SSO flow that can be shown for a registration session.
*/
- (void)handleAuthenticationSession:(MXAuthenticationSession *)authSession;
- (void)handleAuthenticationSession:(MXAuthenticationSession *)authSession withFallbackSSOFlow:(MXLoginSSOFlow *)fallbackSSOFlow;
/**
Customize the MXHTTPClientOnUnrecognizedCertificate block that will be used to handle unrecognized certificate observed during authentication challenge from a server.
@@ -219,6 +222,11 @@
*/
- (void)testUserRegistration:(void (^)(MXError *mxError))callback;
/**
Searches an array of `MXLoginFlow` returning the first valid `MXLoginSSOFlow` found.
*/
- (MXLoginSSOFlow*)loginSSOFlowWithProvidersFromFlows:(NSArray<MXLoginFlow*>*)loginFlows;
/**
Action registered on the following events:
- 'UIControlEventTouchUpInside' for each UIButton instance.
@@ -621,41 +621,51 @@
// Reset potential authentication fallback url
authenticationFallback = nil;
if (mxRestClient)
if (mxRestClient && (self.authType == MXKAuthenticationTypeLogin || self.authType == MXKAuthenticationTypeRegister))
{
if (_authType == MXKAuthenticationTypeLogin)
{
mxCurrentOperation = [mxRestClient getLoginSession:^(MXAuthenticationSession* authSession) {
[self handleAuthenticationSession:authSession];
} failure:^(NSError *error) {
[self onFailureDuringMXOperation:error];
}];
}
else if (_authType == MXKAuthenticationTypeRegister)
{
mxCurrentOperation = [mxRestClient getRegisterSession:^(MXAuthenticationSession* authSession){
[self handleAuthenticationSession:authSession];
} failure:^(NSError *error){
[self onFailureDuringMXOperation:error];
}];
}
else
{
// Not supported for other types
MXLogDebug(@"[MXKAuthenticationVC] refreshAuthenticationSession is ignored");
}
MXWeakify(self);
// Get the login session to determine available SSO flows.
mxCurrentOperation = [mxRestClient getLoginSession:^(MXAuthenticationSession* loginAuthSession) {
MXStrongifyAndReturnIfNil(self);
if (self.authType == MXKAuthenticationTypeRegister)
{
MXWeakify(self);
self->mxCurrentOperation = [self->mxRestClient getRegisterSession:^(MXAuthenticationSession* registerAuthSession) {
MXStrongifyAndReturnIfNil(self);
// Handle the register session along with any SSO flows from the login session
MXLoginSSOFlow *loginSSOFlow = [self loginSSOFlowWithProvidersFromFlows:loginAuthSession.flows];
[self handleAuthenticationSession:registerAuthSession withFallbackSSOFlow:loginSSOFlow];
} failure:^(NSError *error) {
[self onFailureDuringMXOperation:error];
}];
}
else
{
// Handle the login session by itself
[self handleAuthenticationSession:loginAuthSession withFallbackSSOFlow:nil];
}
} failure:^(NSError *error) {
MXStrongifyAndReturnIfNil(self);
[self onFailureDuringMXOperation:error];
}];
}
else
{
// Not supported for other types
MXLogDebug(@"[MXKAuthenticationVC] refreshAuthenticationSession is ignored");
}
}
- (void)handleAuthenticationSession:(MXAuthenticationSession *)authSession
- (void)handleAuthenticationSession:(MXAuthenticationSession *)authSession withFallbackSSOFlow:(MXLoginSSOFlow *)fallbackSSOFlow
{
mxCurrentOperation = nil;
@@ -901,6 +911,27 @@
mxCurrentOperation = [mxRestClient testUserRegistration:self.authInputsView.userId callback:callback];
}
- (MXLoginSSOFlow*)loginSSOFlowWithProvidersFromFlows:(NSArray<MXLoginFlow*>*)loginFlows
{
MXLoginSSOFlow *ssoFlowWithProviders;
for (MXLoginFlow *loginFlow in loginFlows)
{
if ([loginFlow isKindOfClass:MXLoginSSOFlow.class])
{
MXLoginSSOFlow *ssoFlow = (MXLoginSSOFlow *)loginFlow;
if (ssoFlow.identityProviders.count)
{
ssoFlowWithProviders = ssoFlow;
break;
}
}
}
return ssoFlowWithProviders;
}
- (IBAction)onButtonPressed:(id)sender
{
[self dismissKeyboard];
@@ -1451,7 +1482,9 @@
MXRestClient *mxRestClient = [[MXRestClient alloc] initWithCredentials:credentials andOnUnrecognizedCertificateBlock:^BOOL(NSData *certificate) {
return NO;
}];
} andPersistentTokenDataHandler:^(void (^handler)(NSArray<MXCredentials *> *credentials, void (^completion)(BOOL didUpdateCredentials))) {
[[MXKAccountManager sharedManager] readAndWriteCredentials:handler];
} andUnauthenticatedHandler: nil];
MXWeakify(self);
[[MXKAccountManager sharedManager].dehydrationService rehydrateDeviceWithMatrixRestClient:mxRestClient dehydrationKey:keyData success:^(NSString * deviceId) {
@@ -1197,7 +1197,7 @@ static const CGFloat kLocalPreviewMargin = 20;
if (roomListener && mxCall.room)
{
MXWeakify(self);
[mxCall.room liveTimeline:^(MXEventTimeline *liveTimeline) {
[mxCall.room liveTimeline:^(id<MXEventTimeline> liveTimeline) {
MXStrongifyAndReturnIfNil(self);
[liveTimeline removeListener:self->roomListener];
@@ -117,7 +117,7 @@ typedef enum : NSUInteger
*/
@property (nonatomic, readonly) MXRoomMember *mxRoomMember;
@property (nonatomic, readonly) MXRoom *mxRoom;
@property (nonatomic, readonly) MXEventTimeline *mxRoomLiveTimeline;
@property (nonatomic, readonly) id<MXEventTimeline> mxRoomLiveTimeline;
/**
Enable mention option. NO by default
@@ -44,7 +44,7 @@
id roomDidFlushDataNotificationObserver;
// Cache for the room live timeline
MXEventTimeline *mxRoomLiveTimeline;
id<MXEventTimeline> mxRoomLiveTimeline;
}
@end
@@ -134,7 +134,7 @@
mxRoom = room;
MXWeakify(self);
[mxRoom liveTimeline:^(MXEventTimeline *liveTimeline) {
[mxRoom liveTimeline:^(id<MXEventTimeline> liveTimeline) {
MXStrongifyAndReturnIfNil(self);
self->mxRoomLiveTimeline = liveTimeline;
@@ -152,7 +152,7 @@
}];
}
- (MXEventTimeline *)mxRoomLiveTimeline
- (id<MXEventTimeline> )mxRoomLiveTimeline
{
// @TODO(async-state): Just here for dev
NSAssert(mxRoomLiveTimeline, @"[MXKRoomMemberDetailsViewController] Room live timeline must be preloaded before accessing to MXKRoomMemberDetailsViewController.mxRoomLiveTimeline");
@@ -569,7 +569,7 @@
if (membersListener && mxRoom)
{
MXWeakify(self);
[mxRoom liveTimeline:^(MXEventTimeline *liveTimeline) {
[mxRoom liveTimeline:^(id<MXEventTimeline> liveTimeline) {
MXStrongifyAndReturnIfNil(self);
[liveTimeline removeListener:self->membersListener];
@@ -70,7 +70,7 @@
if (roomListener)
{
MXWeakify(self);
[mxRoom liveTimeline:^(MXEventTimeline *liveTimeline) {
[mxRoom liveTimeline:^(id<MXEventTimeline> liveTimeline) {
MXStrongifyAndReturnIfNil(self);
[liveTimeline removeListener:self->roomListener];
@@ -139,7 +139,7 @@
{
// Register a listener to handle messages related to room name, topic...
MXWeakify(self);
[mxRoom liveTimeline:^(MXEventTimeline *liveTimeline) {
[mxRoom liveTimeline:^(id<MXEventTimeline> liveTimeline) {
MXStrongifyAndReturnIfNil(self);
self->roomListener = [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomName, kMXEventTypeStringRoomTopic, kMXEventTypeStringRoomAliases, kMXEventTypeStringRoomAvatar, kMXEventTypeStringRoomPowerLevels, kMXEventTypeStringRoomCanonicalAlias, kMXEventTypeStringRoomJoinRules, kMXEventTypeStringRoomGuestAccess, kMXEventTypeStringRoomHistoryVisibility] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) {
@@ -1,440 +0,0 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2019 The Matrix.org Foundation C.I.C
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 "MXKViewController.h"
#import "MXKRoomDataSource.h"
#import "MXKRoomTitleView.h"
#import "MXKRoomInputToolbarView.h"
#import "MXKRoomActivitiesView.h"
#import "MXKEventDetailsView.h"
#import "MXKAttachmentsViewController.h"
#import "MXKAttachmentAnimator.h"
typedef NS_ENUM(NSUInteger, MXKRoomViewControllerJoinRoomResult) {
MXKRoomViewControllerJoinRoomResultSuccess,
MXKRoomViewControllerJoinRoomResultFailureRoomEmpty,
MXKRoomViewControllerJoinRoomResultFailureJoinInProgress,
MXKRoomViewControllerJoinRoomResultFailureGeneric
};
/**
This view controller displays messages of a room. Only one matrix session is handled by this view controller.
*/
@interface MXKRoomViewController : MXKViewController <MXKDataSourceDelegate, MXKRoomTitleViewDelegate, MXKRoomInputToolbarViewDelegate, UITableViewDelegate, UIDocumentInteractionControllerDelegate, MXKAttachmentsViewControllerDelegate, MXKRoomActivitiesViewDelegate, MXKSourceAttachmentAnimatorDelegate>
{
@protected
/**
The identifier of the current event displayed at the bottom of the table (just above the toolbar).
Use to anchor the message displayed at the bottom during table refresh.
*/
NSString *currentEventIdAtTableBottom;
/**
Boolean value used to scroll to bottom the bubble history after refresh.
*/
BOOL shouldScrollToBottomOnTableRefresh;
/**
Potential event details view.
*/
__weak MXKEventDetailsView *eventDetailsView;
/**
Current alert (if any).
*/
__weak UIAlertController *currentAlert;
/**
The document interaction Controller used to share attachment
*/
UIDocumentInteractionController *documentInteractionController;
/**
The current shared attachment.
*/
MXKAttachment *currentSharedAttachment;
/**
The potential text input placeholder is saved when it is replaced temporarily
*/
NSString *savedInputToolbarPlaceholder;
/**
Tell whether the input toolbar required to run an animation indicator.
*/
BOOL isInputToolbarProcessing;
/**
Tell whether a device rotation is in progress
*/
BOOL isSizeTransitionInProgress;
/**
The current visibility of the status bar in this view controller.
*/
BOOL isStatusBarHidden;
/**
YES to prevent `bubblesTableView` scrolling when calling -[setBubbleTableViewContentOffset:animated:]
*/
BOOL preventBubblesTableViewScroll;
}
/**
The current data source associated to the view controller.
*/
@property (nonatomic, readonly) MXKRoomDataSource *roomDataSource;
/**
Flag indicating if this instance has the memory ownership of its `roomDataSource`.
If YES, it will release it on [self destroy] call;
Default is NO.
*/
@property (nonatomic) BOOL hasRoomDataSourceOwnership;
/**
Tell whether the bubbles table view display is in transition. Its display is not warranty during the transition.
*/
@property (nonatomic, getter=isBubbleTableViewDisplayInTransition) BOOL bubbleTableViewDisplayInTransition;
/**
Tell whether the automatic events acknowledgement (based on read receipt) is enabled.
Default is YES.
*/
@property (nonatomic, getter=isEventsAcknowledgementEnabled) BOOL eventsAcknowledgementEnabled;
/**
Tell whether the room read marker must be updated when an event is acknowledged with a read receipt.
Default is NO.
*/
@property (nonatomic) BOOL updateRoomReadMarker;
/**
When the room view controller displays a room data source based on a timeline with an initial event,
the bubble table view content is scrolled by default to display the top of this event at the center of the screen
the first time it appears.
Use this property to force the table view to center its content on the bottom part of the event.
Default is NO.
*/
@property (nonatomic) BOOL centerBubblesTableViewContentOnTheInitialEventBottom;
/**
The current title view defined into the view controller.
*/
@property (nonatomic, weak, readonly) MXKRoomTitleView* titleView;
/**
The current input toolbar view defined into the view controller.
*/
@property (nonatomic, weak, readonly) MXKRoomInputToolbarView* inputToolbarView;
/**
The current extra info view defined into the view controller.
*/
@property (nonatomic, readonly) MXKRoomActivitiesView* activitiesView;
/**
The threshold used to trigger inconspicuous back pagination, or forwards pagination
for non live timeline. A pagination is triggered when the vertical content offset
is lower this threshold.
Default is 300.
*/
@property (nonatomic) NSUInteger paginationThreshold;
/**
The maximum number of messages to retrieve during a pagination. Default is 30.
*/
@property (nonatomic) NSUInteger paginationLimit;
/**
Enable/disable saving of the current typed text in message composer when view disappears.
The message composer is prefilled with this text when the room is opened again.
This property value is YES by default.
*/
@property BOOL saveProgressTextInput;
/**
The invited rooms can be automatically joined when the data source is ready.
This property enable/disable this option. Its value is YES by default.
*/
@property BOOL autoJoinInvitedRoom;
/**
Tell whether the room history is automatically scrolled to the most recent messages
when a keyboard is presented. YES by default.
This option is ignored when an alert is presented.
*/
@property BOOL scrollHistoryToTheBottomOnKeyboardPresentation;
/**
YES (default) to show actions button in document preview. NO otherwise.
*/
@property BOOL allowActionsInDocumentPreview;
/**
Duration of the animation in case of the composer needs to be resized (default 0.3s)
*/
@property NSTimeInterval resizeComposerAnimationDuration;
/**
This object is defined when the displayed room is left. It is added into the bubbles table header.
This label is used to display the reason why the room has been left.
*/
@property (nonatomic, weak, readonly) UILabel *leftRoomReasonLabel;
@property (weak, nonatomic) IBOutlet UITableView *bubblesTableView;
@property (weak, nonatomic) IBOutlet UIView *roomTitleViewContainer;
@property (weak, nonatomic) IBOutlet UIView *roomInputToolbarContainer;
@property (weak, nonatomic) IBOutlet UIView *roomActivitiesContainer;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bubblesTableViewTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bubblesTableViewBottomConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *roomActivitiesContainerHeightConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *roomInputToolbarContainerHeightConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *roomInputToolbarContainerBottomConstraint;
#pragma mark - Class methods
/**
Returns the `UINib` object initialized for a `MXKRoomViewController`.
@return The initialized `UINib` object or `nil` if there were errors during initialization
or the nib file could not be located.
@discussion You may override this method to provide a customized nib. If you do,
you should also override `roomViewController` to return your
view controller loaded from your custom nib.
*/
+ (UINib *)nib;
/**
Creates and returns a new `MXKRoomViewController` object.
@discussion This is the designated initializer for programmatic instantiation.
@return An initialized `MXKRoomViewController` object if successful, `nil` otherwise.
*/
+ (instancetype)roomViewController;
/**
Display a room.
@param dataSource the data source .
*/
- (void)displayRoom:(MXKRoomDataSource*)dataSource;
/**
This method is called when the associated data source is ready.
By default this operation triggers the initial back pagination when the user is an actual
member of the room (membership = join).
The invited rooms are automatically joined during this operation if 'autoJoinInvitedRoom' is YES.
When the room is successfully joined, an initial back pagination is triggered too.
Else nothing is done for the invited rooms.
Override it to customize the view controller behavior when the data source is ready.
*/
- (void)onRoomDataSourceReady;
/**
Update view controller appearance according to the state of its associated data source.
This method is called in the following use cases:
- on data source change (see `[MXKRoomViewController displayRoom:]`).
- on data source state change (see `[MXKDataSourceDelegate dataSource:didStateChange:]`)
- when view did appear.
The default implementation:
- show input toolbar view if the dataSource is defined and ready (`MXKDataSourceStateReady`), hide toolbar in others use cases.
- stop activity indicator if the dataSource is defined and ready (`MXKDataSourceStateReady`).
- update view controller title with room information.
Override it to customize view appearance according to data source state.
*/
- (void)updateViewControllerAppearanceOnRoomDataSourceState;
/**
This method is called when the associated data source has encountered an error on the timeline.
Override it to customize the view controller behavior.
@param notif the notification data sent with kMXKRoomDataSourceTimelineError notif.
*/
- (void)onTimelineError:(NSNotification *)notif;
/**
Join the current displayed room.
This operation fails if the user has already joined the room, or if the data source is not ready.
It fails if a join request is already running too.
@param completion the block to execute at the end of the operation.
You may specify nil for this parameter.
*/
- (void)joinRoom:(void(^)(MXKRoomViewControllerJoinRoomResult result))completion;
/**
Join a room with a room id or an alias.
This operation fails if the user has already joined the room, or if the data source is not ready,
or if the access to the room is forbidden to the user.
It fails if a join request is already running too.
@param roomIdOrAlias the id or the alias of the room to join.
@param viaServers The server names to try and join through in addition to those that are automatically chosen. It is optional and can be nil.
@param signUrl the signurl paramater passed with a 3PID invitation. It is optional and can be nil.
@param completion the block to execute at the end of the operation.
You may specify nil for this parameter.
*/
- (void)joinRoomWithRoomIdOrAlias:(NSString*)roomIdOrAlias
viaServers:(NSArray<NSString*>*)viaServers
andSignUrl:(NSString*)signUrl
completion:(void(^)(MXKRoomViewControllerJoinRoomResult result))completion;
/**
Update view controller appearance when the user is about to leave the displayed room.
This method is called when the user will leave the current room (see `kMXSessionWillLeaveRoomNotification`).
The default implementation:
- discard `roomDataSource`
- hide input toolbar view
- freeze the room title display
- add a label (`leftRoomReasonLabel`) in bubbles table header to display the reason why the room has been left.
Override it to customize view appearance, or to withdraw the view controller.
@param event the MXEvent responsible for the leaving.
*/
- (void)leaveRoomOnEvent:(MXEvent*)event;
/**
Register the class used to instantiate the title view which will handle the room name display.
The resulting view is added into 'roomTitleViewContainer' view, which must be defined before calling this method.
Note: By default the room name is displayed by using 'navigationItem.title' field of the view controller.
@param roomTitleViewClass a MXKRoomTitleView-inherited class.
*/
- (void)setRoomTitleViewClass:(Class)roomTitleViewClass;
/**
Register the class used to instantiate the input toolbar view which will handle message composer
and attachments selection for the room.
The resulting view is added into 'roomInputToolbarContainer' view, which must be defined before calling this method.
@param roomInputToolbarViewClass a MXKRoomInputToolbarView-inherited class, or nil to remove the current view.
*/
- (void)setRoomInputToolbarViewClass:(Class)roomInputToolbarViewClass;
/**
Register the class used to instantiate the extra info view.
The resulting view is added into 'roomActivitiesContainer' view, which must be defined before calling this method.
@param roomActivitiesViewClass a MXKRoomActivitiesViewClass-inherited class, or nil to remove the current view.
*/
- (void)setRoomActivitiesViewClass:(Class)roomActivitiesViewClass;
/**
Register the class used to instantiate the viewer dedicated to the attachments with thumbnail.
By default 'MXKAttachmentsViewController' class is used.
@param attachmentsViewerClass a MXKAttachmentsViewController-inherited class, or nil to restore the default class.
*/
- (void)setAttachmentsViewerClass:(Class)attachmentsViewerClass;
/**
Register the view class used to display the details of an event.
MXKEventDetailsView is used by default.
@param eventDetailsViewClass a MXKEventDetailsView-inherited class.
*/
- (void)setEventDetailsViewClass:(Class)eventDetailsViewClass;
/**
Detect and process potential IRC command in provided string.
@param string to analyse
@return YES if IRC style command has been detected and interpreted.
*/
- (BOOL)isIRCStyleCommand:(NSString*)string;
/**
Mention the member display name in the current text of the message composer.
The message composer becomes then the first responder.
*/
- (void)mention:(MXRoomMember*)roomMember;
/**
Force to dismiss keyboard if any
*/
- (void)dismissKeyboard;
/**
Tell whether the most recent message of the room history is visible.
*/
- (BOOL)isBubblesTableScrollViewAtTheBottom;
/**
Scroll the room history until the most recent message.
*/
- (void)scrollBubblesTableViewToBottomAnimated:(BOOL)animated;
/**
Dismiss the keyboard and all the potential subviews.
*/
- (void)dismissTemporarySubViews;
/**
Display a popup with the event detais.
@param event the event to inspect.
*/
- (void)showEventDetails:(MXEvent *)event;
/**
Present the attachments viewer by displaying the attachment of the provided cell.
@param cell the table view cell with attachment
*/
- (void)showAttachmentInCell:(UITableViewCell*)cell;
/**
Force a refresh of the room history display.
You should not call this method directly.
You may override it in inherited 'MXKRoomViewController' class.
@param useBottomAnchor tells whether the updated history must keep display the same event at the bottom.
@return a boolean value which tells whether the table has been scrolled to the bottom.
*/
- (BOOL)reloadBubblesTable:(BOOL)useBottomAnchor;
/**
Sets the offset from the content `bubblesTableView`'s origin. Take into account `preventBubblesTableViewScroll` value.
@param contentOffset Offset from the content `bubblesTableView`s origin.
@param animated YES to animate the transition.
*/
- (void)setBubbleTableViewContentOffset:(CGPoint)contentOffset animated:(BOOL)animated;
@end
File diff suppressed because it is too large Load Diff
@@ -1,64 +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" customClass="MXKRoomViewController">
<connections>
<outlet property="bubblesTableView" destination="BGD-sd-SQR" id="OG4-Tw-Ovt"/>
<outlet property="bubblesTableViewBottomConstraint" destination="Ksk-39-kfi" id="CTo-Ux-4NP"/>
<outlet property="bubblesTableViewTopConstraint" destination="X14-4s-uGM" id="Hic-6h-N05"/>
<outlet property="roomActivitiesContainer" destination="XX4-n6-hCm" id="uD0-ab-8s8"/>
<outlet property="roomActivitiesContainerHeightConstraint" destination="E8v-l2-8eV" id="ebD-oV-ttx"/>
<outlet property="roomInputToolbarContainer" destination="nLd-BP-JAE" id="1dp-P1-0js"/>
<outlet property="roomInputToolbarContainerBottomConstraint" destination="kQ6-Cg-FMi" id="nHr-fR-XnV"/>
<outlet property="roomInputToolbarContainerHeightConstraint" destination="5eD-Fm-RDb" id="6ny-5w-1UA"/>
<outlet property="view" destination="iN0-l3-epB" id="ieV-u7-rXU"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="interactive" style="plain" separatorStyle="none" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="BGD-sd-SQR">
<rect key="frame" x="0.0" y="0.0" width="375" height="626"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</tableView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XX4-n6-hCm" userLabel="Activities Container">
<rect key="frame" x="0.0" y="606" width="375" height="20"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="E8v-l2-8eV"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="nLd-BP-JAE" userLabel="Room Input Toolbar Container">
<rect key="frame" x="0.0" y="626" width="375" height="41"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="41" id="5eD-Fm-RDb"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="BGD-sd-SQR" secondAttribute="trailing" id="0la-ok-MBr"/>
<constraint firstItem="nLd-BP-JAE" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="4Q7-hr-rqi"/>
<constraint firstAttribute="bottom" secondItem="BGD-sd-SQR" secondAttribute="bottom" constant="41" id="Ksk-39-kfi"/>
<constraint firstItem="XX4-n6-hCm" firstAttribute="bottom" secondItem="nLd-BP-JAE" secondAttribute="top" id="QO8-nF-xys"/>
<constraint firstItem="XX4-n6-hCm" firstAttribute="width" secondItem="iN0-l3-epB" secondAttribute="width" id="WhE-lH-ZtR"/>
<constraint firstItem="BGD-sd-SQR" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="X14-4s-uGM"/>
<constraint firstAttribute="trailing" secondItem="nLd-BP-JAE" secondAttribute="trailing" id="YAu-gd-ItG"/>
<constraint firstItem="XX4-n6-hCm" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="csl-KT-4s9"/>
<constraint firstItem="BGD-sd-SQR" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="haP-Kv-OLI"/>
<constraint firstAttribute="bottom" secondItem="nLd-BP-JAE" secondAttribute="bottom" id="kQ6-Cg-FMi"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
</view>
</objects>
</document>
@@ -14,3 +14,4 @@
#import "MXKRoomInputToolbarView.h"
#import "MXKImageView.h"
#import "MXKRoomBubbleCellData.h"
@@ -17,6 +17,7 @@
*/
#import <MatrixSDK/MatrixSDK.h>
#import "MXKAccountData.h"
@class MXKAccount;
@@ -56,29 +57,7 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer
`MXKAccount` object contains the credentials of a logged matrix user. It is used to handle matrix
session and presence for this user.
*/
@interface MXKAccount : NSObject <NSCoding>
/**
The account's credentials: homeserver, access token, user id.
*/
@property (nonatomic, readonly) MXCredentials *mxCredentials;
/**
The identity server URL.
*/
@property (nonatomic) NSString *identityServerURL;
/**
The antivirus server URL, if any (nil by default).
Set a non-null url to configure the antivirus scanner use.
*/
@property (nonatomic) NSString *antivirusServerURL;
/**
The Push Gateway URL used to send event notifications to (nil by default).
This URL should be over HTTPS and never over HTTP.
*/
@property (nonatomic) NSString *pushGatewayURL;
@interface MXKAccount : MXKAccountData
/**
The matrix REST client used to make matrix API requests.
@@ -107,12 +86,6 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer
*/
@property (nonatomic, readonly) NSString *fullDisplayName;
/**
The 3PIDs linked to this account.
[self load3PIDs] must be called to update the property.
*/
@property (nonatomic, readonly) NSArray<MXThirdPartyIdentifier *> *threePIDs;
/**
The email addresses linked to this account.
This is a subset of self.threePIDs.
@@ -125,12 +98,6 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer
*/
@property (nonatomic, readonly) NSArray<NSString *> *linkedPhoneNumbers;
/**
The account user's device.
[self loadDeviceInformation] must be called to update the property.
*/
@property (nonatomic, readonly) MXDevice *device;
/**
The account user's presence (`MXPresenceUnknown` by default, available if matrix session `mxSession` is opened).
The notification `kMXKAccountUserInfoDidChangeNotification` is posted in case of change of this property.
@@ -148,11 +115,6 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer
*/
@property (nonatomic, readonly) BOOL pushNotificationServiceIsActive;
/**
Transient information storage.
*/
@property (nonatomic, strong, readonly) NSMutableDictionary<NSString *, id<NSCoding>> *others;
/**
Enable Push notification based on Apple Push Notification Service (APNS).
@@ -166,11 +128,6 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer
success:(void (^)(void))success
failure:(void (^)(NSError *))failure;
/**
Flag to indicate that an APNS pusher has been set on the homeserver for this device.
*/
@property (nonatomic, readonly) BOOL hasPusherForPushNotifications;
/**
The Push notification activity (based on PushKit) for this account.
YES when Push is turned on (locally available and enabled homeserver side).
@@ -190,26 +147,6 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer
success:(void (^)(void))success
failure:(void (^)(NSError *))failure;
/**
Flag to indicate that a PushKit pusher has been set on the homeserver for this device.
*/
@property (nonatomic, readonly) BOOL hasPusherForPushKitNotifications;
/**
Enable In-App notifications based on Remote notifications rules.
NO by default.
*/
@property (nonatomic) BOOL enableInAppNotifications;
/**
Disable the account without logging out (NO by default).
A matrix session is automatically opened for the account when this property is toggled from YES to NO.
The session is closed when this property is set to YES.
*/
@property (nonatomic,getter=isDisabled) BOOL disabled;
/**
Manage the online presence event.
@@ -217,11 +154,6 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer
*/
@property (nonatomic) BOOL hideUserPresence;
/**
Flag indicating if the end user has been warned about encryption and its limitations.
*/
@property (nonatomic,getter=isWarnedAboutEncryption) BOOL warnedAboutEncryption;
/**
Register the MXKAccountOnCertificateChange block that will be used to handle certificate change during account use.
This block is nil by default, any new certificate is ignored/untrusted (this will abort the connection to the server).
@@ -284,11 +216,6 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer
#pragma mark - Soft logout
/**
Flag to indicate if the account has been logged out by the homeserver admin.
*/
@property (nonatomic, readonly) BOOL isSoftLogout;
/**
Soft logout the account.
@@ -432,4 +359,8 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer
success:(void (^)(void))success
failure:(void (^)(NSError *error))failure;
/**
Handle unauthenticated errors from the server triggering hard/soft logouts as appropriate.
*/
- (void)handleUnauthenticatedWithError:(MXError *)error isSoftLogout:(BOOL)isSoftLogout isRefreshTokenAuth:(BOOL)isRefreshTokenAuth andCompletion:(void (^)(void))completion;
@end
@@ -36,6 +36,8 @@
#import "MXKSwiftHeader.h"
#import "GeneratedInterface-Swift.h"
NSString *const kMXKAccountUserInfoDidChangeNotification = @"kMXKAccountUserInfoDidChangeNotification";
NSString *const kMXKAccountAPNSActivityDidChangeNotification = @"kMXKAccountAPNSActivityDidChangeNotification";
NSString *const kMXKAccountPushKitActivityDidChangeNotification = @"kMXKAccountPushKitActivityDidChangeNotification";
@@ -92,13 +94,10 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
@property (nonatomic, strong) id<MXBackgroundTask> backgroundTask;
@property (nonatomic, strong) id<MXBackgroundTask> backgroundSyncBgTask;
@property (nonatomic, strong) NSMutableDictionary<NSString *, id<NSCoding>> *others;
@end
@implementation MXKAccount
@synthesize mxCredentials, mxSession, mxRestClient;
@synthesize threePIDs;
@synthesize mxSession, mxRestClient;
@synthesize userPresence;
@synthesize userTintColor;
@synthesize hideUserPresence;
@@ -144,7 +143,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
notifyOpenSessionFailure = YES;
// Report credentials and alloc REST client.
mxCredentials = credentials;
_mxCredentials = credentials;
[self prepareRESTClient];
userPresence = MXPresenceUnknown;
@@ -171,65 +170,19 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
- (id)initWithCoder:(NSCoder *)coder
{
self = [super init];
self = [super initWithCoder:coder];
if (self)
{
notifyOpenSessionFailure = YES;
NSString *homeServerURL = [coder decodeObjectForKey:@"homeserverurl"];
NSString *userId = [coder decodeObjectForKey:@"userid"];
NSString *accessToken = [coder decodeObjectForKey:@"accesstoken"];
_identityServerURL = [coder decodeObjectForKey:@"identityserverurl"];
NSString *identityServerAccessToken = [coder decodeObjectForKey:@"identityserveraccesstoken"];
mxCredentials = [[MXCredentials alloc] initWithHomeServer:homeServerURL
userId:userId
accessToken:accessToken];
mxCredentials.identityServer = _identityServerURL;
mxCredentials.identityServerAccessToken = identityServerAccessToken;
mxCredentials.deviceId = [coder decodeObjectForKey:@"deviceId"];
mxCredentials.allowedCertificate = [coder decodeObjectForKey:@"allowedCertificate"];
[self prepareRESTClient];
[self registerAccountDataDidChangeIdentityServerNotification];
[self registerIdentityServiceDidChangeAccessTokenNotification];
if ([coder decodeObjectForKey:@"threePIDs"])
{
threePIDs = [coder decodeObjectForKey:@"threePIDs"];
}
if ([coder decodeObjectForKey:@"device"])
{
_device = [coder decodeObjectForKey:@"device"];
}
userPresence = MXPresenceUnknown;
if ([coder decodeObjectForKey:@"antivirusserverurl"])
{
_antivirusServerURL = [coder decodeObjectForKey:@"antivirusserverurl"];
}
if ([coder decodeObjectForKey:@"pushgatewayurl"])
{
_pushGatewayURL = [coder decodeObjectForKey:@"pushgatewayurl"];
}
_hasPusherForPushNotifications = [coder decodeBoolForKey:@"_enablePushNotifications"];
_hasPusherForPushKitNotifications = [coder decodeBoolForKey:@"enablePushKitNotifications"];
_enableInAppNotifications = [coder decodeBoolForKey:@"enableInAppNotifications"];
_disabled = [coder decodeBoolForKey:@"disabled"];
_isSoftLogout = [coder decodeBoolForKey:@"isSoftLogout"];
_warnedAboutEncryption = [coder decodeBoolForKey:@"warnedAboutEncryption"];
_others = [coder decodeObjectForKey:@"others"];
// Refresh device information
[self loadDeviceInformation:nil failure:nil];
}
@@ -237,60 +190,6 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:mxCredentials.homeServer forKey:@"homeserverurl"];
[coder encodeObject:mxCredentials.userId forKey:@"userid"];
[coder encodeObject:mxCredentials.accessToken forKey:@"accesstoken"];
[coder encodeObject:mxCredentials.identityServerAccessToken forKey:@"identityserveraccesstoken"];
if (mxCredentials.deviceId)
{
[coder encodeObject:mxCredentials.deviceId forKey:@"deviceId"];
}
if (mxCredentials.allowedCertificate)
{
[coder encodeObject:mxCredentials.allowedCertificate forKey:@"allowedCertificate"];
}
if (self.threePIDs)
{
[coder encodeObject:threePIDs forKey:@"threePIDs"];
}
if (self.device)
{
[coder encodeObject:_device forKey:@"device"];
}
if (self.identityServerURL)
{
[coder encodeObject:_identityServerURL forKey:@"identityserverurl"];
}
if (self.antivirusServerURL)
{
[coder encodeObject:_antivirusServerURL forKey:@"antivirusserverurl"];
}
if (self.pushGatewayURL)
{
[coder encodeObject:_pushGatewayURL forKey:@"pushgatewayurl"];
}
[coder encodeBool:_hasPusherForPushNotifications forKey:@"_enablePushNotifications"];
[coder encodeBool:_hasPusherForPushKitNotifications forKey:@"enablePushKitNotifications"];
[coder encodeBool:_enableInAppNotifications forKey:@"enableInAppNotifications"];
[coder encodeBool:_disabled forKey:@"disabled"];
[coder encodeBool:_isSoftLogout forKey:@"isSoftLogout"];
[coder encodeBool:_warnedAboutEncryption forKey:@"warnedAboutEncryption"];
[coder encodeObject:_others forKey:@"others"];
}
#pragma mark - Properties
- (void)setIdentityServerURL:(NSString *)identityServerURL
@@ -298,10 +197,10 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
if (identityServerURL.length)
{
_identityServerURL = identityServerURL;
mxCredentials.identityServer = identityServerURL;
self.mxCredentials.identityServer = identityServerURL;
// Update services used in MXSession
[mxSession setIdentityServer:mxCredentials.identityServer andAccessToken:mxCredentials.identityServerAccessToken];
[mxSession setIdentityServer:self.mxCredentials.identityServer andAccessToken:self.mxCredentials.identityServerAccessToken];
}
else
{
@@ -355,24 +254,19 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
{
if (self.userDisplayName.length)
{
return [NSString stringWithFormat:@"%@ (%@)", self.userDisplayName, mxCredentials.userId];
return [NSString stringWithFormat:@"%@ (%@)", self.userDisplayName, self.mxCredentials.userId];
}
else
{
return mxCredentials.userId;
return self.mxCredentials.userId;
}
}
- (NSArray<MXThirdPartyIdentifier *> *)threePIDs
{
return threePIDs;
}
- (NSArray<NSString *> *)linkedEmails
{
NSMutableArray<NSString *> *linkedEmails = [NSMutableArray array];
for (MXThirdPartyIdentifier *threePID in threePIDs)
for (MXThirdPartyIdentifier *threePID in self.threePIDs)
{
if ([threePID.medium isEqualToString:kMX3PIDMediumEmail])
{
@@ -387,7 +281,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
{
NSMutableArray<NSString *> *linkedPhoneNumbers = [NSMutableArray array];
for (MXThirdPartyIdentifier *threePID in threePIDs)
for (MXThirdPartyIdentifier *threePID in self.threePIDs)
{
if ([threePID.medium isEqualToString:kMX3PIDMediumMSISDN])
{
@@ -402,7 +296,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
{
if (!userTintColor)
{
userTintColor = [MXKTools colorWithRGBValue:[mxCredentials.userId hash]];
userTintColor = [MXKTools colorWithRGBValue:[self.mxCredentials.userId hash]];
}
return userTintColor;
@@ -410,7 +304,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
- (BOOL)pushNotificationServiceIsActive
{
BOOL pushNotificationServiceIsActive = ([[MXKAccountManager sharedManager] isAPNSAvailable] && _hasPusherForPushNotifications && mxSession);
BOOL pushNotificationServiceIsActive = ([[MXKAccountManager sharedManager] isAPNSAvailable] && self.hasPusherForPushNotifications && mxSession);
MXLogDebug(@"[MXKAccount][Push] pushNotificationServiceIsActive: %@", @(pushNotificationServiceIsActive));
return pushNotificationServiceIsActive;
@@ -461,7 +355,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
}
}
}
else if (_hasPusherForPushNotifications)
else if (self.hasPusherForPushNotifications)
{
MXLogDebug(@"[MXKAccount][Push] enablePushNotifications: Disable APNS for %@ account", self.mxCredentials.userId);
@@ -487,7 +381,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
- (BOOL)isPushKitNotificationActive
{
BOOL isPushKitNotificationActive = ([[MXKAccountManager sharedManager] isPushAvailable] && _hasPusherForPushKitNotifications && mxSession);
BOOL isPushKitNotificationActive = ([[MXKAccountManager sharedManager] isPushAvailable] && self.hasPusherForPushKitNotifications && mxSession);
MXLogDebug(@"[MXKAccount][Push] isPushKitNotificationActive: %@", @(isPushKitNotificationActive));
return isPushKitNotificationActive;
@@ -535,7 +429,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
failure (error);
}
}
else if (_hasPusherForPushKitNotifications)
else if (self.hasPusherForPushKitNotifications)
{
MXLogDebug(@"[MXKAccount][Push] enablePushKitNotifications: Disable Push for %@ account", self.mxCredentials.userId);
@@ -633,7 +527,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
success();
}
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self->mxCredentials.userId];
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self.mxCredentials.userId];
}
failure:failure];
}
@@ -653,7 +547,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
success();
}
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self->mxCredentials.userId];
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self.mxCredentials.userId];
}
failure:failure];
}
@@ -686,9 +580,9 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
- (void)load3PIDs:(void (^)(void))success failure:(void (^)(NSError *))failure
{
[mxRestClient threePIDs:^(NSArray<MXThirdPartyIdentifier *> *threePIDs2) {
self->threePIDs = threePIDs2;
self->_threePIDs = threePIDs2;
// Archive updated field
[[MXKAccountManager sharedManager] saveAccounts];
@@ -708,9 +602,9 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
- (void)loadDeviceInformation:(void (^)(void))success failure:(void (^)(NSError *error))failure
{
if (mxCredentials.deviceId)
if (self.mxCredentials.deviceId)
{
[mxRestClient deviceByDeviceId:mxCredentials.deviceId success:^(MXDevice *device) {
[mxRestClient deviceByDeviceId:self.mxCredentials.deviceId success:^(MXDevice *device) {
self->_device = device;
@@ -751,21 +645,21 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
[mxSession.myUser setPresence:userPresence
andStatusMessage:statusMessage
success:^{
MXLogDebug(@"[MXKAccount] %@: set user presence (%lu) succeeded", self->mxCredentials.userId, (unsigned long)self->userPresence);
MXLogDebug(@"[MXKAccount] %@: set user presence (%lu) succeeded", self.mxCredentials.userId, (unsigned long)self->userPresence);
if (completion)
{
completion();
}
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self->mxCredentials.userId];
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self.mxCredentials.userId];
}
failure:^(NSError *error) {
MXLogDebug(@"[MXKAccount] %@: set user presence (%lu) failed", self->mxCredentials.userId, (unsigned long)self->userPresence);
MXLogDebug(@"[MXKAccount] %@: set user presence (%lu) failed", self.mxCredentials.userId, (unsigned long)self->userPresence);
}];
}
else if (hideUserPresence)
{
MXLogDebug(@"[MXKAccount] %@: set user presence is disabled.", mxCredentials.userId);
MXLogDebug(@"[MXKAccount] %@: set user presence is disabled.", self.mxCredentials.userId);
}
}
@@ -783,7 +677,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
-(void)openSessionWithStore:(id<MXStore>)store
{
// Sanity check
if (!mxCredentials || !mxRestClient)
if (!self.mxCredentials || !mxRestClient)
{
MXLogDebug(@"[MXKAccount] Matrix session cannot be created without credentials");
return;
@@ -1048,9 +942,9 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
- (void)hydrateWithCredentials:(MXCredentials*)credentials
{
// Sanity check
if ([mxCredentials.userId isEqualToString:credentials.userId])
if ([self.mxCredentials.userId isEqualToString:credentials.userId])
{
mxCredentials = credentials;
_mxCredentials = credentials;
_isSoftLogout = NO;
[[MXKAccountManager sharedManager] saveAccounts];
@@ -1058,11 +952,10 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
}
else
{
MXLogDebug(@"[MXKAccount] hydrateWithCredentials: Error: users ids mismatch: %@ vs %@", credentials.userId, mxCredentials.userId);
MXLogDebug(@"[MXKAccount] hydrateWithCredentials: Error: users ids mismatch: %@ vs %@", credentials.userId, self.mxCredentials.userId);
}
}
- (void)deletePusher
{
if (self.pushNotificationServiceIsActive)
@@ -1259,7 +1152,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
success();
}
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountAPNSActivityDidChangeNotification object:self->mxCredentials.userId];
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountAPNSActivityDidChangeNotification object:self.mxCredentials.userId];
} failure:^(NSError *error) {
@@ -1278,7 +1171,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
success();
}
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountAPNSActivityDidChangeNotification object:self->mxCredentials.userId];
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountAPNSActivityDidChangeNotification object:self.mxCredentials.userId];
return;
}
@@ -1295,7 +1188,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
failure(error);
}
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountAPNSActivityDidChangeNotification object:self->mxCredentials.userId];
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountAPNSActivityDidChangeNotification object:self.mxCredentials.userId];
}];
}
@@ -1322,7 +1215,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
MXLogDebug(@"[MXKAccount][Push] refreshPushKitPusher: Error: %@", error);
}];
}
else if (_hasPusherForPushKitNotifications)
else if (self.hasPusherForPushKitNotifications)
{
if ([MXKAccountManager sharedManager].pushDeviceToken)
{
@@ -1336,7 +1229,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
else
{
MXLogDebug(@"[MXKAccount][Push] refreshPushKitPusher: PushKit pusher for %@ account is already disabled. Reset _hasPusherForPushKitNotifications", self.mxCredentials.userId);
_hasPusherForPushKitNotifications = NO;
self->_hasPusherForPushKitNotifications = NO;
[[MXKAccountManager sharedManager] saveAccounts];
}
}
@@ -1398,7 +1291,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
success();
}
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountPushKitActivityDidChangeNotification object:self->mxCredentials.userId];
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountPushKitActivityDidChangeNotification object:self.mxCredentials.userId];
} failure:^(NSError *error) {
@@ -1417,7 +1310,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
success();
}
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountPushKitActivityDidChangeNotification object:self->mxCredentials.userId];
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountPushKitActivityDidChangeNotification object:self.mxCredentials.userId];
return;
}
@@ -1434,7 +1327,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
failure(error);
}
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountPushKitActivityDidChangeNotification object:self->mxCredentials.userId];
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountPushKitActivityDidChangeNotification object:self.mxCredentials.userId];
}];
}
@@ -1443,7 +1336,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
MXLogDebug(@"[MXKAccount][Push] enablePusher: %@", @(enabled));
// Refuse to try & turn push on if we're not logged in, it's nonsensical.
if (!mxCredentials)
if (!self.mxCredentials)
{
MXLogDebug(@"[MXKAccount][Push] enablePusher: Not setting push token because we're not logged in");
return;
@@ -1610,7 +1503,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
[self.mxSession startWithSyncFilter:syncFilter onServerSyncDone:^{
MXStrongifyAndReturnIfNil(self);
MXLogDebug(@"[MXKAccount] %@: The session is ready. Matrix SDK session has been started in %0.fms.", self->mxCredentials.userId, [[NSDate date] timeIntervalSinceDate:self->openSessionStartDate] * 1000);
MXLogDebug(@"[MXKAccount] %@: The session is ready. Matrix SDK session has been started in %0.fms.", self.mxCredentials.userId, [[NSDate date] timeIntervalSinceDate:self->openSessionStartDate] * 1000);
[self setUserPresence:MXPresenceOnline andStatusMessage:nil completion:nil];
@@ -1745,11 +1638,11 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
}
// Here displayname or other information have been updated, post update notification.
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self->mxCredentials.userId];
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self.mxCredentials.userId];
}];
// User information are just up-to-date (`mxSession` is running), post update notification.
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:mxCredentials.userId];
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self.mxCredentials.userId];
}
}
else if (mxSession.state == MXSessionStateStoreDataReady || mxSession.state == MXSessionStateSyncInProgress)
@@ -1764,40 +1657,30 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
{
// Here the initial server sync is in progress. The session is not running yet, but some user's information are available (from local storage).
// We post update notification to let observer take into account this user's information even if they may not be up-to-date.
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:mxCredentials.userId];
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self.mxCredentials.userId];
}
}
else if (mxSession.state == MXSessionStatePaused)
{
isPauseRequested = NO;
}
else if (mxSession.state == MXSessionStateUnknownToken)
{
// Logout this account
[[MXKAccountManager sharedManager] removeAccount:self completion:nil];
}
else if (mxSession.state == MXSessionStateSoftLogout)
{
// Soft logout this account
[[MXKAccountManager sharedManager] softLogout:self];
}
}
- (void)prepareRESTClient
{
if (!mxCredentials)
if (!self.mxCredentials)
{
return;
}
mxRestClient = [[MXRestClient alloc] initWithCredentials:mxCredentials andOnUnrecognizedCertificateBlock:^BOOL(NSData *certificate) {
MXWeakify(self);
mxRestClient = [[MXRestClient alloc] initWithCredentials:self.mxCredentials andOnUnrecognizedCertificateBlock:^BOOL(NSData *certificate) {
MXStrongifyAndReturnValueIfNil(self, NO);
if (_onCertificateChangeBlock)
{
if (_onCertificateChangeBlock (self, certificate))
{
// Update the certificate in credentials
self->mxCredentials.allowedCertificate = certificate;
self.mxCredentials.allowedCertificate = certificate;
// Archive updated field
[[MXKAccountManager sharedManager] saveAccounts];
@@ -1805,16 +1688,39 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
return YES;
}
self->mxCredentials.ignoredCertificate = certificate;
self.mxCredentials.ignoredCertificate = certificate;
// Archive updated field
[[MXKAccountManager sharedManager] saveAccounts];
}
return NO;
} andPersistentTokenDataHandler:^(void (^handler)(NSArray<MXCredentials *> *credentials, void (^completion)(BOOL didUpdateCredentials))) {
[MXKAccountManager.sharedManager readAndWriteCredentials:handler];
} andUnauthenticatedHandler:^(MXError *error, BOOL isSoftLogout, BOOL isRefreshTokenAuth, void (^completion)(void)) {
MXStrongifyAndReturnIfNil(self);
[self handleUnauthenticatedWithError:error isSoftLogout:isSoftLogout isRefreshTokenAuth:isRefreshTokenAuth andCompletion:completion];
}];
}
- (void)handleUnauthenticatedWithError:(MXError *)error isSoftLogout:(BOOL)isSoftLogout isRefreshTokenAuth:(BOOL)isRefreshTokenAuth andCompletion:(void (^)(void))completion
{
[Analytics.shared trackAuthUnauthenticatedErrorWithSoftLogout:isSoftLogout refreshTokenAuth:isRefreshTokenAuth errorCode:error.errcode errorReason:error.error];
MXLogDebug(@"[MXKAccountManager] handleUnauthenticated: trackAuthUnauthenticatedErrorWithSoftLogout sent");
if (isSoftLogout)
{
MXLogDebug(@"[MXKAccountManager] handleUnauthenticated: soft logout.");
[[MXKAccountManager sharedManager] softLogout:self];
completion();
}
else
{
MXLogDebug(@"[MXKAccountManager] handleUnauthenticated: hard logout.");
[[MXKAccountManager sharedManager] removeAccount:self sendLogoutRequest:NO completion:completion];
}
}
- (void)onDateTimeFormatUpdate
{
if ([mxSession.roomSummaryUpdateDelegate isKindOfClass:MXKEventFormatter.class])
@@ -1871,7 +1777,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
#pragma mark - Crypto
- (void)resetDeviceId
{
mxCredentials.deviceId = nil;
self.mxCredentials.deviceId = nil;
// Archive updated field
[[MXKAccountManager sharedManager] saveAccounts];
@@ -2187,11 +2093,11 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
MXSession *mxSession = notification.object;
if (mxSession == self.mxSession)
{
if (![mxCredentials.identityServer isEqualToString:self.mxSession.accountDataIdentityServer])
if (![self.mxCredentials.identityServer isEqualToString:self.mxSession.accountDataIdentityServer])
{
_identityServerURL = self.mxSession.accountDataIdentityServer;
mxCredentials.identityServer = _identityServerURL;
mxCredentials.identityServerAccessToken = nil;
self.mxCredentials.identityServer = _identityServerURL;
self.mxCredentials.identityServerAccessToken = nil;
// Archive updated field
[[MXKAccountManager sharedManager] saveAccounts];
@@ -2204,7 +2110,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
- (void)identityService:(MXIdentityService *)identityService didUpdateAccessToken:(NSString *)accessToken
{
mxCredentials.identityServerAccessToken = accessToken;
self.mxCredentials.identityServerAccessToken = accessToken;
}
- (void)registerIdentityServiceDidChangeAccessTokenNotification
@@ -2220,9 +2126,9 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
NSString *identityServer = userInfo[MXIdentityServiceNotificationIdentityServerKey];
NSString *accessToken = userInfo[MXIdentityServiceNotificationAccessTokenKey];
if (userId && identityServer && accessToken && [mxCredentials.identityServer isEqualToString:identityServer])
if (userId && identityServer && accessToken && [self.mxCredentials.identityServer isEqualToString:identityServer])
{
mxCredentials.identityServerAccessToken = accessToken;
self.mxCredentials.identityServerAccessToken = accessToken;
// Archive updated field
[[MXKAccountManager sharedManager] saveAccounts];
@@ -0,0 +1,118 @@
//
// Copyright 2021 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>
@class MXKAccountData;
@interface MXKAccountData : NSObject <NSCoding> {
@protected MXCredentials *_mxCredentials;
@protected NSString *_identityServerURL;
@protected NSString *_antivirusServerURL;
@protected NSString *_pushGatewayURL;
@protected MXDevice *_device;
@protected BOOL _disabled;
@protected BOOL _enableInAppNotifications;
@protected BOOL _warnedAboutEncryption;
@protected NSMutableDictionary<NSString *, id<NSCoding>> *_others;
@protected NSArray<MXThirdPartyIdentifier *> *_threePIDs;
@protected BOOL _isSoftLogout;
@protected BOOL _hasPusherForPushNotifications;
@protected BOOL _hasPusherForPushKitNotifications;
}
/**
The account's credentials: homeserver, access token, user id.
*/
@property (nonatomic, readonly, nonnull) MXCredentials *mxCredentials;
/**
The identity server URL.
*/
@property (nonatomic, nonnull) NSString *identityServerURL;
/**
The antivirus server URL, if any (nil by default).
Set a non-null url to configure the antivirus scanner use.
*/
@property (nonatomic, nullable) NSString *antivirusServerURL;
/**
The Push Gateway URL used to send event notifications to (nil by default).
This URL should be over HTTPS and never over HTTP.
*/
@property (nonatomic, nullable) NSString *pushGatewayURL;
/**
The 3PIDs linked to this account.
[self load3PIDs] must be called to update the property.
*/
@property (nonatomic, readonly, nullable) NSArray<MXThirdPartyIdentifier *> *threePIDs;
/**
The account user's device.
[self loadDeviceInformation] must be called to update the property.
*/
@property (nonatomic, readonly, nullable) MXDevice *device;
/**
Transient information storage.
*/
@property (nonatomic, strong, readonly, nonnull) NSMutableDictionary<NSString *, id<NSCoding>> *others;
/**
Flag to indicate that an APNS pusher has been set on the homeserver for this device.
*/
@property (nonatomic, readonly) BOOL hasPusherForPushNotifications;
/**
The Push notification activity (based on PushKit) for this account.
YES when Push is turned on (locally available and enabled homeserver side).
*/
@property (nonatomic, readonly) BOOL isPushKitNotificationActive;
/**
Flag to indicate that a PushKit pusher has been set on the homeserver for this device.
*/
@property (nonatomic, readonly) BOOL hasPusherForPushKitNotifications;
/**
Enable In-App notifications based on Remote notifications rules.
NO by default.
*/
@property (nonatomic) BOOL enableInAppNotifications;
/**
Disable the account without logging out (NO by default).
A matrix session is automatically opened for the account when this property is toggled from YES to NO.
The session is closed when this property is set to YES.
*/
@property (nonatomic,getter=isDisabled) BOOL disabled;
/**
Flag indicating if the end user has been warned about encryption and its limitations.
*/
@property (nonatomic,getter=isWarnedAboutEncryption) BOOL warnedAboutEncryption;
#pragma mark - Soft logout
/**
Flag to indicate if the account has been logged out by the homeserver admin.
*/
@property (nonatomic, readonly) BOOL isSoftLogout;
@end
@@ -0,0 +1,150 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import "MXKAccountData.h"
@interface MXKAccountData ()
@end
@implementation MXKAccountData
@synthesize mxCredentials = _mxCredentials;
#pragma mark - NSCoding
- (id)initWithCoder:(NSCoder *)coder
{
self = [super init];
if (self)
{
NSString *homeServerURL = [coder decodeObjectForKey:@"homeserverurl"];
NSString *userId = [coder decodeObjectForKey:@"userid"];
NSString *accessToken = [coder decodeObjectForKey:@"accesstoken"];
_identityServerURL = [coder decodeObjectForKey:@"identityserverurl"];
NSString *identityServerAccessToken = [coder decodeObjectForKey:@"identityserveraccesstoken"];
_mxCredentials = [[MXCredentials alloc] initWithHomeServer:homeServerURL
userId:userId
accessToken:accessToken];
_mxCredentials.accessTokenExpiresAt = [coder decodeInt64ForKey:@"accessTokenExpiresAt"];
_mxCredentials.refreshToken = [coder decodeObjectForKey:@"refreshToken"];
_mxCredentials.identityServer = _identityServerURL;
_mxCredentials.identityServerAccessToken = identityServerAccessToken;
_mxCredentials.deviceId = [coder decodeObjectForKey:@"deviceId"];
_mxCredentials.allowedCertificate = [coder decodeObjectForKey:@"allowedCertificate"];
if ([coder decodeObjectForKey:@"threePIDs"])
{
_threePIDs = [coder decodeObjectForKey:@"threePIDs"];
}
if ([coder decodeObjectForKey:@"device"])
{
_device = [coder decodeObjectForKey:@"device"];
}
if ([coder decodeObjectForKey:@"antivirusserverurl"])
{
_antivirusServerURL = [coder decodeObjectForKey:@"antivirusserverurl"];
}
if ([coder decodeObjectForKey:@"pushgatewayurl"])
{
_pushGatewayURL = [coder decodeObjectForKey:@"pushgatewayurl"];
}
_hasPusherForPushNotifications = [coder decodeBoolForKey:@"_enablePushNotifications"];
_hasPusherForPushKitNotifications = [coder decodeBoolForKey:@"enablePushKitNotifications"];
_enableInAppNotifications = [coder decodeBoolForKey:@"enableInAppNotifications"];
_disabled = [coder decodeBoolForKey:@"disabled"];
_isSoftLogout = [coder decodeBoolForKey:@"isSoftLogout"];
_warnedAboutEncryption = [coder decodeBoolForKey:@"warnedAboutEncryption"];
_others = [coder decodeObjectForKey:@"others"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:_mxCredentials.homeServer forKey:@"homeserverurl"];
[coder encodeObject:_mxCredentials.userId forKey:@"userid"];
[coder encodeObject:_mxCredentials.accessToken forKey:@"accesstoken"];
if (self.mxCredentials.accessTokenExpiresAt) {
[coder encodeInt64:_mxCredentials.accessTokenExpiresAt forKey:@"accessTokenExpiresAt"];
}
if (self.mxCredentials.refreshToken) {
[coder encodeObject:_mxCredentials.refreshToken forKey:@"refreshToken"];
}
[coder encodeObject:_mxCredentials.identityServerAccessToken forKey:@"identityserveraccesstoken"];
if (self.mxCredentials.deviceId)
{
[coder encodeObject:_mxCredentials.deviceId forKey:@"deviceId"];
}
if (self.mxCredentials.allowedCertificate)
{
[coder encodeObject:_mxCredentials.allowedCertificate forKey:@"allowedCertificate"];
}
if (self.threePIDs)
{
[coder encodeObject:_threePIDs forKey:@"threePIDs"];
}
if (self.device)
{
[coder encodeObject:_device forKey:@"device"];
}
if (self.identityServerURL)
{
[coder encodeObject:_identityServerURL forKey:@"identityserverurl"];
}
if (self.antivirusServerURL)
{
[coder encodeObject:_antivirusServerURL forKey:@"antivirusserverurl"];
}
if (self.pushGatewayURL)
{
[coder encodeObject:_pushGatewayURL forKey:@"pushgatewayurl"];
}
[coder encodeBool:_hasPusherForPushNotifications forKey:@"_enablePushNotifications"];
[coder encodeBool:_hasPusherForPushKitNotifications forKey:@"enablePushKitNotifications"];
[coder encodeBool:_enableInAppNotifications forKey:@"enableInAppNotifications"];
[coder encodeBool:_disabled forKey:@"disabled"];
[coder encodeBool:_isSoftLogout forKey:@"isSoftLogout"];
[coder encodeBool:_warnedAboutEncryption forKey:@"warnedAboutEncryption"];
[coder encodeObject:_others forKey:@"others"];
}
@end
@@ -110,6 +110,8 @@ extern NSString *const MXKAccountManagerDataType;
*/
+ (MXKAccountManager *)sharedManager;
+ (MXKAccountManager *)sharedManagerWithReload:(BOOL)reload;
/**
Check for each enabled account if a matrix session is already opened.
Open a matrix session for each enabled account which doesn't have a session.
@@ -208,11 +210,6 @@ extern NSString *const MXKAccountManagerDataType;
*/
- (MXKAccount *)accountKnowingUserWithUserId:(NSString *)userId;
/**
Force the account manager to reload existing accounts from the local storage.
The account manager is supposed to handle itself the list of the accounts.
Call this method only when an account has been changed from an other application from the same group.
*/
- (void)forceReloadAccounts;
- (void)readAndWriteCredentials:(void (^)(NSArray<MXCredentials*> * _Nullable readData, void (^completion)(BOOL didUpdateCredentials)))readAnWriteHandler;
@end
@@ -21,6 +21,8 @@
#import "MXKAppSettings.h"
#import "MXKTools.h"
#import "MXKAccountData.h"
#import "MXRefreshTokenData.h"
static NSString *const kMXKAccountsKeyOld = @"accounts";
static NSString *const kMXKAccountsKey = @"accountsV2";
@@ -43,13 +45,23 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa
@implementation MXKAccountManager
+ (MXKAccountManager *)sharedManager
{
return [MXKAccountManager sharedManagerWithReload:NO];
}
+ (MXKAccountManager *)sharedManagerWithReload:(BOOL)reload
{
static MXKAccountManager *sharedAccountManager = nil;
static dispatch_once_t onceToken;
__block BOOL didLoad = false;
dispatch_once(&onceToken, ^{
didLoad = true;
sharedAccountManager = [[super allocWithZone:NULL] init];
});
if (reload && !didLoad) {
[sharedAccountManager loadAccounts];
}
return sharedAccountManager;
}
@@ -599,7 +611,6 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa
- (void)loadAccounts
{
MXLogDebug(@"[MXKAccountManager] loadAccounts");
NSString *accountFile = [self accountFile];
if ([[NSFileManager defaultManager] fileExistsAtPath:accountFile])
{
@@ -665,12 +676,6 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa
}
}
- (void)forceReloadAccounts
{
MXLogDebug(@"[MXKAccountManager] Force reload existing accounts from local storage");
[self loadAccounts];
}
- (NSData*)encryptData:(NSData*)data
{
// Exceptions are not caught as the key is always needed if the KeyProviderDelegate
@@ -723,4 +728,69 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa
}
}
- (void)readAndWriteCredentials:(void (^)(NSArray<MXCredentials*> * _Nullable readData, void (^completion)(BOOL didUpdateCredentials)))readAnWriteHandler
{
NSError *error;
NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] init];
__block BOOL coordinatorSuccess = NO;
MXLogDebug(@"[MXKAccountManager] readAndWriteCredentials: purposeIdentifier = %@", fileCoordinator.purposeIdentifier);
NSDate *coordinateStartTime = [NSDate date];
[fileCoordinator coordinateReadingItemAtURL:[self accountFileUrl]
options:0
writingItemAtURL:[self accountFileUrl]
options:NSFileCoordinatorWritingForMerging
error:&error
byAccessor:^(NSURL * _Nonnull newReadingURL, NSURL * _Nonnull newWritingURL) {
NSDate *accessorStartTime = [NSDate date];
NSTimeInterval acquireInterval = [accessorStartTime timeIntervalSinceDate:coordinateStartTime];
MXLogDebug(@"[MXKAccountManager] readAndWriteCredentials: acquireInterval = %f", acquireInterval);
NSError *error = nil;
NSData* data = [NSData dataWithContentsOfURL:newReadingURL options:(NSDataReadingMappedAlways | NSDataReadingUncached) error:&error];
// Decrypt data if encryption method is provided
NSData *unciphered = [self decryptData:data];
NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingFromData:unciphered error:&error];
decoder.requiresSecureCoding = false;
[decoder setClass:[MXKAccountData class] forClassName:@"MXKAccount"];
NSMutableArray<MXKAccountData*>* mxAccountsData = [decoder decodeObjectForKey:@"mxAccounts"];
NSMutableArray<MXCredentials*>* mxAccountCredentials = [NSMutableArray arrayWithCapacity:mxAccounts.count];
for(MXKAccountData *account in mxAccountsData){
[mxAccountCredentials addObject:account.mxCredentials];
}
dispatch_group_t dispatchGroup = dispatch_group_create();
dispatch_group_enter(dispatchGroup);
__block BOOL didUpdate = NO;
readAnWriteHandler(mxAccountCredentials, ^(BOOL didUpdateCredentials) {
didUpdate = didUpdateCredentials;
dispatch_group_leave(dispatchGroup);
});
dispatch_group_wait(dispatchGroup, DISPATCH_TIME_FOREVER);
if (didUpdate) {
MXLogDebug(@"[MXKAccountManager] readAndWriteCredentials: did update saving credential data");
NSKeyedArchiver *encoder = [[NSKeyedArchiver alloc] initRequiringSecureCoding: NO];
[encoder setClassName:@"MXKAccount" forClass:[MXKAccountData class]];
[encoder encodeObject:mxAccountsData forKey:@"mxAccounts"];
NSData *writeData = [self encryptData:[encoder encodedData]];
coordinatorSuccess = [writeData writeToURL:newWritingURL atomically:YES];
} else {
MXLogDebug(@"[MXKAccountManager] readAndWriteCredentials: did not update not saving credential data");
coordinatorSuccess = YES;
}
NSDate *accessorEndTime = [NSDate date];
NSTimeInterval lockedTime = [accessorEndTime timeIntervalSinceDate:accessorStartTime];
MXLogDebug(@"[MXKAccountManager] readAndWriteCredentials: lockedTime = %f", lockedTime);
}];
MXLogDebug(@"[MXKAccountManager] readAndWriteCredentials:exit %d", coordinatorSuccess);
}
- (NSURL *)accountFileUrl
{
return [NSURL fileURLWithPath: [self accountFile]];
}
@end
@@ -27,7 +27,7 @@ extern NSString * const kMXKAttachmentErrorDomain;
/**
List attachment types
*/
typedef enum : NSUInteger {
typedef NS_ENUM(NSUInteger, MXKAttachmentType) {
MXKAttachmentTypeUndefined,
MXKAttachmentTypeImage,
MXKAttachmentTypeAudio,
@@ -35,8 +35,7 @@ typedef enum : NSUInteger {
MXKAttachmentTypeVideo,
MXKAttachmentTypeFile,
MXKAttachmentTypeSticker
} MXKAttachmentType;
};
/**
`MXKAttachment` represents a room attachment.
@@ -50,7 +50,8 @@
The optional text pattern to be highlighted in the body of the message.
*/
NSString *highlightedPattern;
UIColor *highlightedPatternColor;
UIColor *highlightedPatternForegroundColor;
UIColor *highlightedPatternBackgroundColor;
UIFont *highlightedPatternFont;
}
@@ -80,6 +81,11 @@
*/
@property (nonatomic, readonly) BOOL hasLink;
/**
Whether the data has a thread root in its components.
*/
@property (nonatomic, readonly) BOOL hasThreadRoot;
/**
Event formatter
*/
@@ -163,4 +169,11 @@
*/
- (MXKRoomBubbleComponent*)getFirstBubbleComponentWithDisplay;
/**
Get the last visible component.
@return Last visible component or nil.
*/
- (MXKRoomBubbleComponent*)getLastBubbleComponentWithDisplay;
@end
@@ -306,16 +306,15 @@
return first;
}
- (MXKRoomBubbleComponent*) getFirstBubbleComponentWithDisplay
- (MXKRoomBubbleComponent*)getFirstBubbleComponentWithDisplay
{
// Look for the first component which is actually displayed (some event are ignored in room history display).
MXKRoomBubbleComponent* first = nil;
@synchronized(bubbleComponents)
{
for (NSInteger index = 0; index < bubbleComponents.count; index++)
for (MXKRoomBubbleComponent *component in bubbleComponents)
{
MXKRoomBubbleComponent *component = bubbleComponents[index];
if (component.attributedTextMessage)
{
first = component;
@@ -327,6 +326,26 @@
return first;
}
- (MXKRoomBubbleComponent*)getLastBubbleComponentWithDisplay
{
// Look for the first component which is actually displayed (some event are ignored in room history display).
MXKRoomBubbleComponent* lastVisibleComponent = nil;
@synchronized(bubbleComponents)
{
for (MXKRoomBubbleComponent *component in bubbleComponents.reverseObjectEnumerator)
{
if (component.attributedTextMessage)
{
lastVisibleComponent = component;
break;
}
}
}
return lastVisibleComponent;
}
- (NSAttributedString*)attributedTextMessageWithHighlightedEvent:(NSString*)eventId tintColor:(UIColor*)tintColor
{
NSAttributedString *customAttributedTextMsg;
@@ -351,10 +370,14 @@
return customAttributedTextMsg;
}
- (void)highlightPatternInTextMessage:(NSString*)pattern withForegroundColor:(UIColor*)patternColor andFont:(UIFont*)patternFont
- (void)highlightPatternInTextMessage:(NSString*)pattern
withBackgroundColor:(UIColor *)backgroundColor
foregroundColor:(UIColor*)foregroundColor
andFont:(UIFont*)patternFont
{
highlightedPattern = pattern;
highlightedPatternColor = patternColor;
highlightedPatternBackgroundColor = backgroundColor;
highlightedPatternForegroundColor = foregroundColor;
highlightedPatternFont = patternFont;
// Indicate that the text message layout should be recomputed.
@@ -607,6 +630,22 @@
return NO;
}
- (BOOL)hasThreadRoot
{
@synchronized (bubbleComponents)
{
for (MXKRoomBubbleComponent *component in bubbleComponents)
{
if (component.thread)
{
return YES;
}
}
}
return NO;
}
- (MXKRoomBubbleComponentDisplayFix)displayFix
{
MXKRoomBubbleComponentDisplayFix displayFix = MXKRoomBubbleComponentDisplayFixNone;
@@ -705,6 +744,27 @@
return NO;
}
- (BOOL)isAttachment
{
if (!self.attachment)
{
return NO;
}
if (!attachment.contentURL || !attachment.contentInfo) {
return NO;
}
switch (self.attachment.type) {
case MXKAttachmentTypeFile:
case MXKAttachmentTypeAudio:
case MXKAttachmentTypeVoiceMessage:
return YES;
default:
return NO;
}
}
- (void)setMaxTextViewWidth:(CGFloat)inMaxTextViewWidth
{
// Check change
@@ -873,10 +933,16 @@
while (range.location != NSNotFound)
{
if (highlightedPatternColor)
if (highlightedPatternBackgroundColor)
{
// Update background color
[customAttributedTextMsg addAttribute:NSBackgroundColorAttributeName value:highlightedPatternBackgroundColor range:range];
}
if (highlightedPatternForegroundColor)
{
// Update text color
[customAttributedTextMsg addAttribute:NSForegroundColorAttributeName value:highlightedPatternColor range:range];
[customAttributedTextMsg addAttribute:NSForegroundColorAttributeName value:highlightedPatternForegroundColor range:range];
}
if (highlightedPatternFont)
@@ -147,6 +147,11 @@
*/
@property (nonatomic) BOOL isAttachmentWithIcon;
/**
YES when the bubble correspond to an attachment (audio, file...).
*/
@property (nonatomic, readonly) BOOL isAttachment;
/**
Flag that indicates that self.attributedTextMessage will be not nil.
This avoids the computation of self.attributedTextMessage that can take time.
@@ -269,10 +274,14 @@ Update the event because its sent state changed or it is has been redacted.
Highlight all the occurrences of a pattern in the resulting message body 'attributedTextMessage'.
@param pattern the text pattern to highlight.
@param patternColor optional text color (the pattern text color is unchanged if nil).
@param backgroundColor optional text background color (the patterns background color is unchanged if nil)
@param foregroundColor optional text color (the pattern text color is unchanged if nil).
@param patternFont optional text font (the pattern font is unchanged if nil).
*/
- (void)highlightPatternInTextMessage:(NSString*)pattern withForegroundColor:(UIColor*)patternColor andFont:(UIFont*)patternFont;
- (void)highlightPatternInTextMessage:(NSString*)pattern
withBackgroundColor:(UIColor *)backgroundColor
foregroundColor:(UIColor*)foregroundColor
andFont:(UIFont*)patternFont;
/**
Refresh the sender flair information
@@ -19,6 +19,8 @@
#import "MXKEventFormatter.h"
#import "MXKURLPreviewDataProtocol.h"
@class MXThread;
/**
Flags to indicate if a fix is required at the display time.
*/
@@ -103,6 +105,11 @@ typedef enum : NSUInteger {
*/
@property (nonatomic, readonly) BOOL showEncryptionBadge;
/**
Thread for the bubble component. Should only exist for thread root events.
*/
@property (nonatomic, readonly) MXThread *thread;
/**
Create a new `MXKRoomBubbleComponent` object based on a `MXEvent` instance.
@@ -18,6 +18,13 @@
#import "MXEvent+MatrixKit.h"
#import "MXKSwiftHeader.h"
#import <MatrixSDK/MatrixSDK.h>
@interface MXKRoomBubbleComponent ()
@property (nonatomic, readwrite) MXThread *thread;
@end
@implementation MXKRoomBubbleComponent
@@ -62,6 +69,8 @@
_showEncryptionBadge = [self shouldShowWarningBadgeForEvent:event roomState:(MXRoomState*)roomState session:session];
[self updateLinkWithRoomState:roomState];
self.thread = [session.threadingService threadWithId:event.eventId];
}
return self;
}
@@ -138,7 +138,7 @@ extern NSString *const kMXKRoomDataSourceTimelineErrorErrorKey;
The timeline being managed. It can be the live timeline of the room
or a timeline from a past event, initialEventId.
*/
@property (nonatomic, readonly) MXEventTimeline *timeline;
@property (nonatomic, readonly) id<MXEventTimeline> timeline;
/**
Flag indicating if the data source manages, or will manage, a live timeline.
@@ -169,6 +169,11 @@ extern NSString *const kMXKRoomDataSourceTimelineErrorErrorKey;
*/
@property (nonatomic) NSString *partialTextMessage;
/**
The current thread id for the data source. If provided, data source displays the specified thread, otherwise the whole room messages.
*/
@property (nonatomic, readonly) NSString *threadId;
#pragma mark - Configuration
/**
The text formatter applied on the events.
@@ -269,10 +274,15 @@ extern NSString *const kMXKRoomDataSourceTimelineErrorErrorKey;
@param roomId the id of the room to get data from.
@param initialEventId the id of the event where to start the timeline.
@param threadId the id of the thread to load. If provided, thread data source will be loaded from the room specified with `roomId`.
@param mxSession the Matrix session to get data from.
@param onComplete a block providing the newly created instance.
*/
+ (void)loadRoomDataSourceWithRoomId:(NSString*)roomId initialEventId:(NSString*)initialEventId andMatrixSession:(MXSession*)mxSession onComplete:(void (^)(id roomDataSource))onComplete;
+ (void)loadRoomDataSourceWithRoomId:(NSString*)roomId
initialEventId:(NSString*)initialEventId
threadId:(NSString*)threadId
andMatrixSession:(MXSession*)mxSession
onComplete:(void (^)(id roomDataSource))onComplete;
/**
Asynchronously create a data source to peek into a room.
@@ -306,10 +316,14 @@ extern NSString *const kMXKRoomDataSourceTimelineErrorErrorKey;
@param roomId the id of the room to get data from.
@param initialEventId the id of the event where to start the timeline.
@param threadId the id of the thread to initialize. If provided, thread data source will be initialized from the room specified with `roomId`.
@param mxSession the Matrix session to get data from.
@return the newly created instance.
*/
- (instancetype)initWithRoomId:(NSString*)roomId initialEventId:(NSString*)initialEventId andMatrixSession:(MXSession*)mxSession;
- (instancetype)initWithRoomId:(NSString*)roomId
initialEventId:(NSString*)initialEventId
threadId:(NSString*)threadId
andMatrixSession:(MXSession*)mxSession;
/**
Initialise the data source to peek into a room.
@@ -697,6 +711,20 @@ extern NSString *const kMXKRoomDataSourceTimelineErrorErrorKey;
*/
+ (dispatch_queue_t)processingQueue;
/**
Decides whether an event should be considered for asynchronous event processing.
Default implementation checks for `filterMessagesWithURL` and undecryptable events sent before the user joined.
Subclasses must call super at some point.
@param event event to be processed or not
@param roomState the state of the room when the event fired
@param direction the direction of the event
@return YES to process the event, NO otherwise
*/
- (BOOL)shouldQueueEventForProcessing:(MXEvent*)event
roomState:(MXRoomState*)roomState
direction:(MXTimelineDirection)direction;
#pragma mark - Bubble collapsing
/**
@@ -34,6 +34,7 @@
#import "MXKSendReplyEventStringLocalizer.h"
#import "MXKSlashCommands.h"
const BOOL USE_THREAD_TIMELINE = NO;
#pragma mark - Constant definitions
@@ -201,9 +202,11 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
@property (nonatomic, assign) BOOL shouldStopBackPagination;
@property (nonatomic, readwrite) MXRoom *room;
@property (nonatomic, readwrite) MXThread *thread;
@property (nonatomic, readwrite) MXRoom *secondaryRoom;
@property (nonatomic, strong) MXEventTimeline *secondaryTimeline;
@property (nonatomic, strong) id<MXEventTimeline> secondaryTimeline;
@property (nonatomic, readwrite) NSString *threadId;
@end
@@ -215,9 +218,9 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
[self ensureSessionStateForDataSource:roomDataSource initialEventId:nil andMatrixSession:mxSession onComplete:onComplete];
}
+ (void)loadRoomDataSourceWithRoomId:(NSString*)roomId initialEventId:(NSString*)initialEventId andMatrixSession:(MXSession*)mxSession onComplete:(void (^)(id roomDataSource))onComplete
+ (void)loadRoomDataSourceWithRoomId:(NSString*)roomId initialEventId:(NSString*)initialEventId threadId:(NSString*)threadId andMatrixSession:(MXSession*)mxSession onComplete:(void (^)(id roomDataSource))onComplete
{
MXKRoomDataSource *roomDataSource = [[self alloc] initWithRoomId:roomId initialEventId:initialEventId andMatrixSession:mxSession];
MXKRoomDataSource *roomDataSource = [[self alloc] initWithRoomId:roomId initialEventId:initialEventId threadId:threadId andMatrixSession:mxSession];
[self ensureSessionStateForDataSource:roomDataSource initialEventId:initialEventId andMatrixSession:mxSession onComplete:onComplete];
}
@@ -257,9 +260,31 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
// Asynchronously preload data here so that the data will be ready later
// to synchronously respond to that request
[roomDataSource.room liveTimeline:^(MXEventTimeline *liveTimeline) {
onComplete(roomDataSource);
}];
if (USE_THREAD_TIMELINE)
{
if (roomDataSource.threadId)
{
[roomDataSource.thread liveTimeline:^(id<MXEventTimeline> _Nonnull liveTimeline) {
[liveTimeline resetPagination];
onComplete(roomDataSource);
}];
}
else
{
[roomDataSource.room liveTimeline:^(id<MXEventTimeline> liveTimeline) {
[liveTimeline resetPagination];
onComplete(roomDataSource);
}];
}
}
else
{
[roomDataSource.room liveTimeline:^(id<MXEventTimeline> liveTimeline) {
[liveTimeline resetPagination];
onComplete(roomDataSource);
}];
}
}
}
@@ -347,7 +372,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
return self;
}
- (instancetype)initWithRoomId:(NSString*)roomId initialEventId:(NSString*)initialEventId2 andMatrixSession:(MXSession*)mxSession
- (instancetype)initWithRoomId:(NSString*)roomId initialEventId:(NSString*)initialEventId2 threadId:(NSString*)threadId andMatrixSession:(MXSession*)mxSession
{
self = [self initWithRoomId:roomId andMatrixSession:mxSession];
if (self)
@@ -357,6 +382,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
initialEventId = initialEventId2;
_isLive = NO;
}
_threadId = threadId;
}
return self;
@@ -364,7 +390,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
- (instancetype)initWithPeekingRoom:(MXPeekingRoom*)peekingRoom2 andInitialEventId:(NSString*)theInitialEventId
{
self = [self initWithRoomId:peekingRoom2.roomId initialEventId:theInitialEventId andMatrixSession:peekingRoom2.mxSession];
self = [self initWithRoomId:peekingRoom2.roomId initialEventId:theInitialEventId threadId:nil andMatrixSession:peekingRoom2.mxSession];
if (self)
{
peekingRoom = peekingRoom2;
@@ -620,209 +646,318 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
{
if (MXSessionStateStoreDataReady <= self.mxSession.state)
{
// Check whether the room is not already set
if (!_room)
if (USE_THREAD_TIMELINE)
{
// Are we peeking into a random room or displaying a room the user is part of?
if (peekingRoom)
if (_threadId)
{
self.room = peekingRoom;
[self initializeTimelineForThread];
}
else
{
self.room = [self.mxSession roomWithRoomId:_roomId];
[self initializeTimelineForRoom];
}
}
else
{
[self initializeTimelineForRoom];
}
}
}
if (_room)
- (void)initializeTimelineForRoom
{
// Check whether the room is not already set
if (!_room)
{
// Are we peeking into a random room or displaying a room the user is part of?
if (peekingRoom)
{
self.room = peekingRoom;
}
else
{
self.room = [self.mxSession roomWithRoomId:_roomId];
}
if (_room)
{
// This is the time to set up the timeline according to the called init method
if (_isLive)
{
// This is the time to set up the timeline according to the called init method
if (_isLive)
{
// LIVE
MXWeakify(self);
[_room liveTimeline:^(MXEventTimeline *liveTimeline) {
MXStrongifyAndReturnIfNil(self);
// LIVE
MXWeakify(self);
[_room liveTimeline:^(id<MXEventTimeline> liveTimeline) {
MXStrongifyAndReturnIfNil(self);
self->_timeline = liveTimeline;
self->_timeline = liveTimeline;
// Only one pagination process can be done at a time by an MXRoom object.
// This assumption is satisfied by MatrixKit. Only MXRoomDataSource does it.
[self.timeline resetPagination];
// Only one pagination process can be done at a time by an MXRoom object.
// This assumption is satisfied by MatrixKit. Only MXRoomDataSource does it.
[self.timeline resetPagination];
// Observe room history flush (sync with limited timeline, or state event redaction)
self->roomDidFlushDataNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXRoomDidFlushDataNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
// Observe room history flush (sync with limited timeline, or state event redaction)
self->roomDidFlushDataNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXRoomDidFlushDataNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXRoom *room = notif.object;
if (self.mxSession == room.mxSession && ([self.roomId isEqualToString:room.roomId] ||
([self.secondaryRoomId isEqualToString:room.roomId])))
{
// The existing room history has been flushed during server sync because a gap has been observed between local and server storage.
[self reload];
}
}];
// Add the event listeners, by considering all the event types (the event filtering is applying by the event formatter),
// except if only the events with a url key in their content must be handled.
[self refreshEventListeners:(self.filterMessagesWithURL ? @[kMXEventTypeStringRoomMessage] : [MXKAppSettings standardAppSettings].allEventTypesForMessages)];
// display typing notifications is optional
// the inherited class can manage them by its own.
if (self.showTypingNotifications)
MXRoom *room = notif.object;
if (self.mxSession == room.mxSession && ([self.roomId isEqualToString:room.roomId] ||
([self.secondaryRoomId isEqualToString:room.roomId])))
{
// Register on typing notif
[self listenTypingNotifications];
// The existing room history has been flushed during server sync because a gap has been observed between local and server storage.
[self reload];
}
// Manage unsent messages
[self handleUnsentMessages];
// Update here data source state if it is not already ready
if (!self->_secondaryRoomId)
{
[self setState:MXKDataSourceStateReady];
}
// Check user membership in this room
MXMembership membership = self.room.summary.membership;
if (membership == MXMembershipUnknown || membership == MXMembershipInvite)
{
// Here the initial sync is not ended or the room is a pending invitation.
// Note: In case of invitation, a full sync will be triggered if the user joins this room.
// We have to observe here 'kMXRoomInitialSyncNotification' to reload room data when room sync is done.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didMXRoomInitialSynced:) name:kMXRoomInitialSyncNotification object:self.room];
}
}];
if (!_secondaryRoom && _secondaryRoomId)
// Add the event listeners, by considering all the event types (the event filtering is applying by the event formatter),
// except if only the events with a url key in their content must be handled.
[self refreshEventListeners:(self.filterMessagesWithURL ? @[kMXEventTypeStringRoomMessage] : [MXKAppSettings standardAppSettings].allEventTypesForMessages)];
// display typing notifications is optional
// the inherited class can manage them by its own.
if (self.showTypingNotifications)
{
_secondaryRoom = [self.mxSession roomWithRoomId:_secondaryRoomId];
if (_secondaryRoom)
{
MXWeakify(self);
[_secondaryRoom liveTimeline:^(MXEventTimeline *liveTimeline) {
MXStrongifyAndReturnIfNil(self);
// Register on typing notif
[self listenTypingNotifications];
}
self->_secondaryTimeline = liveTimeline;
// Manage unsent messages
[self handleUnsentMessages];
// Only one pagination process can be done at a time by an MXRoom object.
// This assumption is satisfied by MatrixKit. Only MXRoomDataSource does it.
[self.secondaryTimeline resetPagination];
// Update here data source state if it is not already ready
if (!self->_secondaryRoomId)
{
[self setState:MXKDataSourceStateReady];
}
// Add the secondary event listeners, by considering the event types in self.secondaryRoomEventTypes
[self refreshSecondaryEventListeners:self.secondaryRoomEventTypes];
// Update here data source state if it is not already ready
[self setState:MXKDataSourceStateReady];
// Check user membership in this room
MXMembership membership = self.room.summary.membership;
if (membership == MXMembershipUnknown || membership == MXMembershipInvite)
{
// Here the initial sync is not ended or the room is a pending invitation.
// Note: In case of invitation, a full sync will be triggered if the user joins this room.
// Check user membership in the secondary room
MXMembership membership = self.secondaryRoom.summary.membership;
if (membership == MXMembershipUnknown || membership == MXMembershipInvite)
{
// Here the initial sync is not ended or the room is a pending invitation.
// Note: In case of invitation, a full sync will be triggered if the user joins this room.
// We have to observe here 'kMXRoomInitialSyncNotification' to reload room data when room sync is done.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didMXRoomInitialSynced:) name:kMXRoomInitialSyncNotification object:self.room];
}
}];
if (!_secondaryRoom && _secondaryRoomId)
{
_secondaryRoom = [self.mxSession roomWithRoomId:_secondaryRoomId];
if (_secondaryRoom)
{
MXWeakify(self);
[_secondaryRoom liveTimeline:^(id<MXEventTimeline> liveTimeline) {
MXStrongifyAndReturnIfNil(self);
// We have to observe here 'kMXRoomInitialSyncNotification' to reload room data when room sync is done.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didMXRoomInitialSynced:) name:kMXRoomInitialSyncNotification object:self.secondaryRoom];
}
}];
}
self->_secondaryTimeline = liveTimeline;
// Only one pagination process can be done at a time by an MXRoom object.
// This assumption is satisfied by MatrixKit. Only MXRoomDataSource does it.
[self.secondaryTimeline resetPagination];
// Add the secondary event listeners, by considering the event types in self.secondaryRoomEventTypes
[self refreshSecondaryEventListeners:self.secondaryRoomEventTypes];
// Update here data source state if it is not already ready
[self setState:MXKDataSourceStateReady];
// Check user membership in the secondary room
MXMembership membership = self.secondaryRoom.summary.membership;
if (membership == MXMembershipUnknown || membership == MXMembershipInvite)
{
// Here the initial sync is not ended or the room is a pending invitation.
// Note: In case of invitation, a full sync will be triggered if the user joins this room.
// We have to observe here 'kMXRoomInitialSyncNotification' to reload room data when room sync is done.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didMXRoomInitialSynced:) name:kMXRoomInitialSyncNotification object:self.secondaryRoom];
}
}];
}
}
else
{
// Past timeline
// Less things need to configured
_timeline = [_room timelineOnEvent:initialEventId];
}
else
{
// Past timeline
// Less things need to configured
_timeline = [_room timelineOnEvent:initialEventId];
// Refresh the event listeners. Note: events for past timelines come only from pagination request
[self refreshEventListeners:nil];
// Refresh the event listeners. Note: events for past timelines come only from pagination request
[self refreshEventListeners:nil];
MXWeakify(self);
// Preload the state and some messages around the initial event
[_timeline resetPaginationAroundInitialEventWithLimit:_paginationLimitAroundInitialEvent success:^{
MXStrongifyAndReturnIfNil(self);
MXWeakify(self);
// Do a "classic" reset. The room view controller will paginate
// from the events stored in the timeline store
[self.timeline resetPagination];
// Update here data source state if it is not already ready
[self setState:MXKDataSourceStateReady];
// Preload the state and some messages around the initial event
[_timeline resetPaginationAroundInitialEventWithLimit:_paginationLimitAroundInitialEvent success:^{
} failure:^(NSError *error) {
MXStrongifyAndReturnIfNil(self);
MXStrongifyAndReturnIfNil(self);
// Do a "classic" reset. The room view controller will paginate
// from the events stored in the timeline store
[self.timeline resetPagination];
// Update here data source state if it is not already ready
[self setState:MXKDataSourceStateReady];
MXLogDebug(@"[MXKRoomDataSource][%p] Failed to resetPaginationAroundInitialEventWithLimit", self);
// Notify the error
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKRoomDataSourceTimelineError
object:self
userInfo:@{
kMXKRoomDataSourceTimelineErrorErrorKey: error
}];
}];
}
}
else
{
MXLogDebug(@"[MXKRoomDataSource][%p] Warning: The user does not know the room %@", self, _roomId);
// Update here data source state if it is not already ready
[self setState:MXKDataSourceStateFailed];
}
}
if (_room && MXSessionStateRunning == self.mxSession.state)
{
// Flair handling: observe the update in the publicised groups by users when the flair is enabled in the room.
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXSessionDidUpdatePublicisedGroupsForUsersNotification object:self.mxSession];
[self.room state:^(MXRoomState *roomState) {
if (roomState.relatedGroups.count)
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didMXSessionUpdatePublicisedGroupsForUsers:) name:kMXSessionDidUpdatePublicisedGroupsForUsersNotification object:self.mxSession];
// Get a fresh profile for all the related groups. Trigger a table refresh when all requests are done.
__block NSUInteger count = roomState.relatedGroups.count;
for (NSString *groupId in roomState.relatedGroups)
{
MXGroup *group = [self.mxSession groupWithGroupId:groupId];
if (!group)
{
// Create a group instance for the groups that the current user did not join.
group = [[MXGroup alloc] initWithGroupId:groupId];
[self->externalRelatedGroups setObject:group forKey:groupId];
}
// Refresh the group profile from server.
[self.mxSession updateGroupProfile:group success:^{
if (self.delegate && !(--count))
{
// All the requests have been done.
[self.delegate dataSource:self didCellChange:nil];
}
} failure:^(NSError *error) {
MXStrongifyAndReturnIfNil(self);
MXLogDebug(@"[MXKRoomDataSource][%p] Failed to resetPaginationAroundInitialEventWithLimit", self);
MXLogDebug(@"[MXKRoomDataSource][%p] group profile update failed %@", self, groupId);
if (self.delegate && !(--count))
{
// All the requests have been done.
[self.delegate dataSource:self didCellChange:nil];
}
// Notify the error
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKRoomDataSourceTimelineError
object:self
userInfo:@{
kMXKRoomDataSourceTimelineErrorErrorKey: error
}];
}];
}
}
else
{
MXLogDebug(@"[MXKRoomDataSource][%p] Warning: The user does not know the room %@", self, _roomId);
// Update here data source state if it is not already ready
[self setState:MXKDataSourceStateFailed];
}
}
if (_room && MXSessionStateRunning == self.mxSession.state)
}];
}
}
- (void)initializeTimelineForThread
{
// Check whether the thread is not already set
if (_thread && self.state == MXKDataSourceStateReady)
{
return;
}
_thread = [self.mxSession.threadingService threadWithId:_threadId];
if (!_thread)
{
// there is not a thread yet available, this will be a new thread
_thread = [self.mxSession.threadingService createTempThreadWithId:_threadId roomId:_roomId];
}
if (!_room)
{
// also hold a reference to the room
_room = [self.mxSession roomWithRoomId:_roomId];
}
if (_thread)
{
if (_isLive)
{
// Flair handling: observe the update in the publicised groups by users when the flair is enabled in the room.
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXSessionDidUpdatePublicisedGroupsForUsersNotification object:self.mxSession];
[self.room state:^(MXRoomState *roomState) {
if (roomState.relatedGroups.count)
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didMXSessionUpdatePublicisedGroupsForUsers:) name:kMXSessionDidUpdatePublicisedGroupsForUsersNotification object:self.mxSession];
// Get a fresh profile for all the related groups. Trigger a table refresh when all requests are done.
__block NSUInteger count = roomState.relatedGroups.count;
for (NSString *groupId in roomState.relatedGroups)
{
MXGroup *group = [self.mxSession groupWithGroupId:groupId];
if (!group)
{
// Create a group instance for the groups that the current user did not join.
group = [[MXGroup alloc] initWithGroupId:groupId];
[self->externalRelatedGroups setObject:group forKey:groupId];
}
// Refresh the group profile from server.
[self.mxSession updateGroupProfile:group success:^{
if (self.delegate && !(--count))
{
// All the requests have been done.
[self.delegate dataSource:self didCellChange:nil];
}
} failure:^(NSError *error) {
MXLogDebug(@"[MXKRoomDataSource][%p] group profile update failed %@", self, groupId);
if (self.delegate && !(--count))
{
// All the requests have been done.
[self.delegate dataSource:self didCellChange:nil];
}
}];
}
}
[_thread liveTimeline:^(id<MXEventTimeline> _Nonnull liveTimeline) {
self->_timeline = liveTimeline;
// Only one pagination process can be done at a time by an MXThread object.
// This assumption is satisfied by MXRoomDataSource.
[self.timeline resetPagination];
// Add the event listeners, by considering all the event types (the event filtering is applying by the event formatter),
// except if only the events with a url key in their content must be handled.
[self refreshEventListeners:(self.filterMessagesWithURL ? @[kMXEventTypeStringRoomMessage] : [MXKAppSettings standardAppSettings].allEventTypesForMessages)];
// Manage unsent messages
[self handleUnsentMessages];
[self setState:MXKDataSourceStateReady];
}];
}
else
{
// Past timeline
// Less things need to configured
_timeline = [_thread timelineOnEvent:initialEventId];
// Refresh the event listeners. Note: events for past timelines come only from pagination request
[self refreshEventListeners:nil];
MXWeakify(self);
// Preload the state and some messages around the initial event
[_timeline resetPaginationAroundInitialEventWithLimit:_paginationLimitAroundInitialEvent success:^{
MXStrongifyAndReturnIfNil(self);
// Do a "classic" reset. The room view controller will paginate
// from the events stored in the timeline store
[self.timeline resetPagination];
// Update here data source state if it is not already ready
[self setState:MXKDataSourceStateReady];
} failure:^(NSError *error) {
MXStrongifyAndReturnIfNil(self);
MXLogDebug(@"[MXKRoomDataSource][%p] Failed to resetPaginationAroundInitialEventWithLimit", self);
// Notify the error
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKRoomDataSourceTimelineError
object:self
userInfo:@{
kMXKRoomDataSourceTimelineErrorErrorKey: error
}];
}];
}
}
else
{
MXLogDebug(@"[MXKRoomDataSource][%p] Warning: The user does not know the thread %@", self, _threadId);
// Update here data source state if it is not already ready
[self setState:MXKDataSourceStateFailed];
}
}
@@ -1289,6 +1424,34 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
}
- (BOOL)shouldQueueEventForProcessing:(MXEvent*)event roomState:(MXRoomState*)roomState direction:(MXTimelineDirection)direction
{
if (self.filterMessagesWithURL)
{
// Check whether the event has a value for the 'url' key in its content.
if (!event.getMediaURLs.count)
{
// ignore the event
return NO;
}
}
// Check for undecryptable messages that were sent while the user was not in the room and hide them
if ([MXKAppSettings standardAppSettings].hidePreJoinedUndecryptableEvents
&& direction == MXTimelineDirectionBackwards)
{
[self checkForPreJoinUTDWithEvent:event roomState:roomState];
// Hide pre joint UTD events
if (self.shouldStopBackPagination)
{
return NO;
}
}
return YES;
}
#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
@@ -1425,7 +1588,10 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
// Launch the pagination
MXWeakify(self);
paginationRequest = [_timeline paginate:numItems direction:direction onlyFromStore:onlyFromStore complete:^{
paginationRequest = [_timeline paginate:numItems
direction:direction
onlyFromStore:onlyFromStore
complete:^{
MXStrongifyAndReturnIfNil(self);
@@ -1489,7 +1655,10 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
dispatch_group_enter(dispatchGroup);
// Launch the pagination
MXWeakify(self);
secondaryPaginationRequest = [_secondaryTimeline paginate:numItems direction:direction onlyFromStore:onlyFromStore complete:^{
secondaryPaginationRequest = [_secondaryTimeline paginate:numItems
direction:direction
onlyFromStore:onlyFromStore
complete:^{
MXStrongifyAndReturnIfNil(self);
@@ -1676,11 +1845,11 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
// Make the request to the homeserver
if (isEmote)
{
[_room sendEmote:sanitizedText formattedText:html localEcho:&localEchoEvent success:success failure:failure];
[_room sendEmote:sanitizedText formattedText:html threadId:self.threadId localEcho:&localEchoEvent success:success failure:failure];
}
else
{
[_room sendTextMessage:sanitizedText formattedText:html localEcho:&localEchoEvent success:success failure:failure];
[_room sendTextMessage:sanitizedText formattedText:html threadId:self.threadId localEcho:&localEchoEvent success:success failure:failure];
}
if (localEchoEvent)
@@ -1821,7 +1990,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
{
__block MXEvent *localEchoEvent = nil;
[_room sendImage:imageData withImageSize:imageSize mimeType:mimetype andThumbnail:thumbnail localEcho:&localEchoEvent success:success failure:failure];
[_room sendImage:imageData withImageSize:imageSize mimeType:mimetype andThumbnail:thumbnail threadId:self.threadId localEcho:&localEchoEvent success:success failure:failure];
if (localEchoEvent)
{
@@ -1841,7 +2010,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
{
__block MXEvent *localEchoEvent = nil;
[_room sendVideoAsset:videoAsset withThumbnail:videoThumbnail localEcho:&localEchoEvent success:success failure:failure];
[_room sendVideoAsset:videoAsset withThumbnail:videoThumbnail threadId:self.threadId localEcho:&localEchoEvent success:success failure:failure];
if (localEchoEvent)
{
@@ -1855,7 +2024,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
{
__block MXEvent *localEchoEvent = nil;
[_room sendAudioFile:audioFileLocalURL mimeType:mimeType localEcho:&localEchoEvent success:success failure:failure keepActualFilename:YES];
[_room sendAudioFile:audioFileLocalURL mimeType:mimeType threadId:self.threadId localEcho:&localEchoEvent success:success failure:failure keepActualFilename:YES];
if (localEchoEvent)
{
@@ -1874,7 +2043,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
{
__block MXEvent *localEchoEvent = nil;
[_room sendVoiceMessage:audioFileLocalURL mimeType:mimeType duration:duration samples:samples localEcho:&localEchoEvent success:success failure:failure keepActualFilename:YES];
[_room sendVoiceMessage:audioFileLocalURL mimeType:mimeType duration:duration samples:samples threadId:self.threadId localEcho:&localEchoEvent success:success failure:failure keepActualFilename:YES];
if (localEchoEvent)
{
@@ -1889,7 +2058,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
{
__block MXEvent *localEchoEvent = nil;
[_room sendFile:fileLocalURL mimeType:mimeType localEcho:&localEchoEvent success:success failure:failure];
[_room sendFile:fileLocalURL mimeType:mimeType threadId:self.threadId localEcho:&localEchoEvent success:success failure:failure];
if (localEchoEvent)
{
@@ -1904,7 +2073,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
__block MXEvent *localEchoEvent = nil;
// Make the request to the homeserver
[_room sendMessageWithContent:msgContent localEcho:&localEchoEvent success:success failure:failure];
[_room sendMessageWithContent:msgContent threadId:self.threadId localEcho:&localEchoEvent success:success failure:failure];
if (localEchoEvent)
{
@@ -1926,6 +2095,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
[_room sendLocationWithLatitude:latitude
longitude:longitude
description:description
threadId:self.threadId
localEcho:&localEchoEvent
success:success failure:failure];
@@ -1942,7 +2112,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
__block MXEvent *localEchoEvent = nil;
// Make the request to the homeserver
[_room sendEventOfType:eventTypeString content:msgContent localEcho:&localEchoEvent success:success failure:failure];
[_room sendEventOfType:eventTypeString content:msgContent threadId:self.threadId localEcho:&localEchoEvent success:success failure:failure];
if (localEchoEvent)
{
@@ -1969,7 +2139,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
{
// We try here to resent an encrypted event
// Note: we keep the existing local echo.
[_room sendEventOfType:kMXEventTypeStringRoomEncrypted content:event.wireContent localEcho:&event success:success failure:failure];
[_room sendEventOfType:kMXEventTypeStringRoomEncrypted content:event.wireContent threadId:self.threadId localEcho:&event success:success failure:failure];
}
else if ([event.type isEqualToString:kMXEventTypeStringRoomMessage])
{
@@ -1978,7 +2148,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
if ([msgType isEqualToString:kMXMessageTypeText] || [msgType isEqualToString:kMXMessageTypeEmote])
{
// Resend the Matrix event by reusing the existing echo
[_room sendMessageWithContent:event.content localEcho:&event success:success failure:failure];
[_room sendMessageWithContent:event.content threadId:self.threadId localEcho:&event success:success failure:failure];
}
else if ([msgType isEqualToString:kMXMessageTypeImage])
{
@@ -2021,7 +2191,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
else
{
// Resend the Matrix event by reusing the existing echo
[_room sendMessageWithContent:event.content localEcho:&event success:success failure:failure];
[_room sendMessageWithContent:event.content threadId:self.threadId localEcho:&event success:success failure:failure];
}
}
else if ([msgType isEqualToString:kMXMessageTypeAudio])
@@ -2032,7 +2202,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
if (!contentURL || ![contentURL hasPrefix:kMXMediaUploadIdPrefix])
{
// Resend the Matrix event by reusing the existing echo
[_room sendMessageWithContent:event.content localEcho:&event success:success failure:failure];
[_room sendMessageWithContent:event.content threadId:self.threadId localEcho:&event success:success failure:failure];
return;
}
@@ -2050,8 +2220,8 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
[self removeEventWithEventId:eventId];
if (event.isVoiceMessage) {
NSNumber *duration = event.content[kMXMessageContentKeyExtensibleAudio][kMXMessageContentKeyExtensibleAudioDuration];
NSArray<NSNumber *> *samples = event.content[kMXMessageContentKeyExtensibleAudio][kMXMessageContentKeyExtensibleAudioWaveform];
NSNumber *duration = event.content[kMXMessageContentKeyExtensibleAudioMSC1767][kMXMessageContentKeyExtensibleAudioDuration];
NSArray<NSNumber *> *samples = event.content[kMXMessageContentKeyExtensibleAudioMSC1767][kMXMessageContentKeyExtensibleAudioWaveform];
[self sendVoiceMessage:localFileURL mimeType:mimetype duration:duration.doubleValue samples:samples success:success failure:failure];
} else {
@@ -2072,7 +2242,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
else
{
// Resend the Matrix event by reusing the existing echo
[_room sendMessageWithContent:event.content localEcho:&event success:success failure:failure];
[_room sendMessageWithContent:event.content threadId:self.threadId localEcho:&event success:success failure:failure];
}
}
else if ([msgType isEqualToString:kMXMessageTypeFile])
@@ -2108,7 +2278,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
else
{
// Resend the Matrix event by reusing the existing echo
[_room sendMessageWithContent:event.content localEcho:&event success:success failure:failure];
[_room sendMessageWithContent:event.content threadId:self.threadId localEcho:&event success:success failure:failure];
}
}
else
@@ -2805,27 +2975,9 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
MXLogVerbose(@"[MXKRoomDataSource][%p] queueEventForProcessing: %@", self, event.eventId);
}
if (self.filterMessagesWithURL)
if (![self shouldQueueEventForProcessing:event roomState:roomState direction:direction])
{
// Check whether the event has a value for the 'url' key in its content.
if (!event.getMediaURLs.count)
{
// Ignore the event
return;
}
}
// Check for undecryptable messages that were sent while the user was not in the room and hide them
if ([MXKAppSettings standardAppSettings].hidePreJoinedUndecryptableEvents
&& direction == MXTimelineDirectionBackwards)
{
[self checkForPreJoinUTDWithEvent:event roomState:roomState];
// Hide pre joint UTD events
if (self.shouldStopBackPagination)
{
return;
}
return;
}
MXKQueuedEvent *queuedEvent = [[MXKQueuedEvent alloc] initWithEvent:event andRoomState:roomState direction:direction];
@@ -85,7 +85,7 @@ NSString *const kMXKRoomMemberCellIdentifier = @"kMXKRoomMemberCellIdentifier";
if (typingNotifListener)
{
MXWeakify(self);
[mxRoom liveTimeline:^(MXEventTimeline *liveTimeline) {
[mxRoom liveTimeline:^(id<MXEventTimeline> liveTimeline) {
MXStrongifyAndReturnIfNil(self);
[liveTimeline removeListener:self->typingNotifListener];
@@ -325,11 +325,12 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
// Check first whether the event has been redacted
NSString *redactedInfo = nil;
BOOL isRedacted = (event.redactedBecause != nil);
BOOL isRedacted = event.isRedactedEvent;
if (isRedacted)
{
// Check whether redacted information is required
if (_settings.showRedactionsInRoomHistory)
// Check whether the event is a thread root or redacted information is required
if ((RiotSettings.shared.enableThreads && [mxSession.threadingService isEventThreadRoot:event])
|| _settings.showRedactionsInRoomHistory)
{
MXLogDebug(@"[MXKEventFormatter] Redacted event %@ (%@)", event.description, event.redactedBecause);
@@ -1252,7 +1253,7 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
NSString *body;
BOOL isHTML = NO;
NSString *eventThreadIdentifier = event.threadIdentifier;
NSString *eventThreadId = event.threadId;
// Use the HTML formatted string if provided
if ([event.content[@"format"] isEqualToString:kMXRoomMessageFormatHTML])
@@ -1260,20 +1261,21 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
isHTML =YES;
MXJSONModelSetString(body, event.content[@"formatted_body"]);
}
else if (eventThreadIdentifier)
else if (eventThreadId && !RiotSettings.shared.enableThreads)
{
NSString *repliedEventId = event.relatesTo.inReplyTo.eventId ?: eventThreadId;
isHTML = YES;
MXJSONModelSetString(body, event.content[kMXMessageBodyKey]);
MXEvent *threadRootEvent = [mxSession.store eventWithEventId:eventThreadIdentifier
inRoom:event.roomId];
MXEvent *repliedEvent = [mxSession.store eventWithEventId:repliedEventId
inRoom:event.roomId];
NSString *threadRootEventContent;
MXJSONModelSetString(threadRootEventContent, threadRootEvent.content[kMXMessageBodyKey]);
NSString *repliedEventContent;
MXJSONModelSetString(repliedEventContent, repliedEvent.content[kMXMessageBodyKey]);
body = [NSString stringWithFormat:@"<mx-reply><blockquote><a href=\"%@\">In reply to</a> <a href=\"%@\">%@</a><br>%@</blockquote></mx-reply>%@",
[MXTools permalinkToEvent:eventThreadIdentifier inRoom:event.roomId],
[MXTools permalinkToUserWithUserId:threadRootEvent.sender],
threadRootEvent.sender,
threadRootEventContent,
[MXTools permalinkToEvent:repliedEventId inRoom:event.roomId],
[MXTools permalinkToUserWithUserId:repliedEvent.sender],
repliedEvent.sender,
repliedEventContent,
body];
}
@@ -1359,9 +1361,7 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
// For replies, look for the end of the parent message
// This helps us insert the emote prefix in the right place
NSDictionary *relatesTo;
MXJSONModelSetDictionary(relatesTo, event.content[@"m.relates_to"]);
if ([relatesTo[@"m.in_reply_to"] isKindOfClass:NSDictionary.class] || event.isInThread)
if (event.relatesTo.inReplyTo || (event.isInThread && !RiotSettings.shared.enableThreads))
{
[attributedDisplayText enumerateAttribute:kMXKToolsBlockquoteMarkAttribute
inRange:NSMakeRange(0, attributedDisplayText.length)
@@ -1577,6 +1577,11 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
}
case MXEventTypePollStart:
{
if (event.isEditEvent)
{
return nil;
}
displayText = [MXEventContentPollStart modelFromJSON:event.content].question;
break;
}
@@ -1699,7 +1704,7 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
NSString *html = htmlString;
// Special treatment for "In reply to" message
if (event.isReplyEvent || event.isInThread)
if (event.isReplyEvent || (event.isInThread && !RiotSettings.shared.enableThreads))
{
html = [self renderReplyTo:html withRoomState:roomState];
}
@@ -2025,7 +2030,7 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
textColor = _errorTextColor;
}
// Check whether the message is highlighted.
else if (event.mxkIsHighlighted || (event.isInThread && ![event.sender isEqualToString:mxSession.myUserId]))
else if (event.mxkIsHighlighted || (event.isInThread && !RiotSettings.shared.enableThreads && ![event.sender isEqualToString:mxSession.myUserId]))
{
textColor = _bingTextColor;
}
@@ -2089,7 +2094,7 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
{
font = _callNoticesTextFont;
}
else if (event.mxkIsHighlighted || (event.isInThread && ![event.sender isEqualToString:mxSession.myUserId]))
else if (event.mxkIsHighlighted || (event.isInThread && !RiotSettings.shared.enableThreads && ![event.sender isEqualToString:mxSession.myUserId]))
{
font = _bingTextFont;
}
@@ -1,328 +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 "MXKTableViewCell.h"
#import "MXKCellRendering.h"
#import "MXKReceiptSendersContainer.h"
#import <WebKit/WebKit.h>
@class MXKImageView;
@class MXKPieChartView;
@class MXKRoomBubbleCellData;
#pragma mark - MXKCellRenderingDelegate cell tap locations
/**
Action identifier used when the user tapped on message text view.
The `userInfo` dictionary contains an `MXEvent` object under the `kMXKRoomBubbleCellEventKey` key, representing the tapped event.
*/
extern NSString *const kMXKRoomBubbleCellTapOnMessageTextView;
/**
Action identifier used when the user tapped on user name label.
The `userInfo` dictionary contains an `NSString` object under the `kMXKRoomBubbleCellUserIdKey` key, representing the user id of the tapped name label.
*/
extern NSString *const kMXKRoomBubbleCellTapOnSenderNameLabel;
/**
Action identifier used when the user tapped on avatar view.
The `userInfo` dictionary contains an `NSString` object under the `kMXKRoomBubbleCellUserIdKey` key, representing the user id of the tapped avatar.
*/
extern NSString *const kMXKRoomBubbleCellTapOnAvatarView;
/**
Action identifier used when the user tapped on date/time container.
The `userInfo` is nil.
*/
extern NSString *const kMXKRoomBubbleCellTapOnDateTimeContainer;
/**
Action identifier used when the user tapped on attachment view.
The `userInfo` is nil. The attachment can be retrieved via MXKRoomBubbleTableViewCell.attachmentView.
*/
extern NSString *const kMXKRoomBubbleCellTapOnAttachmentView;
/**
Action identifier used when the user tapped on overlay container.
The `userInfo` is nil
*/
extern NSString *const kMXKRoomBubbleCellTapOnOverlayContainer;
/**
Action identifier used when the user tapped on content view.
The `userInfo` dictionary may contain an `MXEvent` object under the `kMXKRoomBubbleCellEventKey` key, representing the event displayed at the level of the tapped line. This dictionary is empty if no event correspond to the tapped position.
*/
extern NSString *const kMXKRoomBubbleCellTapOnContentView;
/**
Action identifier used when the user pressed unsent button displayed in front of an unsent event.
The `userInfo` dictionary contains an `MXEvent` object under the `kMXKRoomBubbleCellEventKey` key, representing the unsent event.
*/
extern NSString *const kMXKRoomBubbleCellUnsentButtonPressed;
/**
Action identifier used when the user long pressed on a displayed event.
The `userInfo` dictionary contains an `MXEvent` object under the `kMXKRoomBubbleCellEventKey` key, representing the selected event.
*/
extern NSString *const kMXKRoomBubbleCellLongPressOnEvent;
/**
Action identifier used when the user long pressed on progress view.
The `userInfo` is nil. The progress view can be retrieved via MXKRoomBubbleTableViewCell.progressView.
*/
extern NSString *const kMXKRoomBubbleCellLongPressOnProgressView;
/**
Action identifier used when the user long pressed on avatar view.
The `userInfo` dictionary contains an `NSString` object under the `kMXKRoomBubbleCellUserIdKey` key, representing the user id of the concerned avatar.
*/
extern NSString *const kMXKRoomBubbleCellLongPressOnAvatarView;
/**
Action identifier used when the user clicked on a link.
This action is sent via the MXKCellRenderingDelegate `shouldDoAction` operation.
The `userInfo` dictionary contains a `NSURL` object under the `kMXKRoomBubbleCellUrl` key, representing the url the user wants to open. And a NSNumber wrapping `UITextItemInteraction` raw value, representing the type of interaction expected with the URL, under the `kMXKRoomBubbleCellUrlItemInteraction` key.
The shouldDoAction implementation must return NO to prevent the system (safari) from opening the link.
@discussion: If the link refers to a room alias/id, a user id or an event id, the non-ASCII characters (like '#' in room alias) has been
escaped to be able to convert it into a legal URL string.
*/
extern NSString *const kMXKRoomBubbleCellShouldInteractWithURL;
/**
Notifications `userInfo` keys
*/
extern NSString *const kMXKRoomBubbleCellUserIdKey;
extern NSString *const kMXKRoomBubbleCellEventKey;
extern NSString *const kMXKRoomBubbleCellEventIdKey;
extern NSString *const kMXKRoomBubbleCellReceiptsContainerKey;
extern NSString *const kMXKRoomBubbleCellUrl;
extern NSString *const kMXKRoomBubbleCellUrlItemInteraction;
#pragma mark - MXKRoomBubbleTableViewCell
/**
`MXKRoomBubbleTableViewCell` is a base class for displaying a room bubble.
This class is used to handle a maximum of items which may be present in bubbles display (like the user's picture view, the message text view...).
To optimize bubbles rendering, we advise to define a .xib for each kind of bubble layout (with or without sender's information, with or without attachment...).
Each inherited class should define only the actual displayed items.
*/
@interface MXKRoomBubbleTableViewCell : MXKTableViewCell <MXKCellRendering, UITextViewDelegate, WKNavigationDelegate>
{
@protected
/**
The current bubble data displayed by the table view cell
*/
MXKRoomBubbleCellData *bubbleData;
}
/**
The current bubble data displayed by the table view cell
*/
@property (strong, nonatomic, readonly) MXKRoomBubbleCellData *bubbleData;
/**
Option to highlight or not the content of message text view (May be used in case of text selection).
NO by default.
*/
@property (nonatomic) BOOL allTextHighlighted;
/**
Tell whether the animation should start automatically in case of animated gif (NO by default).
*/
@property (nonatomic) BOOL isAutoAnimatedGif;
/**
The default picture displayed when no picture is available.
*/
@property (nonatomic) UIImage *picturePlaceholder;
/**
The list of the temporary subviews that should be removed before reusing the cell (nil by default).
*/
@property (nonatomic) NSMutableArray<UIView*> *tmpSubviews;
/**
The read receipts alignment.
By default, they are left aligned.
*/
@property (nonatomic) ReadReceiptsAlignment readReceiptsAlignment;
@property (weak, nonatomic) IBOutlet UILabel *userNameLabel;
@property (weak, nonatomic) IBOutlet UIView *userNameTapGestureMaskView;
@property (strong, nonatomic) IBOutlet MXKImageView *pictureView;
@property (weak, nonatomic) IBOutlet UITextView *messageTextView;
@property (strong, nonatomic) IBOutlet MXKImageView *attachmentView;
@property (strong, nonatomic) IBOutlet UIImageView *playIconView;
@property (strong, nonatomic) IBOutlet UIImageView *fileTypeIconView;
@property (weak, nonatomic) IBOutlet UIView *bubbleInfoContainer;
@property (weak, nonatomic) IBOutlet UIView *bubbleOverlayContainer;
/**
The container view in which the encryption information may be displayed
*/
@property (weak, nonatomic) IBOutlet UIView *encryptionStatusContainerView;
@property (weak, nonatomic) IBOutlet UIView *progressView;
@property (weak, nonatomic) IBOutlet UILabel *statsLabel;
@property (weak, nonatomic) IBOutlet MXKPieChartView *progressChartView;
/**
The constraints which defines the relationship between messageTextView and its superview.
The defined constant are supposed >= 0.
*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *msgTextViewTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *msgTextViewBottomConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *msgTextViewLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *msgTextViewTrailingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *msgTextViewWidthConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *msgTextViewMinHeightConstraint;
/**
The constraints which defines the relationship between attachmentView and its superview
The defined constant are supposed >= 0.
*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *attachViewWidthConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *attachViewMinHeightConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *attachViewTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *attachViewBottomConstraint;
/**
The constraints which defines the relationship between bubbleInfoContainer and its superview
The defined constant are supposed >= 0.
*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bubbleInfoContainerTopConstraint;
/**
The read marker view and its layout constraints (nil by default).
*/
@property (nonatomic) UIView *readMarkerView;
@property (nonatomic) NSLayoutConstraint *readMarkerViewTopConstraint;
@property (nonatomic) NSLayoutConstraint *readMarkerViewLeadingConstraint;
@property (nonatomic) NSLayoutConstraint *readMarkerViewTrailingConstraint;
@property (nonatomic) NSLayoutConstraint *readMarkerViewHeightConstraint;
/**
The potential webview used to render an attachment (for example an animated gif).
*/
@property (nonatomic) WKWebView *attachmentWebView;
/**
Called during the designated initializer of the UITableViewCell class to set the default
properties values.
You should not call this method directly.
Subclasses can override this method as needed to customize the initialization.
*/
- (void)finalizeInit;
/**
Handle progressView display.
*/
- (void)startProgressUI;
- (void)updateProgressUI:(NSDictionary*)statisticsDict;
#pragma mark - Original Xib values
/**
Get an original instance of the `MXKRoomBubbleTableViewCell` child class.
@return an instance of the child class caller which has the original Xib values.
*/
+ (MXKRoomBubbleTableViewCell*)cellWithOriginalXib;
/**
Disable the handling of the long press on event (see kMXKRoomBubbleCellLongPressOnEvent). NO by default.
CAUTION: Changing this flag only impact the new created cells (existing 'MXKRoomBubbleTableViewCell' instances are unchanged).
*/
+ (void)disableLongPressGestureOnEvent:(BOOL)disable;
/**
Method used during [MXKCellRendering render:] to check the provided `cellData`
and prepare the protected `bubbleData`.
Do not override it.
@param cellData the data object to render.
*/
- (void)prepareRender:(MXKCellData*)cellData;
/**
Refresh the flair information added to the sender display name.
*/
- (void)renderSenderFlair;
/**
Highlight text message related to a specific event in the displayed message.
@param eventId the id of the event to highlight (use nil to cancel highlighting).
*/
- (void)highlightTextMessageForEvent:(NSString*)eventId;
/**
The top position of an event in the cell.
A cell can display several events. The method returns the vertical position of a given
event in the cell.
@return the y position (in pixel) of the event in the cell.
*/
- (CGFloat)topPositionOfEvent:(NSString*)eventId;
/**
The bottom position of an event in the cell.
A cell can display several events. The method returns the vertical position of the bottom part
of a given event in the cell.
@return the y position (in pixel) of the bottom part of the event in the cell.
*/
- (CGFloat)bottomPositionOfEvent:(NSString*)eventId;
/**
Restore `attachViewBottomConstraint` constant to default value.
*/
- (void)resetAttachmentViewBottomConstraintConstant;
/**
Redeclare heightForCellData:withMaximumWidth: method from MXKCellRendering to use it as a class method in Swift and not a static method.
*/
+ (CGFloat)heightForCellData:(MXKCellData*)cellData withMaximumWidth:(CGFloat)maxWidth;
/**
Setup outlets views. Useful to call when cell subclass does not use a xib otherwise this method is called automatically in `awakeFromNib`.
*/
- (void)setupViews;
@end
File diff suppressed because it is too large Load Diff
@@ -1,25 +0,0 @@
/*
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 "MXKRoomBubbleTableViewCell.h"
/**
`MXKRoomEmptyBubbleTableViewCell` displays an empty bubbles without user's information.
This kind of bubble may be used to localize an event without display in the room history.
*/
@interface MXKRoomEmptyBubbleTableViewCell : MXKRoomBubbleTableViewCell
@end
@@ -1,21 +0,0 @@
/*
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 "MXKRoomEmptyBubbleTableViewCell.h"
@implementation MXKRoomEmptyBubbleTableViewCell
@end
@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12120" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
<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"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="pad-g3-2YJ" customClass="MXKRoomEmptyBubbleTableViewCell">
<rect key="frame" x="0.0" y="0.0" width="600" height="8"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="pad-g3-2YJ" id="fCg-ju-gnG">
<rect key="frame" x="0.0" y="0.0" width="600" height="7"/>
<autoresizingMask key="autoresizingMask"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</tableViewCellContentView>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</tableViewCell>
</objects>
</document>
@@ -1,26 +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 "MXKTableViewCell.h"
#import "MXKCellRendering.h"
/**
`MXKRoomIOSBubbleTableViewCell` instances mimic bubbles in the stock iOS messages application.
*/
@interface MXKRoomIOSBubbleTableViewCell : MXKTableViewCell <MXKCellRendering>
@end
@@ -1,45 +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 "MXKRoomIOSBubbleTableViewCell.h"
#import "MXKRoomBubbleCellDataStoring.h"
@implementation MXKRoomIOSBubbleTableViewCell
- (void)render:(MXKCellData *)cellData
{
id<MXKRoomBubbleCellDataStoring> bubbleData = (id<MXKRoomBubbleCellDataStoring>)cellData;
if (bubbleData)
{
self.textLabel.attributedText = bubbleData.attributedTextMessage;
}
else
{
self.textLabel.text = @"";
}
// Light custo for now... @TODO
self.layer.cornerRadius = 20;
self.backgroundColor = [UIColor blueColor];
}
+ (CGFloat)heightForCellData:(MXKCellData *)cellData withMaximumWidth:(CGFloat)maxWidth
{
return 44;
}
@end
@@ -1,36 +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 "MXKRoomOutgoingBubbleTableViewCell.h"
/**
`MXKRoomIOSBubbleTableViewCell` instances mimic bubbles in the stock iOS messages application.
It is dedicated to outgoing messages.
It subclasses `MXKRoomOutgoingBubbleTableViewCell` to take benefit of the available mechanic.
*/
@interface MXKRoomIOSOutgoingBubbleTableViewCell : MXKRoomOutgoingBubbleTableViewCell
/**
The green bubble displayed in background.
*/
@property (weak, nonatomic) IBOutlet UIImageView *bubbleImageView;
/**
The width constraint on this backgroung green bubble.
*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bubbleImageViewWidthConstraint;
@end
@@ -1,130 +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 "MXKRoomIOSOutgoingBubbleTableViewCell.h"
#import "MXKRoomBubbleCellData.h"
#import "MXEvent+MatrixKit.h"
#import "MXKTools.h"
#import "NSBundle+MatrixKit.h"
#import "MXKImageView.h"
#define OUTGOING_BUBBLE_COLOR 0x00e34d
@implementation MXKRoomIOSOutgoingBubbleTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
// Create the strechable background bubble
self.bubbleImageView.image = self.class.bubbleImage;
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
}
- (void)render:(MXKCellData *)cellData
{
[super render:cellData];
// Reset values
self.bubbleImageView.hidden = NO;
// Customise the data precomputed by the legacy classes
// Replace black color in texts by the white color expected for outgoing messages.
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:self.messageTextView.attributedText];
// Change all attributes one by one
[attributedString enumerateAttributesInRange:NSMakeRange(0, attributedString.length) options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop)
{
// Replace only black colored texts
if (attrs[NSForegroundColorAttributeName] == self->bubbleData.eventFormatter.defaultTextColor)
{
// By white
NSMutableDictionary *newAttrs = [NSMutableDictionary dictionaryWithDictionary:attrs];
newAttrs[NSForegroundColorAttributeName] = [UIColor whiteColor];
[attributedString setAttributes:newAttrs range:range];
}
}];
self.messageTextView.attributedText = attributedString;
// Update the bubble width to include the text view
self.bubbleImageViewWidthConstraint.constant = bubbleData.contentSize.width + 17;
// Limit bubble width
if (self.bubbleImageViewWidthConstraint.constant < 46)
{
self.bubbleImageViewWidthConstraint.constant = 46;
}
// Mask the image with the bubble
if (bubbleData.attachment && bubbleData.attachment.type != MXKAttachmentTypeFile && bubbleData.attachment.type != MXKAttachmentTypeAudio)
{
self.bubbleImageView.hidden = YES;
UIImageView *rightBubbleImageView = [[UIImageView alloc] initWithImage:self.class.bubbleImage];
rightBubbleImageView.frame = CGRectMake(0, 0, self.bubbleImageViewWidthConstraint.constant, bubbleData.contentSize.height + self.attachViewTopConstraint.constant - 4);
self.attachmentView.layer.mask = rightBubbleImageView.layer;
}
}
+ (CGFloat)heightForCellData:(MXKCellData *)cellData withMaximumWidth:(CGFloat)maxWidth
{
CGFloat rowHeight = [super heightForCellData:cellData withMaximumWidth:maxWidth];
CGFloat height = self.cellWithOriginalXib.frame.size.height;
// Use the xib height as the minimal height
if (rowHeight < height)
{
rowHeight = height;
}
return rowHeight;
}
/**
Create the strechable background bubble.
@return the bubble image.
*/
+ (UIImage *)bubbleImage
{
UIImage *rightBubbleImage = [NSBundle mxk_imageFromMXKAssetsBundleWithName:@"bubble_ios_messages_right"];
rightBubbleImage = [MXKTools paintImage:rightBubbleImage
withColor:[MXKTools colorWithRGBValue:OUTGOING_BUBBLE_COLOR]];
UIEdgeInsets edgeInsets = UIEdgeInsetsMake(17, 22, 17, 27);
return [rightBubbleImage resizableImageWithCapInsets:edgeInsets];
}
@end
@@ -1,161 +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="Constraints to layout margins" minToolsVersion="6.0"/>
<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"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="pad-g3-2YJ" customClass="MXKRoomIOSOutgoingBubbleTableViewCell">
<rect key="frame" x="0.0" y="0.0" width="600" height="40"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="pad-g3-2YJ" id="fCg-ju-gnG">
<rect key="frame" x="0.0" y="0.0" width="600" height="39"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Ktd-ms-tAu" userLabel="Bubble Image View">
<rect key="frame" x="160" y="2" width="422" height="35"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="422" id="ptG-QV-Cix"/>
</constraints>
</imageView>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="text message" translatesAutoresizingMaskIntoConstraints="NO" id="tgO-Rv-C7R">
<rect key="frame" x="74" y="3" width="495" height="36"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<dataDetectorType key="dataDetectorTypes" link="YES"/>
</textView>
<view contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="SIW-l4-PfI" userLabel="Attachment View" customClass="MXKImageView">
<rect key="frame" x="160" y="2" width="422" height="35"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<constraints>
<constraint firstAttribute="width" constant="192" id="iGr-e7-bde"/>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="iGr-e7-bde"/>
</mask>
</variation>
</view>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ZC1-hT-7fs">
<rect key="frame" x="355" y="4" width="32" height="32"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="MOQ-Xi-18w"/>
<constraint firstAttribute="height" constant="32" id="pGM-pf-SiP"/>
</constraints>
</imageView>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="Mqf-7a-bsm">
<rect key="frame" x="361" y="10" width="20" height="20"/>
</activityIndicatorView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LVJ-Av-zVs" userLabel="showHideDateTime">
<rect key="frame" x="0.0" y="0.0" width="74" height="39"/>
<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="showHideDateTime:" destination="pad-g3-2YJ" eventType="touchUpInside" id="Ztw-z5-zlU"/>
</connections>
</button>
<view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="a51-cR-7FE" userLabel="bubbleInfoContainer">
<rect key="frame" x="8" y="10" width="66" height="29"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="apW-hM-veR" userLabel="ProgressView">
<rect key="frame" x="18" y="-16" width="100" height="71"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="text" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" minimumFontSize="4" preferredMaxLayoutWidth="100" translatesAutoresizingMaskIntoConstraints="NO" id="DYj-Mb-me4" userLabel="Progress stats">
<rect key="frame" x="0.0" y="61" width="100" height="10"/>
<fontDescription key="fontDescription" type="system" pointSize="8"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Oec-kb-Tsz" customClass="MXKPieChartView">
<rect key="frame" x="30" y="0.0" width="40" height="41"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="6tZ-ag-7TT"/>
<constraint firstAttribute="width" constant="40" id="duC-38-cLC"/>
</constraints>
</view>
</subviews>
<gestureRecognizers/>
<constraints>
<constraint firstItem="Oec-kb-Tsz" firstAttribute="top" secondItem="apW-hM-veR" secondAttribute="top" id="49H-ch-qkC"/>
<constraint firstAttribute="height" constant="70" id="GhG-zB-k3A"/>
<constraint firstAttribute="centerX" secondItem="Oec-kb-Tsz" secondAttribute="centerX" id="KZy-2p-KaW"/>
<constraint firstItem="DYj-Mb-me4" firstAttribute="leading" secondItem="apW-hM-veR" secondAttribute="leading" id="UxL-bV-5Ca"/>
<constraint firstAttribute="bottom" secondItem="DYj-Mb-me4" secondAttribute="bottom" id="YFp-gQ-NFz"/>
<constraint firstAttribute="width" constant="100" id="Zr4-5T-h9g"/>
<constraint firstAttribute="trailing" secondItem="DYj-Mb-me4" secondAttribute="trailing" id="ehN-ME-1U1"/>
<constraint firstAttribute="centerX" secondItem="DYj-Mb-me4" secondAttribute="centerX" id="nuq-lO-8pT"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="SIW-l4-PfI" firstAttribute="centerX" secondItem="Mqf-7a-bsm" secondAttribute="centerX" id="1b6-1l-hg0"/>
<constraint firstItem="tgO-Rv-C7R" firstAttribute="leading" secondItem="LVJ-Av-zVs" secondAttribute="trailing" id="45B-ph-Sc3"/>
<constraint firstAttribute="bottom" secondItem="tgO-Rv-C7R" secondAttribute="bottom" id="7C3-Tl-mMq"/>
<constraint firstItem="tgO-Rv-C7R" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" constant="3" id="8Sy-eu-tYs"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="width" secondItem="Ktd-ms-tAu" secondAttribute="width" id="AjE-jd-r7q"/>
<constraint firstAttribute="trailing" secondItem="SIW-l4-PfI" secondAttribute="trailing" constant="8" id="D8p-mV-ytb"/>
<constraint firstItem="a51-cR-7FE" firstAttribute="leading" secondItem="fCg-ju-gnG" secondAttribute="leading" constant="8" id="E3x-h8-GPF"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="bottom" secondItem="Ktd-ms-tAu" secondAttribute="bottom" id="ET2-rD-uVQ"/>
<constraint firstItem="LVJ-Av-zVs" firstAttribute="leading" secondItem="fCg-ju-gnG" secondAttribute="leading" id="Hve-E3-z5N"/>
<constraint firstAttribute="bottom" secondItem="LVJ-Av-zVs" secondAttribute="bottom" id="IKr-Dc-HKz"/>
<constraint firstAttribute="trailingMargin" secondItem="Ktd-ms-tAu" secondAttribute="trailing" constant="10" id="LEJ-mA-aF4"/>
<constraint firstItem="apW-hM-veR" firstAttribute="leading" secondItem="fCg-ju-gnG" secondAttribute="leading" constant="18" id="LFn-vp-m0v"/>
<constraint firstItem="tgO-Rv-C7R" firstAttribute="leading" secondItem="a51-cR-7FE" secondAttribute="trailing" id="PJ9-Px-DPH"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" constant="18" id="QDm-tP-KWa"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="top" secondItem="Ktd-ms-tAu" secondAttribute="top" id="W0E-eV-LGg"/>
<constraint firstAttribute="bottom" secondItem="SIW-l4-PfI" secondAttribute="bottom" id="fMN-Th-SOT"/>
<constraint firstItem="a51-cR-7FE" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" constant="10" id="fQv-07-Pgx"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="centerY" secondItem="ZC1-hT-7fs" secondAttribute="centerY" id="ffI-vh-SW3"/>
<constraint firstAttribute="bottom" secondItem="Ktd-ms-tAu" secondAttribute="bottom" constant="2" id="haQ-G8-eZD"/>
<constraint firstItem="tgO-Rv-C7R" firstAttribute="leading" secondItem="fCg-ju-gnG" secondAttribute="leading" constant="74" id="hwr-aa-TB4"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="centerX" secondItem="ZC1-hT-7fs" secondAttribute="centerX" id="jah-TA-P0P"/>
<constraint firstItem="Ktd-ms-tAu" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" constant="2" id="ltB-ic-svk"/>
<constraint firstItem="LVJ-Av-zVs" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" id="pYO-hi-P72"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="centerY" secondItem="Mqf-7a-bsm" secondAttribute="centerY" id="sKJ-ny-LjM"/>
<constraint firstAttribute="bottom" secondItem="a51-cR-7FE" secondAttribute="bottom" id="viZ-Sx-3RW"/>
<constraint firstItem="apW-hM-veR" firstAttribute="centerY" secondItem="SIW-l4-PfI" secondAttribute="centerY" id="wmh-x1-U32"/>
<constraint firstAttribute="trailing" secondItem="tgO-Rv-C7R" secondAttribute="trailing" constant="31" id="xYz-lp-c7K"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="leading" secondItem="Ktd-ms-tAu" secondAttribute="leading" id="zvi-c3-uqP"/>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="D8p-mV-ytb"/>
<exclude reference="QDm-tP-KWa"/>
<exclude reference="fMN-Th-SOT"/>
</mask>
</variation>
</tableViewCellContentView>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="activityIndicator" destination="Mqf-7a-bsm" id="toi-nL-eJa"/>
<outlet property="attachViewTopConstraint" destination="QDm-tP-KWa" id="Ku6-FC-jqo"/>
<outlet property="attachViewWidthConstraint" destination="iGr-e7-bde" id="uD4-aU-1Ru"/>
<outlet property="attachmentView" destination="SIW-l4-PfI" id="L4H-ub-pPI"/>
<outlet property="bubbleImageView" destination="Ktd-ms-tAu" id="I0r-5F-rhe"/>
<outlet property="bubbleImageViewWidthConstraint" destination="ptG-QV-Cix" id="TUH-dw-uqF"/>
<outlet property="bubbleInfoContainer" destination="a51-cR-7FE" id="wrR-cU-DVm"/>
<outlet property="bubbleInfoContainerTopConstraint" destination="fQv-07-Pgx" id="82c-KH-Wop"/>
<outlet property="messageTextView" destination="tgO-Rv-C7R" id="LZ5-hQ-AbQ"/>
<outlet property="msgTextViewBottomConstraint" destination="7C3-Tl-mMq" id="5Ff-xL-G1i"/>
<outlet property="msgTextViewLeadingConstraint" destination="hwr-aa-TB4" id="A1E-ap-Ug6"/>
<outlet property="msgTextViewTopConstraint" destination="8Sy-eu-tYs" id="7yx-oJ-KBP"/>
<outlet property="msgTextViewTrailingConstraint" destination="xYz-lp-c7K" id="hc2-Cg-is4"/>
<outlet property="playIconView" destination="ZC1-hT-7fs" id="zeH-8B-gxw"/>
<outlet property="progressChartView" destination="Oec-kb-Tsz" id="uoq-6v-6Tm"/>
<outlet property="progressView" destination="apW-hM-veR" id="Iy8-ca-hMi"/>
<outlet property="statsLabel" destination="DYj-Mb-me4" id="CbG-5V-unO"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -1,24 +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 "MXKRoomIncomingBubbleTableViewCell.h"
/**
`MXKRoomIncomingAttachmentBubbleCell` displays incoming attachment bubbles with sender's information.
*/
@interface MXKRoomIncomingAttachmentBubbleCell : MXKRoomIncomingBubbleTableViewCell
@end
@@ -1,21 +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 "MXKRoomIncomingAttachmentBubbleCell.h"
@implementation MXKRoomIncomingAttachmentBubbleCell
@end
@@ -1,171 +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"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="WmY-Jw-mqv" customClass="MXKRoomIncomingAttachmentBubbleCell">
<rect key="frame" x="0.0" y="0.0" width="600" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="WmY-Jw-mqv" id="ef1-Tq-U3Z">
<rect key="frame" x="0.0" y="0.0" width="600" height="49"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view clipsSubviews="YES" contentMode="scaleAspectFill" translatesAutoresizingMaskIntoConstraints="NO" id="hgp-Z5-rAj" userLabel="Picture View" customClass="MXKImageView">
<rect key="frame" x="8" y="5" width="40" height="40"/>
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="NQk-ck-Lo8"/>
<constraint firstAttribute="height" constant="40" id="dNT-QU-CUG"/>
</constraints>
</view>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="zwq-eh-8Fb" userLabel="typingBadge">
<rect key="frame" x="5" y="0.0" width="20" height="20"/>
<constraints>
<constraint firstAttribute="width" constant="20" id="7ni-rb-ovL"/>
<constraint firstAttribute="height" constant="20" id="mcu-rQ-hnj"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="User name:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="q9c-0p-QyP">
<rect key="frame" x="51" y="3" width="480" height="20"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="20" placeholder="YES" id="5ZO-W1-tS2"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<color key="textColor" red="0.33333333333333331" green="0.33333333333333331" blue="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="5IE-JS-uf3" userLabel="Attachment View" customClass="MXKImageView">
<rect key="frame" x="51" y="18" width="192" height="23"/>
<color key="backgroundColor" red="0.93725490196078431" green="0.93725490196078431" blue="0.95686274509803926" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="192" id="9zO-jU-qTb"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="23" id="C5F-6D-LZx"/>
</constraints>
</view>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="K9X-gn-noF" userLabel="File Type Image View">
<rect key="frame" x="51" y="18" width="32" height="32"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="OE8-oh-B7Q"/>
<constraint firstAttribute="height" constant="32" id="jJB-zj-fbT"/>
</constraints>
</imageView>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Cot-3X-2cU" userLabel="Play Icon Image View">
<rect key="frame" x="131" y="14" width="32" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="32" id="8io-Wk-GzF"/>
<constraint firstAttribute="width" constant="32" id="aeJ-j3-rfX"/>
</constraints>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kwd-hP-feC" userLabel="showHideDateTime">
<rect key="frame" x="531" y="0.0" width="69" height="49"/>
<constraints>
<constraint firstAttribute="width" constant="69" id="9vA-4g-EE5"/>
</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="showHideDateTime:" destination="WmY-Jw-mqv" eventType="touchUpInside" id="jYV-nj-p60"/>
</connections>
</button>
<view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IOg-Kt-8vW" userLabel="bubbleInfoContainer">
<rect key="frame" x="531" y="18" width="61" height="31"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="61" id="tLr-6k-ArA"/>
</constraints>
</view>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fdx-qs-8en" userLabel="ProgressView">
<rect key="frame" x="487" y="-5" width="100" height="70"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="rate" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" minimumFontSize="4" preferredMaxLayoutWidth="100" translatesAutoresizingMaskIntoConstraints="NO" id="eU5-iK-u8i" userLabel="Progress stats">
<rect key="frame" x="0.0" y="60" width="100" height="10"/>
<fontDescription key="fontDescription" type="system" pointSize="8"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hJj-TC-pxK" customClass="MXKPieChartView">
<rect key="frame" x="30" y="0.0" width="40" height="40"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="Cpt-s4-tlK"/>
<constraint firstAttribute="height" constant="40" id="Jb4-9E-tG0"/>
</constraints>
</view>
</subviews>
<gestureRecognizers/>
<constraints>
<constraint firstAttribute="height" constant="70" id="5w2-Hm-hZx"/>
<constraint firstAttribute="centerX" secondItem="eU5-iK-u8i" secondAttribute="centerX" id="APi-aE-mLc"/>
<constraint firstItem="eU5-iK-u8i" firstAttribute="leading" secondItem="fdx-qs-8en" secondAttribute="leading" id="Njw-3a-E9Y"/>
<constraint firstAttribute="bottom" secondItem="eU5-iK-u8i" secondAttribute="bottom" id="QMO-g9-QVE"/>
<constraint firstAttribute="centerX" secondItem="hJj-TC-pxK" secondAttribute="centerX" id="laR-Vg-ol3"/>
<constraint firstItem="hJj-TC-pxK" firstAttribute="top" secondItem="fdx-qs-8en" secondAttribute="top" id="ovD-8p-4dP"/>
<constraint firstAttribute="width" constant="100" id="ryE-fW-SgG"/>
<constraint firstAttribute="trailing" secondItem="eU5-iK-u8i" secondAttribute="trailing" id="teG-8q-BOX"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GGt-zb-6zg">
<rect key="frame" x="48" y="0.0" width="486" height="26"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="hgp-Z5-rAj" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" constant="5" id="2Ih-ga-N9s"/>
<constraint firstItem="kwd-hP-feC" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" id="5bB-HV-WAA"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="leading" secondItem="hgp-Z5-rAj" secondAttribute="trailing" constant="3" id="6mM-Ag-m0K"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" constant="18" id="96U-67-5TP"/>
<constraint firstAttribute="trailing" secondItem="q9c-0p-QyP" secondAttribute="trailing" constant="69" id="Bkh-h2-JOQ"/>
<constraint firstItem="GGt-zb-6zg" firstAttribute="leading" secondItem="q9c-0p-QyP" secondAttribute="leading" constant="-3" id="CsE-Bf-Wq7"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="centerY" secondItem="Cot-3X-2cU" secondAttribute="centerY" id="H5t-l6-fL1"/>
<constraint firstItem="q9c-0p-QyP" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" constant="3" id="Ixr-7h-f8j"/>
<constraint firstItem="GGt-zb-6zg" firstAttribute="trailing" secondItem="q9c-0p-QyP" secondAttribute="trailing" constant="3" id="Ku9-ad-hb4"/>
<constraint firstAttribute="bottom" secondItem="kwd-hP-feC" secondAttribute="bottom" id="LQg-cI-Nkn"/>
<constraint firstAttribute="bottom" secondItem="5IE-JS-uf3" secondAttribute="bottom" constant="8" id="SHN-tC-zsJ"/>
<constraint firstAttribute="bottom" secondItem="IOg-Kt-8vW" secondAttribute="bottom" id="TPw-iE-nii"/>
<constraint firstItem="IOg-Kt-8vW" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" constant="18" id="XSL-TG-m62"/>
<constraint firstItem="q9c-0p-QyP" firstAttribute="leading" secondItem="hgp-Z5-rAj" secondAttribute="trailing" constant="3" id="YWK-C2-15w"/>
<constraint firstItem="GGt-zb-6zg" firstAttribute="top" secondItem="q9c-0p-QyP" secondAttribute="top" constant="-3" id="cqf-j2-9vP"/>
<constraint firstItem="zwq-eh-8Fb" firstAttribute="leading" secondItem="ef1-Tq-U3Z" secondAttribute="leading" constant="5" id="dgK-4e-UuE"/>
<constraint firstAttribute="trailing" secondItem="kwd-hP-feC" secondAttribute="trailing" id="f2V-Ka-kU3"/>
<constraint firstAttribute="trailing" secondItem="IOg-Kt-8vW" secondAttribute="trailing" constant="8" id="hQV-lO-7aQ"/>
<constraint firstItem="GGt-zb-6zg" firstAttribute="bottom" secondItem="q9c-0p-QyP" secondAttribute="bottom" constant="3" id="kYd-0y-Rcu"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="leading" secondItem="K9X-gn-noF" secondAttribute="leading" id="p93-5h-lvW"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="centerX" secondItem="Cot-3X-2cU" secondAttribute="centerX" id="sF7-QL-vdj"/>
<constraint firstItem="zwq-eh-8Fb" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" id="tUd-UR-lzA"/>
<constraint firstItem="hgp-Z5-rAj" firstAttribute="leading" secondItem="ef1-Tq-U3Z" secondAttribute="leading" constant="8" id="tuw-aU-ncu"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="centerY" secondItem="fdx-qs-8en" secondAttribute="centerY" id="v0F-Ts-14P"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="top" secondItem="K9X-gn-noF" secondAttribute="top" id="wkX-zQ-iQS"/>
<constraint firstAttribute="trailing" secondItem="fdx-qs-8en" secondAttribute="trailing" constant="13" id="xKk-Gz-moE"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="attachViewBottomConstraint" destination="SHN-tC-zsJ" id="cG0-a7-eHa"/>
<outlet property="attachViewMinHeightConstraint" destination="C5F-6D-LZx" id="frk-Ox-WbA"/>
<outlet property="attachViewTopConstraint" destination="96U-67-5TP" id="Ugm-cH-32E"/>
<outlet property="attachViewWidthConstraint" destination="9zO-jU-qTb" id="fOO-VW-fe1"/>
<outlet property="attachmentView" destination="5IE-JS-uf3" id="imT-1z-hR1"/>
<outlet property="bubbleInfoContainer" destination="IOg-Kt-8vW" id="TAw-QY-Y9e"/>
<outlet property="bubbleInfoContainerTopConstraint" destination="XSL-TG-m62" id="qVf-vJ-4aP"/>
<outlet property="fileTypeIconView" destination="K9X-gn-noF" id="4Pj-bc-3gk"/>
<outlet property="pictureView" destination="hgp-Z5-rAj" id="rKM-QG-RJN"/>
<outlet property="playIconView" destination="Cot-3X-2cU" id="KEF-KK-Og1"/>
<outlet property="progressChartView" destination="hJj-TC-pxK" id="Zz3-s5-Qqr"/>
<outlet property="progressView" destination="fdx-qs-8en" id="V7E-pn-Xze"/>
<outlet property="statsLabel" destination="eU5-iK-u8i" id="MSm-kU-RSY"/>
<outlet property="typingBadge" destination="zwq-eh-8Fb" id="4vs-Dk-vWt"/>
<outlet property="userNameLabel" destination="q9c-0p-QyP" id="JId-R7-LoM"/>
<outlet property="userNameTapGestureMaskView" destination="GGt-zb-6zg" id="uV1-Jm-vLT"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -1,24 +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 "MXKRoomIncomingAttachmentBubbleCell.h"
/**
`MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell` displays incoming message bubbles without sender's information.
*/
@interface MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell : MXKRoomIncomingAttachmentBubbleCell
@end
@@ -1,21 +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 "MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell.h"
@implementation MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell
@end
@@ -1,127 +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"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="WmY-Jw-mqv" customClass="MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell">
<rect key="frame" x="0.0" y="0.0" width="600" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="WmY-Jw-mqv" id="ef1-Tq-U3Z">
<rect key="frame" x="0.0" y="0.0" width="600" height="49"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="5IE-JS-uf3" userLabel="Attachment View" customClass="MXKImageView">
<rect key="frame" x="51" y="8" width="192" height="33"/>
<color key="backgroundColor" red="0.93725490196078431" green="0.93725490196078431" blue="0.95686274509803926" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="192" id="9zO-jU-qTb"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="33" id="Uqr-7d-0dv"/>
</constraints>
</view>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="K9X-gn-noF" userLabel="File Type Image View">
<rect key="frame" x="51" y="8" width="32" height="32"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="OE8-oh-B7Q"/>
<constraint firstAttribute="height" constant="32" id="jJB-zj-fbT"/>
</constraints>
</imageView>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Cot-3X-2cU" userLabel="Play Icon Image View">
<rect key="frame" x="131" y="9" width="32" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="32" id="8io-Wk-GzF"/>
<constraint firstAttribute="width" constant="32" id="aeJ-j3-rfX"/>
</constraints>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kwd-hP-feC" userLabel="showHideDateTime">
<rect key="frame" x="531" y="0.0" width="69" height="49"/>
<constraints>
<constraint firstAttribute="width" constant="69" id="9vA-4g-EE5"/>
</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="showHideDateTime:" destination="WmY-Jw-mqv" eventType="touchUpInside" id="jYV-nj-p60"/>
</connections>
</button>
<view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IOg-Kt-8vW" userLabel="bubbleInfoContainer">
<rect key="frame" x="531" y="8" width="61" height="41"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="61" id="tLr-6k-ArA"/>
</constraints>
</view>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fdx-qs-8en" userLabel="ProgressView">
<rect key="frame" x="487" y="-10" width="100" height="70"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="rate" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" minimumFontSize="4" preferredMaxLayoutWidth="100" translatesAutoresizingMaskIntoConstraints="NO" id="eU5-iK-u8i" userLabel="Progress stats">
<rect key="frame" x="0.0" y="60" width="100" height="10"/>
<fontDescription key="fontDescription" type="system" pointSize="8"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hJj-TC-pxK" customClass="MXKPieChartView">
<rect key="frame" x="30" y="0.0" width="40" height="40"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="Cpt-s4-tlK"/>
<constraint firstAttribute="height" constant="40" id="Jb4-9E-tG0"/>
</constraints>
</view>
</subviews>
<gestureRecognizers/>
<constraints>
<constraint firstAttribute="height" constant="70" id="5w2-Hm-hZx"/>
<constraint firstAttribute="centerX" secondItem="eU5-iK-u8i" secondAttribute="centerX" id="APi-aE-mLc"/>
<constraint firstItem="eU5-iK-u8i" firstAttribute="leading" secondItem="fdx-qs-8en" secondAttribute="leading" id="Njw-3a-E9Y"/>
<constraint firstAttribute="bottom" secondItem="eU5-iK-u8i" secondAttribute="bottom" id="QMO-g9-QVE"/>
<constraint firstAttribute="centerX" secondItem="hJj-TC-pxK" secondAttribute="centerX" id="laR-Vg-ol3"/>
<constraint firstItem="hJj-TC-pxK" firstAttribute="top" secondItem="fdx-qs-8en" secondAttribute="top" id="ovD-8p-4dP"/>
<constraint firstAttribute="width" constant="100" id="ryE-fW-SgG"/>
<constraint firstAttribute="trailing" secondItem="eU5-iK-u8i" secondAttribute="trailing" id="teG-8q-BOX"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="kwd-hP-feC" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" id="5bB-HV-WAA"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" constant="8" id="96U-67-5TP"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="centerY" secondItem="Cot-3X-2cU" secondAttribute="centerY" id="H5t-l6-fL1"/>
<constraint firstAttribute="bottom" secondItem="kwd-hP-feC" secondAttribute="bottom" id="LQg-cI-Nkn"/>
<constraint firstAttribute="bottom" secondItem="5IE-JS-uf3" secondAttribute="bottom" constant="8" id="SHN-tC-zsJ"/>
<constraint firstAttribute="bottom" secondItem="IOg-Kt-8vW" secondAttribute="bottom" id="TPw-iE-nii"/>
<constraint firstItem="IOg-Kt-8vW" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" constant="8" id="XSL-TG-m62"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="leading" secondItem="ef1-Tq-U3Z" secondAttribute="leading" constant="51" id="bSL-lG-ued"/>
<constraint firstAttribute="trailing" secondItem="kwd-hP-feC" secondAttribute="trailing" id="f2V-Ka-kU3"/>
<constraint firstAttribute="trailing" secondItem="IOg-Kt-8vW" secondAttribute="trailing" constant="8" id="hQV-lO-7aQ"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="leading" secondItem="K9X-gn-noF" secondAttribute="leading" id="p93-5h-lvW"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="centerX" secondItem="Cot-3X-2cU" secondAttribute="centerX" id="sF7-QL-vdj"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="centerY" secondItem="fdx-qs-8en" secondAttribute="centerY" id="v0F-Ts-14P"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="top" secondItem="K9X-gn-noF" secondAttribute="top" id="wkX-zQ-iQS"/>
<constraint firstAttribute="trailing" secondItem="fdx-qs-8en" secondAttribute="trailing" constant="13" id="xKk-Gz-moE"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="attachViewBottomConstraint" destination="SHN-tC-zsJ" id="cG0-a7-eHa"/>
<outlet property="attachViewMinHeightConstraint" destination="Uqr-7d-0dv" id="UIs-4K-np5"/>
<outlet property="attachViewTopConstraint" destination="96U-67-5TP" id="Ugm-cH-32E"/>
<outlet property="attachViewWidthConstraint" destination="9zO-jU-qTb" id="fOO-VW-fe1"/>
<outlet property="attachmentView" destination="5IE-JS-uf3" id="imT-1z-hR1"/>
<outlet property="bubbleInfoContainer" destination="IOg-Kt-8vW" id="TAw-QY-Y9e"/>
<outlet property="bubbleInfoContainerTopConstraint" destination="XSL-TG-m62" id="qVf-vJ-4aP"/>
<outlet property="fileTypeIconView" destination="K9X-gn-noF" id="4Pj-bc-3gk"/>
<outlet property="playIconView" destination="Cot-3X-2cU" id="KEF-KK-Og1"/>
<outlet property="progressChartView" destination="hJj-TC-pxK" id="Zz3-s5-Qqr"/>
<outlet property="progressView" destination="fdx-qs-8en" id="V7E-pn-Xze"/>
<outlet property="statsLabel" destination="eU5-iK-u8i" id="MSm-kU-RSY"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -1,29 +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 "MXKRoomBubbleTableViewCell.h"
/**
`MXKRoomIncomingBubbleTableViewCell` inherits from 'MXKRoomBubbleTableViewCell' class in order to handle specific
options related to incoming messages (like typing badge).
In order to optimize bubbles rendering, we advise to define a .xib for each layout.
*/
@interface MXKRoomIncomingBubbleTableViewCell : MXKRoomBubbleTableViewCell
@property (weak, nonatomic) IBOutlet UIImageView *typingBadge;
@end
@@ -1,65 +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 "MXKRoomIncomingBubbleTableViewCell.h"
#import "MXKRoomBubbleCellData.h"
#import "NSBundle+MatrixKit.h"
@implementation MXKRoomIncomingBubbleTableViewCell
- (void)finalizeInit
{
[super finalizeInit];
self.readReceiptsAlignment = ReadReceiptAlignmentRight;
}
- (void)awakeFromNib
{
[super awakeFromNib];
self.typingBadge.image = [NSBundle mxk_imageFromMXKAssetsBundleWithName:@"icon_keyboard"];
}
- (void)render:(MXKCellData *)cellData
{
[super render:cellData];
if (bubbleData)
{
// Handle here typing badge (if any)
if (self.typingBadge)
{
if (bubbleData.isTyping)
{
self.typingBadge.hidden = NO;
[self.typingBadge.superview bringSubviewToFront:self.typingBadge];
}
else
{
self.typingBadge.hidden = YES;
}
}
}
}
- (void)didEndDisplay
{
[super didEndDisplay];
self.readReceiptsAlignment = ReadReceiptAlignmentRight;
}
@end
@@ -1,24 +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 "MXKRoomIncomingBubbleTableViewCell.h"
/**
`MXKRoomIncomingTextMsgBubbleCell` displays incoming message bubbles with sender's information.
*/
@interface MXKRoomIncomingTextMsgBubbleCell : MXKRoomIncomingBubbleTableViewCell
@end
@@ -1,21 +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 "MXKRoomIncomingTextMsgBubbleCell.h"
@implementation MXKRoomIncomingTextMsgBubbleCell
@end
@@ -1,124 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
<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"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="WmY-Jw-mqv" customClass="MXKRoomIncomingTextMsgBubbleCell">
<rect key="frame" x="0.0" y="0.0" width="600" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="WmY-Jw-mqv" id="ef1-Tq-U3Z">
<rect key="frame" x="0.0" y="0.0" width="600" height="49.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view clipsSubviews="YES" contentMode="scaleAspectFill" translatesAutoresizingMaskIntoConstraints="NO" id="hgp-Z5-rAj" userLabel="Picture View" customClass="MXKImageView">
<rect key="frame" x="8" y="5" width="40" height="40"/>
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="NQk-ck-Lo8"/>
<constraint firstAttribute="height" constant="40" id="dNT-QU-CUG"/>
</constraints>
</view>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="zwq-eh-8Fb" userLabel="typingBadge">
<rect key="frame" x="5" y="0.0" width="20" height="20"/>
<constraints>
<constraint firstAttribute="width" constant="20" id="7ni-rb-ovL"/>
<constraint firstAttribute="height" constant="20" id="mcu-rQ-hnj"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="User name:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="q9c-0p-QyP">
<rect key="frame" x="51" y="3" width="480" height="20"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="20" placeholder="YES" id="5ZO-W1-tS2"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<color key="textColor" red="0.33333333333333331" green="0.33333333333333331" blue="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="text message" translatesAutoresizingMaskIntoConstraints="NO" id="HTH-5n-MSU" customClass="MXKMessageTextView">
<rect key="frame" x="51" y="10" width="97" height="39.5"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="97" id="9EQ-AP-La0"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="39" id="ZZt-rc-tVJ"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<dataDetectorType key="dataDetectorTypes" link="YES"/>
</textView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kwd-hP-feC" userLabel="showHideDateTime">
<rect key="frame" x="531" y="0.0" width="69" height="49.5"/>
<constraints>
<constraint firstAttribute="width" constant="69" id="9vA-4g-EE5"/>
</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="showHideDateTime:" destination="WmY-Jw-mqv" eventType="touchUpInside" id="jYV-nj-p60"/>
</connections>
</button>
<view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IOg-Kt-8vW" userLabel="bubbleInfoContainer">
<rect key="frame" x="531" y="10" width="61" height="39.5"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="61" id="tLr-6k-ArA"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="L3N-gy-H1n">
<rect key="frame" x="48" y="0.0" width="486" height="26"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="hgp-Z5-rAj" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" constant="5" id="2Ih-ga-N9s"/>
<constraint firstItem="kwd-hP-feC" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" id="5bB-HV-WAA"/>
<constraint firstItem="L3N-gy-H1n" firstAttribute="leading" secondItem="q9c-0p-QyP" secondAttribute="leading" constant="-3" id="7gc-A2-DNj"/>
<constraint firstAttribute="trailing" secondItem="q9c-0p-QyP" secondAttribute="trailing" constant="69" id="Bkh-h2-JOQ"/>
<constraint firstItem="L3N-gy-H1n" firstAttribute="trailing" secondItem="q9c-0p-QyP" secondAttribute="trailing" constant="3" id="FLX-Dd-CqO"/>
<constraint firstItem="L3N-gy-H1n" firstAttribute="bottom" secondItem="q9c-0p-QyP" secondAttribute="bottom" constant="3" id="IpR-cp-Yod"/>
<constraint firstItem="q9c-0p-QyP" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" constant="3" id="Ixr-7h-f8j"/>
<constraint firstAttribute="bottom" secondItem="kwd-hP-feC" secondAttribute="bottom" id="LQg-cI-Nkn"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="HTH-5n-MSU" secondAttribute="trailing" constant="69" id="Shz-6S-kGd"/>
<constraint firstAttribute="bottom" secondItem="IOg-Kt-8vW" secondAttribute="bottom" id="TPw-iE-nii"/>
<constraint firstItem="IOg-Kt-8vW" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" constant="10" id="XSL-TG-m62"/>
<constraint firstItem="q9c-0p-QyP" firstAttribute="leading" secondItem="hgp-Z5-rAj" secondAttribute="trailing" constant="3" id="YWK-C2-15w"/>
<constraint firstItem="zwq-eh-8Fb" firstAttribute="leading" secondItem="ef1-Tq-U3Z" secondAttribute="leading" constant="5" id="dgK-4e-UuE"/>
<constraint firstAttribute="trailing" secondItem="kwd-hP-feC" secondAttribute="trailing" id="f2V-Ka-kU3"/>
<constraint firstAttribute="trailing" secondItem="IOg-Kt-8vW" secondAttribute="trailing" constant="8" id="hQV-lO-7aQ"/>
<constraint firstItem="HTH-5n-MSU" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" constant="10" id="mkw-3s-H8B"/>
<constraint firstAttribute="bottom" secondItem="HTH-5n-MSU" secondAttribute="bottom" id="oTk-3F-SEC"/>
<constraint firstItem="zwq-eh-8Fb" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" id="tUd-UR-lzA"/>
<constraint firstItem="L3N-gy-H1n" firstAttribute="top" secondItem="q9c-0p-QyP" secondAttribute="top" constant="-3" id="tXi-p0-wft"/>
<constraint firstItem="hgp-Z5-rAj" firstAttribute="leading" secondItem="ef1-Tq-U3Z" secondAttribute="leading" constant="8" id="tuw-aU-ncu"/>
<constraint firstItem="HTH-5n-MSU" firstAttribute="leading" secondItem="ef1-Tq-U3Z" secondAttribute="leading" constant="51" id="uig-Xh-7m6"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="bubbleInfoContainer" destination="IOg-Kt-8vW" id="TAw-QY-Y9e"/>
<outlet property="bubbleInfoContainerTopConstraint" destination="XSL-TG-m62" id="qVf-vJ-4aP"/>
<outlet property="messageTextView" destination="HTH-5n-MSU" id="YN4-iK-gNc"/>
<outlet property="msgTextViewBottomConstraint" destination="oTk-3F-SEC" id="o0p-3S-hM4"/>
<outlet property="msgTextViewLeadingConstraint" destination="uig-Xh-7m6" id="kgj-3v-ECW"/>
<outlet property="msgTextViewMinHeightConstraint" destination="ZZt-rc-tVJ" id="hDD-TL-PFM"/>
<outlet property="msgTextViewTopConstraint" destination="mkw-3s-H8B" id="lON-oG-Xx9"/>
<outlet property="msgTextViewTrailingConstraint" destination="Shz-6S-kGd" id="5ib-m6-Lna"/>
<outlet property="msgTextViewWidthConstraint" destination="9EQ-AP-La0" id="wgi-Yc-sTT"/>
<outlet property="pictureView" destination="hgp-Z5-rAj" id="rKM-QG-RJN"/>
<outlet property="typingBadge" destination="zwq-eh-8Fb" id="4vs-Dk-vWt"/>
<outlet property="userNameLabel" destination="q9c-0p-QyP" id="JId-R7-LoM"/>
<outlet property="userNameTapGestureMaskView" destination="L3N-gy-H1n" id="1Eb-Vn-fSP"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -1,24 +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 "MXKRoomIncomingTextMsgBubbleCell.h"
/**
`MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell` displays incoming message bubbles without sender's information.
*/
@interface MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell : MXKRoomIncomingTextMsgBubbleCell
@end
@@ -1,21 +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 "MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell.h"
@implementation MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell
@end
@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
<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"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="WmY-Jw-mqv" customClass="MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell">
<rect key="frame" x="0.0" y="0.0" width="600" height="35"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="WmY-Jw-mqv" id="ef1-Tq-U3Z">
<rect key="frame" x="0.0" y="0.0" width="600" height="34.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="text message" translatesAutoresizingMaskIntoConstraints="NO" id="HTH-5n-MSU" customClass="MXKMessageTextView">
<rect key="frame" x="51" y="0.0" width="97" height="34.5"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="29" id="SHB-dF-A5J"/>
<constraint firstAttribute="width" constant="97" id="aQ3-Pg-LVD"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<dataDetectorType key="dataDetectorTypes" link="YES"/>
</textView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kwd-hP-feC" userLabel="showHideDateTime">
<rect key="frame" x="531" y="0.0" width="69" height="34.5"/>
<constraints>
<constraint firstAttribute="width" constant="69" id="9vA-4g-EE5"/>
</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="showHideDateTime:" destination="WmY-Jw-mqv" eventType="touchUpInside" id="jYV-nj-p60"/>
</connections>
</button>
<view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IOg-Kt-8vW" userLabel="bubbleInfoContainer">
<rect key="frame" x="531" y="0.0" width="61" height="34.5"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="61" id="tLr-6k-ArA"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="kwd-hP-feC" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" id="5bB-HV-WAA"/>
<constraint firstAttribute="bottom" secondItem="kwd-hP-feC" secondAttribute="bottom" id="LQg-cI-Nkn"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="HTH-5n-MSU" secondAttribute="trailing" constant="69" id="Shz-6S-kGd"/>
<constraint firstAttribute="bottom" secondItem="IOg-Kt-8vW" secondAttribute="bottom" id="TPw-iE-nii"/>
<constraint firstItem="IOg-Kt-8vW" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" id="XSL-TG-m62"/>
<constraint firstAttribute="trailing" secondItem="kwd-hP-feC" secondAttribute="trailing" id="f2V-Ka-kU3"/>
<constraint firstAttribute="trailing" secondItem="IOg-Kt-8vW" secondAttribute="trailing" constant="8" id="hQV-lO-7aQ"/>
<constraint firstItem="HTH-5n-MSU" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" id="mkw-3s-H8B"/>
<constraint firstAttribute="bottom" secondItem="HTH-5n-MSU" secondAttribute="bottom" id="oTk-3F-SEC"/>
<constraint firstItem="HTH-5n-MSU" firstAttribute="leading" secondItem="ef1-Tq-U3Z" secondAttribute="leading" constant="51" id="uig-Xh-7m6"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="bubbleInfoContainer" destination="IOg-Kt-8vW" id="TAw-QY-Y9e"/>
<outlet property="bubbleInfoContainerTopConstraint" destination="XSL-TG-m62" id="qVf-vJ-4aP"/>
<outlet property="messageTextView" destination="HTH-5n-MSU" id="YN4-iK-gNc"/>
<outlet property="msgTextViewBottomConstraint" destination="oTk-3F-SEC" id="86P-vn-8mz"/>
<outlet property="msgTextViewLeadingConstraint" destination="uig-Xh-7m6" id="kgj-3v-ECW"/>
<outlet property="msgTextViewMinHeightConstraint" destination="SHB-dF-A5J" id="RjV-W7-QaK"/>
<outlet property="msgTextViewTopConstraint" destination="mkw-3s-H8B" id="lON-oG-Xx9"/>
<outlet property="msgTextViewTrailingConstraint" destination="Shz-6S-kGd" id="5ib-m6-Lna"/>
<outlet property="msgTextViewWidthConstraint" destination="aQ3-Pg-LVD" id="JPV-up-dzy"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -1,26 +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 "MXKRoomOutgoingBubbleTableViewCell.h"
/**
`MXKRoomOutgoingAttachmentBubbleCell` displays outgoing attachment bubbles.
*/
@interface MXKRoomOutgoingAttachmentBubbleCell : MXKRoomOutgoingBubbleTableViewCell
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
@end
@@ -1,144 +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 "MXKRoomOutgoingAttachmentBubbleCell.h"
#import "MXEvent+MatrixKit.h"
#import "MXKRoomBubbleCellData.h"
#import "MXKImageView.h"
#import "MXKPieChartView.h"
@implementation MXKRoomOutgoingAttachmentBubbleCell
- (void)dealloc
{
[self stopAnimating];
}
- (void)render:(MXKCellData *)cellData
{
[super render:cellData];
if (bubbleData)
{
// Do not display activity indicator on outgoing attachments (These attachments are supposed to be stored locally)
// Some download may append to retrieve the actual thumbnail after posting an image.
self.attachmentView.hideActivityIndicator = YES;
// Check if the attachment is uploading
MXKRoomBubbleComponent *component = bubbleData.bubbleComponents.firstObject;
if (component.event.sentState == MXEventSentStatePreparing || component.event.sentState == MXEventSentStateEncrypting || component.event.sentState == MXEventSentStateUploading)
{
// Retrieve the uploadId embedded in the fake url
bubbleData.uploadId = component.event.content[@"url"];
self.attachmentView.alpha = 0.5;
// Start showing upload progress
[self startUploadAnimating];
}
else if (component.event.sentState == MXEventSentStateSending)
{
self.attachmentView.alpha = 0.5;
[self.activityIndicator startAnimating];
}
else if (component.event.sentState == MXEventSentStateFailed)
{
self.attachmentView.alpha = 0.5;
[self.activityIndicator stopAnimating];
}
else
{
self.attachmentView.alpha = 1;
[self.activityIndicator stopAnimating];
}
}
}
- (void)didEndDisplay
{
[super didEndDisplay];
// Hide potential loading wheel
[self stopAnimating];
}
-(void)startUploadAnimating
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:nil];
[self.activityIndicator startAnimating];
MXMediaLoader *uploader = [MXMediaManager existingUploaderWithId:bubbleData.uploadId];
if (uploader)
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMediaLoaderStateDidChange:) name:kMXMediaLoaderStateDidChangeNotification object:uploader];
if (uploader.statisticsDict)
{
[self.activityIndicator stopAnimating];
[self updateProgressUI:uploader.statisticsDict];
// Check whether the upload is ended
if (self.progressChartView.progress == 1.0)
{
[self stopAnimating];
}
}
}
else
{
self.progressView.hidden = YES;
}
}
-(void)stopAnimating
{
self.progressView.hidden = YES;
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:nil];
[self.activityIndicator stopAnimating];
}
- (void)onMediaLoaderStateDidChange:(NSNotification *)notif
{
MXMediaLoader *loader = (MXMediaLoader*)notif.object;
// Consider only the progress of the current upload.
if ([loader.uploadId isEqualToString:bubbleData.uploadId])
{
switch (loader.state) {
case MXMediaLoaderStateUploadInProgress:
{
[self.activityIndicator stopAnimating];
[self updateProgressUI:loader.statisticsDict];
// the upload is ended
if (self.progressChartView.progress == 1.0)
{
[self stopAnimating];
}
break;
}
default:
break;
}
}
}
@end
@@ -1,144 +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"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="pad-g3-2YJ" customClass="MXKRoomOutgoingAttachmentBubbleCell">
<rect key="frame" x="0.0" y="0.0" width="600" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="pad-g3-2YJ" id="fCg-ju-gnG">
<rect key="frame" x="0.0" y="0.0" width="600" height="49"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view clipsSubviews="YES" contentMode="scaleAspectFill" translatesAutoresizingMaskIntoConstraints="NO" id="ezT-Dl-ESR" userLabel="Picture View" customClass="MXKImageView">
<rect key="frame" x="552" y="5" width="40" height="40"/>
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="JHc-dD-geb"/>
<constraint firstAttribute="height" constant="40" id="lFb-Jo-C7R"/>
</constraints>
</view>
<view contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="SIW-l4-PfI" userLabel="Attachment View" customClass="MXKImageView">
<rect key="frame" x="357" y="18" width="192" height="23"/>
<color key="backgroundColor" red="0.93725490196078431" green="0.93725490196078431" blue="0.95686274509803926" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="192" id="iGr-e7-bde"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="23" id="kGW-IH-XnB"/>
</constraints>
</view>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="KGx-6M-GER" userLabel="File Type Image View">
<rect key="frame" x="357" y="18" width="32" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="32" id="QGB-Md-MLn"/>
<constraint firstAttribute="width" constant="32" id="h7Q-f3-gtz"/>
</constraints>
</imageView>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ZC1-hT-7fs" userLabel="Play Icon Image View">
<rect key="frame" x="437" y="14" width="32" height="32"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="MOQ-Xi-18w"/>
<constraint firstAttribute="height" constant="32" id="pGM-pf-SiP"/>
</constraints>
</imageView>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="Mqf-7a-bsm">
<rect key="frame" x="443" y="20" width="20" height="20"/>
</activityIndicatorView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LVJ-Av-zVs" userLabel="showHideDateTime">
<rect key="frame" x="0.0" y="0.0" width="69" height="49"/>
<constraints>
<constraint firstAttribute="width" constant="69" id="ghQ-Qb-BBg"/>
</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="showHideDateTime:" destination="pad-g3-2YJ" eventType="touchUpInside" id="Ztw-z5-zlU"/>
</connections>
</button>
<view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="a51-cR-7FE" userLabel="bubbleInfoContainer">
<rect key="frame" x="8" y="18" width="61" height="31"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="61" id="fDy-iL-hrD"/>
</constraints>
</view>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="apW-hM-veR" userLabel="ProgressView">
<rect key="frame" x="18" y="-5" width="100" height="70"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="text" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" minimumFontSize="4" preferredMaxLayoutWidth="100" translatesAutoresizingMaskIntoConstraints="NO" id="DYj-Mb-me4" userLabel="Progress stats">
<rect key="frame" x="0.0" y="60" width="100" height="10"/>
<fontDescription key="fontDescription" type="system" pointSize="8"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Oec-kb-Tsz" customClass="MXKPieChartView">
<rect key="frame" x="30" y="0.0" width="40" height="40"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="6tZ-ag-7TT"/>
<constraint firstAttribute="width" constant="40" id="duC-38-cLC"/>
</constraints>
</view>
</subviews>
<gestureRecognizers/>
<constraints>
<constraint firstItem="Oec-kb-Tsz" firstAttribute="top" secondItem="apW-hM-veR" secondAttribute="top" id="49H-ch-qkC"/>
<constraint firstAttribute="height" constant="70" id="GhG-zB-k3A"/>
<constraint firstAttribute="centerX" secondItem="Oec-kb-Tsz" secondAttribute="centerX" id="KZy-2p-KaW"/>
<constraint firstItem="DYj-Mb-me4" firstAttribute="leading" secondItem="apW-hM-veR" secondAttribute="leading" id="UxL-bV-5Ca"/>
<constraint firstAttribute="bottom" secondItem="DYj-Mb-me4" secondAttribute="bottom" id="YFp-gQ-NFz"/>
<constraint firstAttribute="width" constant="100" id="Zr4-5T-h9g"/>
<constraint firstAttribute="trailing" secondItem="DYj-Mb-me4" secondAttribute="trailing" id="ehN-ME-1U1"/>
<constraint firstAttribute="centerX" secondItem="DYj-Mb-me4" secondAttribute="centerX" id="nuq-lO-8pT"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="SIW-l4-PfI" firstAttribute="centerX" secondItem="Mqf-7a-bsm" secondAttribute="centerX" id="1b6-1l-hg0"/>
<constraint firstAttribute="trailing" secondItem="ezT-Dl-ESR" secondAttribute="trailing" constant="8" id="3Pd-Qy-Xva"/>
<constraint firstItem="ezT-Dl-ESR" firstAttribute="leading" secondItem="SIW-l4-PfI" secondAttribute="trailing" constant="3" id="9z3-D2-2SS"/>
<constraint firstItem="a51-cR-7FE" firstAttribute="leading" secondItem="fCg-ju-gnG" secondAttribute="leading" constant="8" id="E3x-h8-GPF"/>
<constraint firstItem="LVJ-Av-zVs" firstAttribute="leading" secondItem="fCg-ju-gnG" secondAttribute="leading" id="Hve-E3-z5N"/>
<constraint firstAttribute="bottom" secondItem="LVJ-Av-zVs" secondAttribute="bottom" id="IKr-Dc-HKz"/>
<constraint firstItem="apW-hM-veR" firstAttribute="leading" secondItem="fCg-ju-gnG" secondAttribute="leading" constant="18" id="LFn-vp-m0v"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" constant="18" id="QDm-tP-KWa"/>
<constraint firstItem="ezT-Dl-ESR" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" constant="5" id="aHl-KA-68x"/>
<constraint firstAttribute="bottom" secondItem="SIW-l4-PfI" secondAttribute="bottom" constant="8" id="fMN-Th-SOT"/>
<constraint firstItem="a51-cR-7FE" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" constant="18" id="fQv-07-Pgx"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="centerY" secondItem="ZC1-hT-7fs" secondAttribute="centerY" id="ffI-vh-SW3"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="leading" secondItem="KGx-6M-GER" secondAttribute="leading" id="j1U-XX-IZW"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="centerX" secondItem="ZC1-hT-7fs" secondAttribute="centerX" id="jah-TA-P0P"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="top" secondItem="KGx-6M-GER" secondAttribute="top" id="loA-GI-HkT"/>
<constraint firstItem="LVJ-Av-zVs" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" id="pYO-hi-P72"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="centerY" secondItem="Mqf-7a-bsm" secondAttribute="centerY" id="sKJ-ny-LjM"/>
<constraint firstAttribute="bottom" secondItem="a51-cR-7FE" secondAttribute="bottom" id="viZ-Sx-3RW"/>
<constraint firstItem="apW-hM-veR" firstAttribute="centerY" secondItem="SIW-l4-PfI" secondAttribute="centerY" id="wmh-x1-U32"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="activityIndicator" destination="Mqf-7a-bsm" id="toi-nL-eJa"/>
<outlet property="attachViewBottomConstraint" destination="fMN-Th-SOT" id="WVk-1p-S0P"/>
<outlet property="attachViewMinHeightConstraint" destination="kGW-IH-XnB" id="97f-NR-EDv"/>
<outlet property="attachViewTopConstraint" destination="QDm-tP-KWa" id="Ku6-FC-jqo"/>
<outlet property="attachViewWidthConstraint" destination="iGr-e7-bde" id="uD4-aU-1Ru"/>
<outlet property="attachmentView" destination="SIW-l4-PfI" id="L4H-ub-pPI"/>
<outlet property="bubbleInfoContainer" destination="a51-cR-7FE" id="wrR-cU-DVm"/>
<outlet property="bubbleInfoContainerTopConstraint" destination="fQv-07-Pgx" id="82c-KH-Wop"/>
<outlet property="fileTypeIconView" destination="KGx-6M-GER" id="MXF-4w-mY7"/>
<outlet property="pictureView" destination="ezT-Dl-ESR" id="YsO-Kp-xaD"/>
<outlet property="playIconView" destination="ZC1-hT-7fs" id="zeH-8B-gxw"/>
<outlet property="progressChartView" destination="Oec-kb-Tsz" id="uoq-6v-6Tm"/>
<outlet property="progressView" destination="apW-hM-veR" id="Iy8-ca-hMi"/>
<outlet property="statsLabel" destination="DYj-Mb-me4" id="CbG-5V-unO"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -1,24 +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 "MXKRoomOutgoingAttachmentBubbleCell.h"
/**
`MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell` displays outgoing attachment with thumbnail, without user's name.
*/
@interface MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell : MXKRoomOutgoingAttachmentBubbleCell
@end
@@ -1,21 +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 "MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell.h"
@implementation MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell
@end
@@ -1,132 +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"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="pad-g3-2YJ" customClass="MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell">
<rect key="frame" x="0.0" y="0.0" width="600" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="pad-g3-2YJ" id="fCg-ju-gnG">
<rect key="frame" x="0.0" y="0.0" width="600" height="49"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="SIW-l4-PfI" userLabel="Attachment View" customClass="MXKImageView">
<rect key="frame" x="357" y="8" width="192" height="33"/>
<color key="backgroundColor" red="0.93725490196078431" green="0.93725490196078431" blue="0.95686274509803926" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="192" id="iGr-e7-bde"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="33" id="nSK-Ug-3Q4"/>
</constraints>
</view>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="KGx-6M-GER" userLabel="File Type Image View">
<rect key="frame" x="357" y="8" width="32" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="32" id="QGB-Md-MLn"/>
<constraint firstAttribute="width" constant="32" id="h7Q-f3-gtz"/>
</constraints>
</imageView>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ZC1-hT-7fs" userLabel="Play Icon Image View">
<rect key="frame" x="437" y="9" width="32" height="32"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="MOQ-Xi-18w"/>
<constraint firstAttribute="height" constant="32" id="pGM-pf-SiP"/>
</constraints>
</imageView>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="Mqf-7a-bsm">
<rect key="frame" x="443" y="15" width="20" height="20"/>
</activityIndicatorView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LVJ-Av-zVs" userLabel="showHideDateTime">
<rect key="frame" x="0.0" y="0.0" width="69" height="49"/>
<constraints>
<constraint firstAttribute="width" constant="69" id="ghQ-Qb-BBg"/>
</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="showHideDateTime:" destination="pad-g3-2YJ" eventType="touchUpInside" id="Ztw-z5-zlU"/>
</connections>
</button>
<view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="a51-cR-7FE" userLabel="bubbleInfoContainer">
<rect key="frame" x="8" y="8" width="61" height="41"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="61" id="fDy-iL-hrD"/>
</constraints>
</view>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="apW-hM-veR" userLabel="ProgressView">
<rect key="frame" x="18" y="-10" width="100" height="70"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="text" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" minimumFontSize="4" preferredMaxLayoutWidth="100" translatesAutoresizingMaskIntoConstraints="NO" id="DYj-Mb-me4" userLabel="Progress stats">
<rect key="frame" x="0.0" y="60" width="100" height="10"/>
<fontDescription key="fontDescription" type="system" pointSize="8"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Oec-kb-Tsz" customClass="MXKPieChartView">
<rect key="frame" x="30" y="0.0" width="40" height="40"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="6tZ-ag-7TT"/>
<constraint firstAttribute="width" constant="40" id="duC-38-cLC"/>
</constraints>
</view>
</subviews>
<gestureRecognizers/>
<constraints>
<constraint firstItem="Oec-kb-Tsz" firstAttribute="top" secondItem="apW-hM-veR" secondAttribute="top" id="49H-ch-qkC"/>
<constraint firstAttribute="height" constant="70" id="GhG-zB-k3A"/>
<constraint firstAttribute="centerX" secondItem="Oec-kb-Tsz" secondAttribute="centerX" id="KZy-2p-KaW"/>
<constraint firstItem="DYj-Mb-me4" firstAttribute="leading" secondItem="apW-hM-veR" secondAttribute="leading" id="UxL-bV-5Ca"/>
<constraint firstAttribute="bottom" secondItem="DYj-Mb-me4" secondAttribute="bottom" id="YFp-gQ-NFz"/>
<constraint firstAttribute="width" constant="100" id="Zr4-5T-h9g"/>
<constraint firstAttribute="trailing" secondItem="DYj-Mb-me4" secondAttribute="trailing" id="ehN-ME-1U1"/>
<constraint firstAttribute="centerX" secondItem="DYj-Mb-me4" secondAttribute="centerX" id="nuq-lO-8pT"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="SIW-l4-PfI" firstAttribute="centerX" secondItem="Mqf-7a-bsm" secondAttribute="centerX" id="1b6-1l-hg0"/>
<constraint firstItem="a51-cR-7FE" firstAttribute="leading" secondItem="fCg-ju-gnG" secondAttribute="leading" constant="8" id="E3x-h8-GPF"/>
<constraint firstItem="LVJ-Av-zVs" firstAttribute="leading" secondItem="fCg-ju-gnG" secondAttribute="leading" id="Hve-E3-z5N"/>
<constraint firstAttribute="bottom" secondItem="LVJ-Av-zVs" secondAttribute="bottom" id="IKr-Dc-HKz"/>
<constraint firstItem="apW-hM-veR" firstAttribute="leading" secondItem="fCg-ju-gnG" secondAttribute="leading" constant="18" id="LFn-vp-m0v"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" constant="8" id="QDm-tP-KWa"/>
<constraint firstAttribute="bottom" secondItem="SIW-l4-PfI" secondAttribute="bottom" constant="8" id="fMN-Th-SOT"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="centerY" secondItem="ZC1-hT-7fs" secondAttribute="centerY" id="ffI-vh-SW3"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="leading" secondItem="KGx-6M-GER" secondAttribute="leading" id="j1U-XX-IZW"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="centerX" secondItem="ZC1-hT-7fs" secondAttribute="centerX" id="jah-TA-P0P"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="top" secondItem="KGx-6M-GER" secondAttribute="top" id="loA-GI-HkT"/>
<constraint firstItem="LVJ-Av-zVs" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" id="pYO-hi-P72"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="centerY" secondItem="Mqf-7a-bsm" secondAttribute="centerY" id="sKJ-ny-LjM"/>
<constraint firstAttribute="bottom" secondItem="a51-cR-7FE" secondAttribute="bottom" id="viZ-Sx-3RW"/>
<constraint firstAttribute="trailing" secondItem="SIW-l4-PfI" secondAttribute="trailing" constant="51" id="wgx-Zr-5R6"/>
<constraint firstItem="apW-hM-veR" firstAttribute="centerY" secondItem="SIW-l4-PfI" secondAttribute="centerY" id="wmh-x1-U32"/>
<constraint firstItem="a51-cR-7FE" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" constant="8" id="xop-v3-Xh2"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="activityIndicator" destination="Mqf-7a-bsm" id="toi-nL-eJa"/>
<outlet property="attachViewBottomConstraint" destination="fMN-Th-SOT" id="fw0-N9-RUj"/>
<outlet property="attachViewMinHeightConstraint" destination="nSK-Ug-3Q4" id="rfM-Ha-vVp"/>
<outlet property="attachViewTopConstraint" destination="QDm-tP-KWa" id="Ku6-FC-jqo"/>
<outlet property="attachViewWidthConstraint" destination="iGr-e7-bde" id="uD4-aU-1Ru"/>
<outlet property="attachmentView" destination="SIW-l4-PfI" id="L4H-ub-pPI"/>
<outlet property="bubbleInfoContainer" destination="a51-cR-7FE" id="wrR-cU-DVm"/>
<outlet property="fileTypeIconView" destination="KGx-6M-GER" id="MXF-4w-mY7"/>
<outlet property="playIconView" destination="ZC1-hT-7fs" id="zeH-8B-gxw"/>
<outlet property="progressChartView" destination="Oec-kb-Tsz" id="uoq-6v-6Tm"/>
<outlet property="progressView" destination="apW-hM-veR" id="Iy8-ca-hMi"/>
<outlet property="statsLabel" destination="DYj-Mb-me4" id="CbG-5V-unO"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -1,27 +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 "MXKRoomBubbleTableViewCell.h"
/**
`MXKRoomOutgoingBubbleTableViewCell` inherits from 'MXKRoomBubbleTableViewCell' class in order to handle specific
options related to outgoing messages (like unsent labels, upload progress in case of attachment).
In order to optimize bubbles rendering, we advise to define a .xib for each layout.
*/
@interface MXKRoomOutgoingBubbleTableViewCell : MXKRoomBubbleTableViewCell
@end
@@ -1,117 +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 "MXKRoomOutgoingBubbleTableViewCell.h"
#import "MXEvent+MatrixKit.h"
#import "NSBundle+Matrixkit.h"
#import "MXKRoomBubbleCellData.h"
#import "MXKSwiftHeader.h"
@implementation MXKRoomOutgoingBubbleTableViewCell
- (void)render:(MXKCellData *)cellData
{
[super render:cellData];
if (bubbleData)
{
// Add unsent label for failed components (except if the app customizes it)
if (self.bubbleInfoContainer && (bubbleData.useCustomUnsentButton == NO))
{
for (MXKRoomBubbleComponent *component in bubbleData.bubbleComponents)
{
if (component.event.sentState == MXEventSentStateFailed)
{
UIButton *unsentButton = [[UIButton alloc] initWithFrame:CGRectMake(0, component.position.y, 58 , 20)];
[unsentButton setTitle:[MatrixKitL10n unsent] forState:UIControlStateNormal];
[unsentButton setTitle:[MatrixKitL10n unsent] forState:UIControlStateSelected];
[unsentButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[unsentButton setTitleColor:[UIColor redColor] forState:UIControlStateSelected];
unsentButton.backgroundColor = [UIColor whiteColor];
unsentButton.titleLabel.font = [UIFont systemFontOfSize:14];
[unsentButton addTarget:self action:@selector(onResendToggle:) forControlEvents:UIControlEventTouchUpInside];
[self.bubbleInfoContainer addSubview:unsentButton];
self.bubbleInfoContainer.hidden = NO;
self.bubbleInfoContainer.userInteractionEnabled = YES;
// ensure that bubbleInfoContainer is at front to catch the tap event
[self.bubbleInfoContainer.superview bringSubviewToFront:self.bubbleInfoContainer];
}
}
}
}
}
- (void)didEndDisplay
{
[super didEndDisplay];
self.bubbleInfoContainer.userInteractionEnabled = NO;
}
#pragma mark - User actions
- (IBAction)onResendToggle:(id)sender
{
if ([sender isKindOfClass:[UIButton class]] && self.delegate)
{
MXEvent *selectedEvent = nil;
NSArray *bubbleComponents = bubbleData.bubbleComponents;
if (bubbleComponents.count == 1)
{
MXKRoomBubbleComponent *component = [bubbleComponents firstObject];
selectedEvent = component.event;
}
else if (bubbleComponents.count)
{
// Here the selected view is a textView (attachment has no more than one component)
// Look for the selected component
UIButton *unsentButton = (UIButton *)sender;
for (MXKRoomBubbleComponent *component in bubbleComponents)
{
// Ignore components without display.
if (!component.attributedTextMessage)
{
continue;
}
if (unsentButton.frame.origin.y == component.position.y)
{
selectedEvent = component.event;
break;
}
}
}
if (selectedEvent)
{
[self.delegate cell:self didRecognizeAction:kMXKRoomBubbleCellUnsentButtonPressed userInfo:@{kMXKRoomBubbleCellEventKey:selectedEvent}];
}
}
}
@end
@@ -1,24 +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 "MXKRoomOutgoingBubbleTableViewCell.h"
/**
`MXKRoomOutgoingTextMsgBubbleCell` displays outgoing message bubbles with user's picture.
*/
@interface MXKRoomOutgoingTextMsgBubbleCell : MXKRoomOutgoingBubbleTableViewCell
@end
@@ -1,21 +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 "MXKRoomOutgoingTextMsgBubbleCell.h"
@implementation MXKRoomOutgoingTextMsgBubbleCell
@end
@@ -1,91 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
<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"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="pad-g3-2YJ" customClass="MXKRoomOutgoingTextMsgBubbleCell">
<rect key="frame" x="0.0" y="0.0" width="600" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="pad-g3-2YJ" id="fCg-ju-gnG">
<rect key="frame" x="0.0" y="0.0" width="600" height="49.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view clipsSubviews="YES" contentMode="scaleAspectFill" translatesAutoresizingMaskIntoConstraints="NO" id="ezT-Dl-ESR" userLabel="Picture View" customClass="MXKImageView">
<rect key="frame" x="552" y="5" width="40" height="40"/>
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="JHc-dD-geb"/>
<constraint firstAttribute="height" constant="40" id="lFb-Jo-C7R"/>
</constraints>
</view>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="text message" translatesAutoresizingMaskIntoConstraints="NO" id="tgO-Rv-C7R" customClass="MXKMessageTextView">
<rect key="frame" x="452" y="10" width="97" height="39.5"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="39" id="2xF-Uv-zZv"/>
<constraint firstAttribute="width" constant="97" id="ghN-E7-j4d"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<dataDetectorType key="dataDetectorTypes" link="YES"/>
</textView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LVJ-Av-zVs" userLabel="showHideDateTime">
<rect key="frame" x="0.0" y="0.0" width="69" height="49.5"/>
<constraints>
<constraint firstAttribute="width" constant="69" id="ghQ-Qb-BBg"/>
</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="showHideDateTime:" destination="pad-g3-2YJ" eventType="touchUpInside" id="Ztw-z5-zlU"/>
</connections>
</button>
<view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="a51-cR-7FE" userLabel="bubbleInfoContainer">
<rect key="frame" x="8" y="10" width="61" height="39.5"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="61" id="fDy-iL-hrD"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="ezT-Dl-ESR" secondAttribute="trailing" constant="8" id="3Pd-Qy-Xva"/>
<constraint firstAttribute="bottom" secondItem="tgO-Rv-C7R" secondAttribute="bottom" id="7C3-Tl-mMq"/>
<constraint firstItem="tgO-Rv-C7R" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" constant="10" id="8Sy-eu-tYs"/>
<constraint firstItem="a51-cR-7FE" firstAttribute="leading" secondItem="fCg-ju-gnG" secondAttribute="leading" constant="8" id="E3x-h8-GPF"/>
<constraint firstItem="LVJ-Av-zVs" firstAttribute="leading" secondItem="fCg-ju-gnG" secondAttribute="leading" id="Hve-E3-z5N"/>
<constraint firstAttribute="bottom" secondItem="LVJ-Av-zVs" secondAttribute="bottom" id="IKr-Dc-HKz"/>
<constraint firstItem="ezT-Dl-ESR" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" constant="5" id="aHl-KA-68x"/>
<constraint firstItem="a51-cR-7FE" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" constant="10" id="fQv-07-Pgx"/>
<constraint firstItem="tgO-Rv-C7R" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="fCg-ju-gnG" secondAttribute="leading" constant="69" id="hwr-aa-TB4"/>
<constraint firstItem="LVJ-Av-zVs" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" id="pYO-hi-P72"/>
<constraint firstAttribute="bottom" secondItem="a51-cR-7FE" secondAttribute="bottom" id="viZ-Sx-3RW"/>
<constraint firstAttribute="trailing" secondItem="tgO-Rv-C7R" secondAttribute="trailing" constant="51" id="xYz-lp-c7K"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="bubbleInfoContainer" destination="a51-cR-7FE" id="wrR-cU-DVm"/>
<outlet property="bubbleInfoContainerTopConstraint" destination="fQv-07-Pgx" id="82c-KH-Wop"/>
<outlet property="messageTextView" destination="tgO-Rv-C7R" id="LZ5-hQ-AbQ"/>
<outlet property="msgTextViewBottomConstraint" destination="7C3-Tl-mMq" id="wCR-UC-iq8"/>
<outlet property="msgTextViewLeadingConstraint" destination="hwr-aa-TB4" id="c9j-8p-cpx"/>
<outlet property="msgTextViewMinHeightConstraint" destination="2xF-Uv-zZv" id="t2t-18-GqU"/>
<outlet property="msgTextViewTopConstraint" destination="8Sy-eu-tYs" id="7yx-oJ-KBP"/>
<outlet property="msgTextViewTrailingConstraint" destination="xYz-lp-c7K" id="ubt-P4-J2o"/>
<outlet property="msgTextViewWidthConstraint" destination="ghN-E7-j4d" id="Dic-6M-yCy"/>
<outlet property="pictureView" destination="ezT-Dl-ESR" id="YsO-Kp-xaD"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -1,24 +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 "MXKRoomOutgoingTextMsgBubbleCell.h"
/**
`MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell` displays outgoing message bubbles without user's name.
*/
@interface MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell : MXKRoomOutgoingTextMsgBubbleCell
@end
@@ -1,21 +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 "MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell.h"
@implementation MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell
@end
@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
<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"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="pad-g3-2YJ" customClass="MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell">
<rect key="frame" x="0.0" y="0.0" width="600" height="35"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="pad-g3-2YJ" id="fCg-ju-gnG">
<rect key="frame" x="0.0" y="0.0" width="600" height="34.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="text message" translatesAutoresizingMaskIntoConstraints="NO" id="tgO-Rv-C7R" customClass="MXKMessageTextView">
<rect key="frame" x="452" y="0.0" width="97" height="34.5"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="97" id="QpI-TQ-x5C"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="34" id="ur4-ZU-uyL"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<dataDetectorType key="dataDetectorTypes" link="YES"/>
</textView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LVJ-Av-zVs" userLabel="showHideDateTime">
<rect key="frame" x="0.0" y="0.0" width="69" height="34.5"/>
<constraints>
<constraint firstAttribute="width" constant="69" id="ghQ-Qb-BBg"/>
</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="showHideDateTime:" destination="pad-g3-2YJ" eventType="touchUpInside" id="Ztw-z5-zlU"/>
</connections>
</button>
<view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="a51-cR-7FE" userLabel="bubbleInfoContainer">
<rect key="frame" x="8" y="0.0" width="61" height="34.5"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="61" id="fDy-iL-hrD"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="tgO-Rv-C7R" secondAttribute="bottom" id="7C3-Tl-mMq"/>
<constraint firstItem="tgO-Rv-C7R" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" id="8Sy-eu-tYs"/>
<constraint firstItem="a51-cR-7FE" firstAttribute="leading" secondItem="fCg-ju-gnG" secondAttribute="leading" constant="8" id="E3x-h8-GPF"/>
<constraint firstItem="LVJ-Av-zVs" firstAttribute="leading" secondItem="fCg-ju-gnG" secondAttribute="leading" id="Hve-E3-z5N"/>
<constraint firstAttribute="bottom" secondItem="LVJ-Av-zVs" secondAttribute="bottom" id="IKr-Dc-HKz"/>
<constraint firstItem="a51-cR-7FE" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" id="fQv-07-Pgx"/>
<constraint firstItem="tgO-Rv-C7R" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="fCg-ju-gnG" secondAttribute="leading" constant="69" id="hwr-aa-TB4"/>
<constraint firstItem="LVJ-Av-zVs" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" id="pYO-hi-P72"/>
<constraint firstAttribute="bottom" secondItem="a51-cR-7FE" secondAttribute="bottom" id="viZ-Sx-3RW"/>
<constraint firstAttribute="trailing" secondItem="tgO-Rv-C7R" secondAttribute="trailing" constant="51" id="xYz-lp-c7K"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="bubbleInfoContainer" destination="a51-cR-7FE" id="wrR-cU-DVm"/>
<outlet property="bubbleInfoContainerTopConstraint" destination="fQv-07-Pgx" id="82c-KH-Wop"/>
<outlet property="messageTextView" destination="tgO-Rv-C7R" id="LZ5-hQ-AbQ"/>
<outlet property="msgTextViewBottomConstraint" destination="7C3-Tl-mMq" id="ReW-nS-1Jc"/>
<outlet property="msgTextViewLeadingConstraint" destination="hwr-aa-TB4" id="c9j-8p-cpx"/>
<outlet property="msgTextViewMinHeightConstraint" destination="ur4-ZU-uyL" id="gIe-dR-6cb"/>
<outlet property="msgTextViewTopConstraint" destination="8Sy-eu-tYs" id="7yx-oJ-KBP"/>
<outlet property="msgTextViewTrailingConstraint" destination="xYz-lp-c7K" id="ubt-P4-J2o"/>
<outlet property="msgTextViewWidthConstraint" destination="QpI-TQ-x5C" id="6JK-pz-IWm"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -115,7 +115,7 @@
if (self->roomTopicListener && self.mxRoom)
{
MXWeakify(self);
[self.mxRoom liveTimeline:^(MXEventTimeline *liveTimeline) {
[self.mxRoom liveTimeline:^(id<MXEventTimeline> liveTimeline) {
MXStrongifyAndReturnIfNil(self);
[liveTimeline removeListener:self->roomTopicListener];