├── Chat21UI ├── sounds │ ├── inline.caf │ └── newnotif.caf ├── images │ ├── general │ │ ├── avatar.png │ │ ├── sendbutton.png │ │ ├── exiticonbutton.png │ │ ├── group-conversation-avatar.png │ │ └── no_image_message_placeholder.png │ ├── icons │ │ ├── chat_check.png │ │ ├── chat_failed.png │ │ ├── chat_watch.png │ │ ├── plus_icon@3x.png │ │ ├── is_new_icon16.png │ │ ├── chat_double_check.png │ │ ├── history_button@2x.png │ │ ├── history_button@3x.png │ │ ├── is_new_point16@2x.png │ │ ├── ic_linear_support_gray_01.png │ │ ├── baseline_create_black_24pt@2x.png │ │ ├── baseline_create_black_24pt@3x.png │ │ ├── ic_linear_support_gray_01@2x.png │ │ ├── ic_linear_support_gray_01@3x.png │ │ ├── baseline_history_black_24pt@2x.png │ │ └── baseline_history_black_24pt@3x.png │ └── tabicons │ │ ├── icon_light_altro.png │ │ ├── icon_light_cerca.png │ │ ├── icon_light_chat.png │ │ ├── icon_light_foto.png │ │ ├── icon_light_home.png │ │ ├── icon_light_watch.png │ │ ├── icon_light_altro@2x.png │ │ ├── icon_light_cerca@2x.png │ │ ├── icon_light_chat@2x.png │ │ ├── icon_light_foto@2x.png │ │ ├── icon_light_home@2x.png │ │ ├── icon_light_profile.png │ │ ├── icon_light_watch@2x.png │ │ └── icon_light_profile@2x.png ├── chat21-Bridging-Header.h ├── view │ ├── ChatNYTPhoto.m │ ├── ChatShowImage.h │ ├── ChatInfoMessageAttributesTVC.h │ ├── UIView+Property.h │ ├── ChatDMConversationCell.h │ ├── ChatStatusTitle.h │ ├── ChatTitleVC.h │ ├── ChatModalCallerDelegate.h │ ├── ChatImagePreviewVC.h │ ├── ChatGroupConversationCell.h │ ├── ChatDMConversationCell.m │ ├── ChatStatusTitle.m │ ├── ChatInfoMessageCell.h │ ├── ChatBaseConversationCell.m │ ├── ChatTitleVC.m │ ├── ChatGroupConversationCell.m │ ├── ChatNYTPhoto.h │ ├── UIView+Property.m │ ├── ChatChangeGroupNameVC.h │ ├── ChatShowImage.m │ ├── ChatGroupMembersVC.h │ ├── ChatBaseConversationCell.h │ ├── ChatMessageCell.h │ ├── ChatTextMessageLeftCell.h │ ├── ChatTextMessageRightCell.h │ ├── ChatSelectGroupLocalTVC.h │ ├── NotificationAlertVC.h │ ├── ChatSelectGroupMembersCellConfigurator.h │ ├── NotificationAlertView.h │ ├── ChatUserCellConfigurator.h │ ├── ChatInfoMessageTVC.h │ ├── ChatImageMessageLeftCell.h │ ├── ChatImageMessageRightCell.h │ ├── ChatImageMessageCell.h │ ├── ChatImagePreviewVC.m │ ├── ChatInfoMessageCell.m │ ├── ChatArchivedConversationsTVC.h │ ├── ChatCreateGroupVC.h │ ├── ChatImageMessageRightCell.m │ ├── ChatMiniBrowserVC.h │ ├── ChatGroupInfoVC.h │ ├── ChatSelectUserLocalVC.h │ ├── ChatSelectGroupMembersLocal.h │ ├── ChatMessagesTVC.h │ ├── ChatChangeGroupNameVC.m │ ├── ChatInfoMessageAttributesTVC.m │ ├── ChatTextMessageLeftCell.m │ ├── ChatTextMessageRightCell.m │ ├── ChatConversationsVC.h │ ├── ChatSelectGroupLocalTVC.m │ ├── ChatMessagesVC.h │ ├── ChatImageMessageCell.m │ └── ChatMessageCell.m ├── util │ ├── ChatMessageComponents.m │ ├── ChatLocal.h │ ├── ChatLocal.m │ ├── ChatCustomView.h │ ├── ChatMessageComponents.h │ ├── ChatCustomView.m │ ├── ChatStyles.h │ ├── CellConfigurator.h │ ├── ChatImageUtil.h │ └── ChatStyles.m ├── QBPopupMenu │ ├── QBPopupMenuOverlayView.h │ ├── QBPopupMenuItemView.h │ ├── QBPlasticPopupMenu.h │ ├── QBPopupMenuItem.h │ ├── QBPopupMenuPagenatorView.h │ ├── QBPopupMenuItem.m │ ├── QBPopupMenuOverlayView.m │ ├── QBPopupMenu.h │ ├── QBPopupMenuItemView.m │ └── QBPopupMenuPagenatorView.m ├── ChatUIManager.h └── xib │ └── status_title_ios11.xib ├── README.md ├── Chat21Core ├── lib │ ├── ChatSynchDelegate.h │ ├── ChatGroupsSubscriber.h │ ├── ChatAuth.h │ ├── FirebaseCustomAuthHelper.h │ ├── ChatImageDownloadManager.h │ ├── ChatService.h │ ├── ChatMessageMetadata.h │ ├── ChatConnectionStatusHandler.h │ ├── ChatPresenceHandler.h │ ├── FirebaseCustomAuthHelper.m │ ├── ChatMessageMetadata.m │ ├── ChatContactsSynchronizer.h │ ├── ChatGroupsHandler.h │ ├── ChatConversationsHandler.h │ ├── ChatImageDownloadManager.m │ ├── ChatAuth.m │ ├── ChatConnectionStatusHandler.m │ └── ChatConversationHandler.h ├── util │ ├── ChatImageWrapper.m │ ├── ChatStringUtil.h │ ├── ChatUpload.m │ ├── ChatImageWrapper.h │ ├── ChatUploadsController.h │ ├── ChatUpload.h │ ├── ChatImageCache.h │ ├── ChatUploadsController.m │ ├── ChatDiskImageCache.h │ ├── ChatUtil.h │ ├── ChatStringUtil.m │ └── ChatImageCache.m ├── Chat-Services.plist ├── DB │ ├── ChatGroupsDB.h │ ├── ChatContactsDB.h │ └── ChatDB.h ├── model │ ├── ChatEventType.h │ ├── ChatUser.h │ ├── ChatGroup.h │ ├── ChatConversation.h │ └── ChatUser.m ├── Base.lproj │ └── Chat.strings ├── en.lproj │ └── Chat.strings └── it.lproj │ └── Chat.strings └── Chat21Tests ├── Info.plist └── ChatMessagesPersistenceTests.m /Chat21UI/sounds/inline.caf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/sounds/inline.caf -------------------------------------------------------------------------------- /Chat21UI/sounds/newnotif.caf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/sounds/newnotif.caf -------------------------------------------------------------------------------- /Chat21UI/images/general/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/general/avatar.png -------------------------------------------------------------------------------- /Chat21UI/images/general/sendbutton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/general/sendbutton.png -------------------------------------------------------------------------------- /Chat21UI/images/icons/chat_check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/icons/chat_check.png -------------------------------------------------------------------------------- /Chat21UI/images/icons/chat_failed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/icons/chat_failed.png -------------------------------------------------------------------------------- /Chat21UI/images/icons/chat_watch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/icons/chat_watch.png -------------------------------------------------------------------------------- /Chat21UI/images/icons/plus_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/icons/plus_icon@3x.png -------------------------------------------------------------------------------- /Chat21UI/images/icons/is_new_icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/icons/is_new_icon16.png -------------------------------------------------------------------------------- /Chat21UI/images/general/exiticonbutton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/general/exiticonbutton.png -------------------------------------------------------------------------------- /Chat21UI/images/icons/chat_double_check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/icons/chat_double_check.png -------------------------------------------------------------------------------- /Chat21UI/images/icons/history_button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/icons/history_button@2x.png -------------------------------------------------------------------------------- /Chat21UI/images/icons/history_button@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/icons/history_button@3x.png -------------------------------------------------------------------------------- /Chat21UI/images/icons/is_new_point16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/icons/is_new_point16@2x.png -------------------------------------------------------------------------------- /Chat21UI/images/tabicons/icon_light_altro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/tabicons/icon_light_altro.png -------------------------------------------------------------------------------- /Chat21UI/images/tabicons/icon_light_cerca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/tabicons/icon_light_cerca.png -------------------------------------------------------------------------------- /Chat21UI/images/tabicons/icon_light_chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/tabicons/icon_light_chat.png -------------------------------------------------------------------------------- /Chat21UI/images/tabicons/icon_light_foto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/tabicons/icon_light_foto.png -------------------------------------------------------------------------------- /Chat21UI/images/tabicons/icon_light_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/tabicons/icon_light_home.png -------------------------------------------------------------------------------- /Chat21UI/images/tabicons/icon_light_watch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/tabicons/icon_light_watch.png -------------------------------------------------------------------------------- /Chat21UI/images/tabicons/icon_light_altro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/tabicons/icon_light_altro@2x.png -------------------------------------------------------------------------------- /Chat21UI/images/tabicons/icon_light_cerca@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/tabicons/icon_light_cerca@2x.png -------------------------------------------------------------------------------- /Chat21UI/images/tabicons/icon_light_chat@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/tabicons/icon_light_chat@2x.png -------------------------------------------------------------------------------- /Chat21UI/images/tabicons/icon_light_foto@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/tabicons/icon_light_foto@2x.png -------------------------------------------------------------------------------- /Chat21UI/images/tabicons/icon_light_home@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/tabicons/icon_light_home@2x.png -------------------------------------------------------------------------------- /Chat21UI/images/tabicons/icon_light_profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/tabicons/icon_light_profile.png -------------------------------------------------------------------------------- /Chat21UI/images/tabicons/icon_light_watch@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/tabicons/icon_light_watch@2x.png -------------------------------------------------------------------------------- /Chat21UI/images/tabicons/icon_light_profile@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/tabicons/icon_light_profile@2x.png -------------------------------------------------------------------------------- /Chat21UI/images/general/group-conversation-avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/general/group-conversation-avatar.png -------------------------------------------------------------------------------- /Chat21UI/images/icons/ic_linear_support_gray_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/icons/ic_linear_support_gray_01.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repo has been deprecated. 2 | 3 | Please refer to new SDK iOS repo here [ios-sdk](https://github.com/chat21/ios-sdk) 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Chat21UI/images/icons/baseline_create_black_24pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/icons/baseline_create_black_24pt@2x.png -------------------------------------------------------------------------------- /Chat21UI/images/icons/baseline_create_black_24pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/icons/baseline_create_black_24pt@3x.png -------------------------------------------------------------------------------- /Chat21UI/images/icons/ic_linear_support_gray_01@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/icons/ic_linear_support_gray_01@2x.png -------------------------------------------------------------------------------- /Chat21UI/images/icons/ic_linear_support_gray_01@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/icons/ic_linear_support_gray_01@3x.png -------------------------------------------------------------------------------- /Chat21UI/images/general/no_image_message_placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/general/no_image_message_placeholder.png -------------------------------------------------------------------------------- /Chat21UI/images/icons/baseline_history_black_24pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/icons/baseline_history_black_24pt@2x.png -------------------------------------------------------------------------------- /Chat21UI/images/icons/baseline_history_black_24pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chat21/chat21-ios-sdk/HEAD/Chat21UI/images/icons/baseline_history_black_24pt@3x.png -------------------------------------------------------------------------------- /Chat21UI/chat21-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "ChatManager.h" 6 | #import "ChatUIManager.h" 7 | #import "ChatUser.h" 8 | #import "ChatAuth.h" 9 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatNYTPhoto.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatNYTPhoto.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 07/05/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatNYTPhoto.h" 10 | 11 | @implementation ChatNYTPhoto 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Chat21Core/lib/ChatSynchDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatSynchDelegate.h 3 | // 4 | // Created by Andrea Sponziello on 10/10/2017. 5 | // Copyright © 2017 Frontiere21. All rights reserved. 6 | // 7 | 8 | @protocol ChatSynchDelegate 9 | @required 10 | - (void)synchEnd; 11 | - (void)synchStart; 12 | @end 13 | 14 | -------------------------------------------------------------------------------- /Chat21Core/util/ChatImageWrapper.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatImageWrapper.m 3 | // Salve Smart 4 | // 5 | // Created by Andrea Sponziello on 05/11/15. 6 | // Copyright © 2015 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatImageWrapper.h" 10 | 11 | @implementation ChatImageWrapper 12 | 13 | @end 14 | 15 | 16 | -------------------------------------------------------------------------------- /Chat21UI/util/ChatMessageComponents.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatMessageComponents.m 3 | // Chat21 4 | // 5 | // Created by Andrea Sponziello on 29/03/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatMessageComponents.h" 10 | 11 | @implementation ChatMessageComponents 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Chat21UI/util/ChatLocal.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatLocal.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 05/02/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ChatLocal : NSObject 12 | 13 | +(NSString *)translate:(NSString *)key; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Chat21Core/lib/ChatGroupsSubscriber.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatGroupsSubscriber.h 3 | // chat 4 | // 5 | // Created by Andrea Sponziello on 11/10/2017. 6 | // Copyright © 2017 Frontiere21. All rights reserved. 7 | // 8 | @class ChatGroup; 9 | 10 | @protocol ChatGroupsSubscriber 11 | @required 12 | -(void)groupAddedOrChanged:(ChatGroup *)group; 13 | @end 14 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatShowImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatShowImage.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 01/09/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ChatShowImage : UIViewController 12 | @property (weak, nonatomic) IBOutlet UIImageView *imageView; 13 | @property (strong, nonatomic) UIImage *image; 14 | @end 15 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatInfoMessageAttributesTVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatInfoMessageAttributesTVC.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 05/04/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ChatInfoMessageAttributesTVC : UITableViewController 12 | 13 | @property(strong, nonatomic) NSDictionary *attributes; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Chat21UI/view/UIView+Property.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+UIView_Property.h 3 | // BirdWatching 4 | // 5 | // Created by andrea sponziello on 05/07/12. 6 | // Copyright (c) 2012 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface UIView (UIView_Property) 13 | 14 | @property (nonatomic, retain) NSObject *property; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Chat21Core/util/ChatStringUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatStringUtil.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 07/12/2017. 6 | // Copyright © 2017 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ChatStringUtil : NSObject 12 | 13 | +(NSString *)timeFromNowToString:(NSDate *)date; 14 | +(NSInteger)daysBetweenDate:(NSDate*)fromDateTime andDate:(NSDate*)toDateTime; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Chat21UI/QBPopupMenu/QBPopupMenuOverlayView.h: -------------------------------------------------------------------------------- 1 | // 2 | // QBPopupMenuOverlayView.h 3 | // QBPopupMenu 4 | // 5 | // Created by Tanaka Katsuma on 2013/11/24. 6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class QBPopupMenu; 12 | 13 | @interface QBPopupMenuOverlayView : UIView 14 | 15 | @property (nonatomic, weak) QBPopupMenu *popupMenu; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatDMConversationCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatDMConversationCell.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 01/03/2019. 6 | // Copyright © 2019 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ChatBaseConversationCell.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface ChatDMConversationCell : ChatBaseConversationCell 15 | 16 | // For future extensions 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /Chat21UI/util/ChatLocal.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatLocal.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 05/02/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatLocal.h" 10 | 11 | @implementation ChatLocal 12 | 13 | +(NSString *)translate:(NSString *)key { 14 | // NSLog(@"translate: %@ with: %@", key, NSLocalizedStringFromTable(key, @"Chat", nil)); 15 | return NSLocalizedStringFromTable(key, @"Chat", nil); 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatStatusTitle.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatStatusTitle.h 3 | // Chat21 4 | // 5 | // Created by Andrea Sponziello on 13/04/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ChatStatusTitle : UIView 12 | 13 | @property (weak, nonatomic) IBOutlet UIButton *usernameButton; 14 | @property (weak, nonatomic) IBOutlet UILabel *statusLabel; 15 | @property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatTitleVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatTitleVC.h 3 | // Chat21 4 | // 5 | // Created by Andrea Sponziello on 20/01/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ChatTitleVC : UIViewController 12 | 13 | @property (weak, nonatomic) IBOutlet UIButton *usernameButton; 14 | @property (weak, nonatomic) IBOutlet UILabel *statusLabel; 15 | @property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Chat21Core/util/ChatUpload.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatUpload.m 3 | // Chat21 4 | // 5 | // Created by Andrea Sponziello on 20/04/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatUpload.h" 10 | 11 | @implementation ChatUpload 12 | 13 | // abstract 14 | -(void)cancel { 15 | } 16 | 17 | // abstract 18 | -(void)start { 19 | } 20 | 21 | - (NSComparisonResult)compare:(ChatUpload *)otherObject { 22 | return [self.creationDate compare:otherObject.creationDate]; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatModalCallerDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatModalCallerDelegate.h 3 | // Shopper 4 | // 5 | // Created by andrea sponziello on 27/07/12. 6 | // Copyright (c) 2012 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol ChatModalCallerDelegate 12 | 13 | - (void)setupViewController:(UIViewController *)controller didFinishSetupWithInfo:(NSDictionary *)setupInfo; 14 | - (void)setupViewController:(UIViewController *)controller didCancelSetupWithInfo:(NSDictionary *)setupInfo; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Chat21Core/lib/ChatAuth.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatAuth.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 05/02/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | @import Firebase; 11 | @class ChatUser; 12 | 13 | @interface ChatAuth : NSObject 14 | 15 | +(void)authWithEmail:(NSString *)email password:(NSString *)password completion:(void (^)(ChatUser *user, NSError *))callback; 16 | +(void)authWithCustomToken:(NSString *)token completion:(void (^)(ChatUser *user, NSError *))callback; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatImagePreviewVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatImagePreviewVC.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 16/05/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ChatImagePreviewVC : UIViewController 12 | @property (weak, nonatomic) IBOutlet UIImageView *imageView; 13 | 14 | @property (strong, nonatomic) UIImage *image; 15 | @property (strong, nonatomic) NSString* recipientFullname; 16 | 17 | @property (weak, nonatomic) IBOutlet UILabel *recipientFullnameLabel; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatGroupConversationCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatGroupConversationCell.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 01/03/2019. 6 | // Copyright © 2019 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ChatBaseConversationCell.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface ChatGroupConversationCell : ChatBaseConversationCell 15 | 16 | @property (weak, nonatomic) IBOutlet UILabel *infoMessageLabel; 17 | @property (weak, nonatomic) IBOutlet UILabel *senderLabel; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatDMConversationCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatDMConversationCell.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 01/03/2019. 6 | // Copyright © 2019 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatDMConversationCell.h" 10 | 11 | @implementation ChatDMConversationCell 12 | 13 | - (void)awakeFromNib { 14 | [super awakeFromNib]; 15 | // Initialization code 16 | } 17 | 18 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 19 | [super setSelected:selected animated:animated]; 20 | 21 | // Configure the view for the selected state 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatStatusTitle.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatStatusTitle.m 3 | // Chat21 4 | // 5 | // Created by Andrea Sponziello on 13/04/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatStatusTitle.h" 10 | 11 | @implementation ChatStatusTitle 12 | 13 | /* 14 | // Only override drawRect: if you perform custom drawing. 15 | // An empty implementation adversely affects performance during animation. 16 | - (void)drawRect:(CGRect)rect { 17 | // Drawing code 18 | } 19 | */ 20 | 21 | - (CGSize)intrinsicContentSize { 22 | return UILayoutFittingExpandedSize; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Chat21Core/util/ChatImageWrapper.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatImageWrapper.h 3 | // Salve Smart 4 | // 5 | // Created by Andrea Sponziello on 05/11/15. 6 | // Copyright © 2015 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface ChatImageWrapper : NSObject 13 | 14 | @property (nonatomic, strong) NSDate *lastReadTime; 15 | @property (nonatomic, strong) NSDate *createdTime; 16 | @property (nonatomic, strong) NSDate *modifiedTime; 17 | @property (nonatomic, strong) UIImage *image; 18 | @property (nonatomic, strong) NSString *key; 19 | 20 | @end 21 | 22 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatInfoMessageCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatInfoMessageCell.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 18/04/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ChatMessageCell.h" 11 | 12 | @interface ChatInfoMessageCell : ChatMessageCell 13 | 14 | @property (weak, nonatomic) IBOutlet UIView *messageBackgroundView; 15 | @property (weak, nonatomic) IBOutlet UILabel *messageLabel; 16 | 17 | -(void)configure:(ChatMessage *)message indexPath:(NSIndexPath *)indexPath rowComponents:(NSDictionary *)rowComponents; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Chat21UI/util/ChatCustomView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatCustomView.h 3 | // 4 | // Created by Andrea Sponziello on 14/09/2017. 5 | // Copyright © 2017 Frontiere21. All rights reserved. 6 | // tutorial: https://github.com/milanpanchal/IBDesignables/blob/master/IBDesignables/SAMCustomView.h 7 | 8 | #import 9 | #import 10 | 11 | IB_DESIGNABLE 12 | 13 | @interface ChatCustomView : UIView 14 | 15 | @property (nonatomic) IBInspectable CGFloat cornerRadius; 16 | @property (nonatomic) IBInspectable UIColor *borderColor; 17 | @property (nonatomic) IBInspectable CGFloat borderWidth; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatBaseConversationCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatBaseConversationCell.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 01/03/2019. 6 | // Copyright © 2019 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatBaseConversationCell.h" 10 | 11 | @implementation ChatBaseConversationCell 12 | 13 | - (void)awakeFromNib { 14 | [super awakeFromNib]; 15 | // Initialization code 16 | } 17 | 18 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 19 | [super setSelected:selected animated:animated]; 20 | 21 | // Configure the view for the selected state 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatTitleVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatTitleVC.m 3 | // Chat21 4 | // 5 | // Created by Andrea Sponziello on 20/01/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatTitleVC.h" 10 | 11 | @implementation ChatTitleVC 12 | 13 | - (void)viewDidLoad { 14 | [super viewDidLoad]; 15 | NSLog(@"ChatTitleVC loaded."); 16 | } 17 | 18 | - (void)didReceiveMemoryWarning { 19 | [super didReceiveMemoryWarning]; 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | -(void)dealloc { 24 | NSLog(@"DEALLOCATING CHAT_TITLE_VC"); 25 | } 26 | 27 | @end 28 | 29 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatGroupConversationCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatGroupConversationCell.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 01/03/2019. 6 | // Copyright © 2019 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatGroupConversationCell.h" 10 | 11 | @implementation ChatGroupConversationCell 12 | 13 | - (void)awakeFromNib { 14 | [super awakeFromNib]; 15 | // Initialization code 16 | } 17 | 18 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 19 | [super setSelected:selected animated:animated]; 20 | 21 | // Configure the view for the selected state 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Chat21Core/lib/FirebaseCustomAuthHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // FirebaseCustomAuthHelper.h 3 | // Soleto 4 | // 5 | // Created by Andrea Sponziello on 13/11/14. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @import Firebase; 12 | //@class Firebase; 13 | @class FAuthData; 14 | 15 | @interface FirebaseCustomAuthHelper : NSObject 16 | 17 | @property (strong, nonatomic) FIRDatabaseReference *ref; 18 | @property (strong, nonatomic) NSString *token; 19 | 20 | - (id) initWithFirebaseRef:(FIRDatabaseReference *)ref token:(NSString *)token; 21 | 22 | - (void) authenticate:(void (^)(NSError *, FAuthData *authData))callback; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Chat21Core/lib/ChatImageDownloadManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatImageDownloadManager.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 06/05/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | @class ChatMessage; 12 | @class ChatImageCache; 13 | 14 | @interface ChatImageDownloadManager : NSObject 15 | 16 | @property(strong, nonatomic) NSMutableDictionary *tasks; 17 | 18 | - (void)downloadImage:(ChatMessage *)message onIndexPath:(NSIndexPath *)indexPath completionHandler:(void(^)(NSIndexPath* indexPath, UIImage *image, NSError *error))callback; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Chat21Core/lib/ChatService.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatService.h 3 | // tiledesk 4 | // 5 | // Created by Andrea Sponziello on 08/07/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | @class ChatConversation; 11 | 12 | @interface ChatService : NSObject 13 | 14 | +(void)archiveConversation:(ChatConversation *)conversation completion:(void (^)(NSError *error))callback; 15 | +(void)archiveAndCloseSupportConversation:(ChatConversation *)conversation completion:(void (^)(NSError *error))callback; 16 | +(void)deleteProfilePhoto:(NSString *)userId completion:(void (^)(NSError *error))callback; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Chat21UI/util/ChatMessageComponents.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatMessageComponents.h 3 | // Chat21 4 | // 5 | // Created by Andrea Sponziello on 29/03/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ChatMessageComponents : NSObject 12 | 13 | // array of NSTextCheckingResult. Iterate: 14 | // for (NSTextCheckingResult *match in arrayOfAllMatches) { 15 | // NSString* substringForMatch = [text substringWithRange:match.range]; 16 | // } 17 | @property (strong, nonatomic) NSString *text; 18 | @property (strong, nonatomic) NSArray *urlsMatches; 19 | @property (strong, nonatomic) NSArray *chatLinkMatches; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Chat21Core/lib/ChatMessageMetadata.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatMessageMetadata.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 10/04/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | @class FIRDataSnapshot; 11 | 12 | @interface ChatMessageMetadata : NSObject 13 | 14 | @property (assign, nonatomic) NSInteger width; 15 | @property (assign, nonatomic) NSInteger height; 16 | @property (strong, nonatomic) NSString *src; 17 | @property (strong, nonatomic) NSMutableDictionary *propertiesDictionary; 18 | 19 | -(NSDictionary *)asDictionary; 20 | +(ChatMessageMetadata *)fromDictionaryFactory:(NSDictionary *)metadata; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatNYTPhoto.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatNYTPhoto.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 07/05/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface ChatNYTPhoto : NSObject 13 | 14 | @property (nonatomic) UIImage *image; 15 | @property (nonatomic) NSData *imageData; 16 | @property (nonatomic) UIImage *placeholderImage; 17 | @property (nonatomic) NSAttributedString *attributedCaptionTitle; 18 | @property (nonatomic) NSAttributedString *attributedCaptionSummary; 19 | @property (nonatomic) NSAttributedString *attributedCaptionCredit; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Chat21UI/view/UIView+Property.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+UIView_Property.m 3 | // BirdWatching 4 | // 5 | // Created by andrea sponziello on 05/07/12. 6 | // Copyright (c) 2012 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "UIView+Property.h" 10 | #import 11 | 12 | @implementation UIView (UIView_Property) 13 | 14 | static char UIB_PROPERTY_KEY; 15 | 16 | @dynamic property; 17 | 18 | -(void)setProperty:(NSObject *)property 19 | { 20 | objc_setAssociatedObject(self, &UIB_PROPERTY_KEY, property, OBJC_ASSOCIATION_RETAIN); 21 | } 22 | 23 | -(NSObject*)property 24 | { 25 | return (NSObject*)objc_getAssociatedObject(self, &UIB_PROPERTY_KEY); 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Chat21Core/util/ChatUploadsController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatUploadsController.h 3 | // Chat21 4 | // 5 | // Created by Andrea Sponziello on 20/04/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ChatUpload; 12 | 13 | @interface ChatUploadsController : NSObject 14 | 15 | @property(strong, nonatomic) NSMutableDictionary *currentUploads; 16 | 17 | -(void)addDataController:(ChatUpload *)dc; 18 | -(void)removeDataController:(ChatUpload *)dc; 19 | 20 | // delegate methods 21 | -(void)didFinishConnection:(ChatUpload *)dataController withError:(NSError *)error; 22 | 23 | +(ChatUploadsController *)getSharedInstance; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatChangeGroupNameVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatChangeGroupNameVC.h 3 | // Chat21 4 | // 5 | // Created by Andrea Sponziello on 28/04/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ChatGroup; 12 | 13 | @interface ChatChangeGroupNameVC : UIViewController 14 | 15 | @property (strong, nonatomic) ChatGroup *group; 16 | 17 | @property (weak, nonatomic) IBOutlet UITextField *groupNameTextField; 18 | @property (weak, nonatomic) IBOutlet UIBarButtonItem *saveButton; 19 | @property (weak, nonatomic) IBOutlet UIBarButtonItem *cancelButton; 20 | - (IBAction)cancelAction:(id)sender; 21 | - (IBAction)saveAction:(id)sender; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Chat21Core/Chat-Services.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | delete-profile-photo-uri 6 | /api/%@/contacts/me/photo 7 | profile-image-base-url 8 | https://firebasestorage.googleapis.com/v0/b/%@/o 9 | archive-and-support-conversation-uri 10 | /supportapi/%@/groups/%@ 11 | archive-conversation-uri 12 | /api/%@/conversations/%@ 13 | base-url 14 | https://us-central1-chat-v2-dev.cloudfunctions.net 15 | 16 | 17 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatShowImage.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatShowImage.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 01/09/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatShowImage.h" 10 | 11 | @interface ChatShowImage () 12 | 13 | @end 14 | 15 | @implementation ChatShowImage 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | self.imageView.image = self.image; 20 | } 21 | 22 | - (void)didReceiveMemoryWarning { 23 | [super didReceiveMemoryWarning]; 24 | // Dispose of any resources that can be recreated. 25 | } 26 | 27 | - (IBAction)cancelAction:(id)sender { 28 | [self dismissViewControllerAnimated:YES completion:nil]; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatGroupMembersVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatGroupMembersVC.h 3 | // Smart21 4 | // 5 | // Created by Andrea Sponziello on 05/05/15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @class ChatGroup; 12 | @class ChatImageCache; 13 | 14 | @interface ChatGroupMembersVC : UITableViewController 15 | 16 | @property (strong, nonatomic) ChatGroup *group; 17 | //@property (strong, nonatomic) NSMutableArray *members_array; 18 | - (IBAction)addMember:(id)sender; 19 | @property (weak, nonatomic) IBOutlet UIBarButtonItem *addMemberButton; 20 | 21 | // IMAGES 22 | @property (strong, nonatomic) ChatImageCache *imageCache; 23 | @property (nonatomic, retain) NSMutableDictionary *imageDownloadsInProgress; 24 | 25 | @end 26 | 27 | -------------------------------------------------------------------------------- /Chat21Core/util/ChatUpload.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatUpload.h 3 | // Chat21 4 | // 5 | // Created by Andrea Sponziello on 20/04/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ChatUploadsController; 12 | 13 | @interface ChatUpload : NSObject 14 | 15 | @property (nonatomic, strong) ChatUploadsController *uploadsController; 16 | @property (nonatomic, assign) NSInteger currentState; 17 | @property (nonatomic, assign) float progress; 18 | @property (nonatomic, strong) NSString *uploadDescription; 19 | @property (nonatomic, strong) NSDate *creationDate; 20 | @property (nonatomic, strong) NSString *uploadId; 21 | 22 | // abstract 23 | -(void)cancel; 24 | // abstract 25 | -(void)start; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Chat21Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatBaseConversationCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatBaseConversationCell.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 01/03/2019. 6 | // Copyright © 2019 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface ChatBaseConversationCell : UITableViewCell 14 | 15 | @property (weak, nonatomic) IBOutlet UILabel *subjectLabel; 16 | @property (weak, nonatomic) IBOutlet UILabel *dateLabel; 17 | @property (weak, nonatomic) IBOutlet UILabel *lastMessageLabel; 18 | @property (weak, nonatomic) IBOutlet UIImageView *is_newMessageIcon; 19 | @property (weak, nonatomic) IBOutlet UIImageView *profileImageView; 20 | @property (weak, nonatomic) IBOutlet UIImageView *archivedImageView; 21 | 22 | @end 23 | 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /Chat21UI/QBPopupMenu/QBPopupMenuItemView.h: -------------------------------------------------------------------------------- 1 | // 2 | // QBPopupMenuItemView.h 3 | // QBPopupMenu 4 | // 5 | // Created by Tanaka Katsuma on 2013/11/22. 6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class QBPopupMenu; 12 | @class QBPopupMenuItem; 13 | 14 | @interface QBPopupMenuItemView : UIView 15 | 16 | @property (nonatomic, weak) QBPopupMenu *popupMenu; 17 | 18 | @property (nonatomic, strong, readonly) UIButton *button; 19 | @property (nonatomic, strong) UIImage *image; 20 | @property (nonatomic, strong) UIImage *highlightedImage; 21 | 22 | @property (nonatomic, strong) QBPopupMenuItem *item; 23 | 24 | + (instancetype)itemViewWithItem:(QBPopupMenuItem *)item; 25 | - (instancetype)initWithItem:(QBPopupMenuItem *)item; 26 | 27 | - (void)performAction; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatMessageCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatMessageCell.h 3 | // Chat21 4 | // 5 | // Created by Andrea Sponziello on 08/03/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | @class ChatMessage; 11 | 12 | @interface ChatMessageCell : UITableViewCell 13 | 14 | -(void)attributedString:(UILabel *)label text:(ChatMessage *)message indexPath:(NSIndexPath *)indexPath rowComponents:(NSDictionary *)rowComponents right_cell:(BOOL)right_cell; 15 | -(NSString *)displayUserOfMessage:(ChatMessage *)m; 16 | -(NSString*)formatDateMessage:(int)numberDaysBetweenChats message:(ChatMessage*)message row:(CGFloat)row; 17 | +(void)setStatusImage:(ChatMessage *)message statusImage:(UIImageView *)status_image_view; 18 | //+(CGSize)computeImageSize:(ChatMessage *)message; 19 | //+(BOOL)hasValidImageMetadata:(ChatMessage *)message; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatTextMessageLeftCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatTextMessageLeftCell.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 17/04/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ChatMessageCell.h" 11 | 12 | @interface ChatTextMessageLeftCell : ChatMessageCell 13 | 14 | @property (weak, nonatomic) IBOutlet UIView *messageBackgroundView; 15 | @property (weak, nonatomic) IBOutlet UILabel *messageLabel; 16 | @property (weak, nonatomic) IBOutlet UILabel *usernameLabel; 17 | @property (weak, nonatomic) IBOutlet UILabel *dateLabel; 18 | @property (weak, nonatomic) IBOutlet UILabel *timeLabel; 19 | 20 | -(void)configure:(ChatMessage *)message messages:(NSArray *)messages indexPath:(NSIndexPath *)indexPath viewController:(UIViewController *)viewController rowComponents:(NSMutableDictionary *)rowComponents; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatTextMessageRightCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatTextMessageRightCell.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 18/04/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ChatImageMessageCell.h" 11 | 12 | @interface ChatTextMessageRightCell : ChatImageMessageCell 13 | 14 | @property (weak, nonatomic) IBOutlet UIView *messageBackgroundView; 15 | @property (weak, nonatomic) IBOutlet UILabel *messageLabel; 16 | @property (weak, nonatomic) IBOutlet UILabel *dateLabel; 17 | @property (weak, nonatomic) IBOutlet UIImageView *statusImageView; 18 | @property (weak, nonatomic) IBOutlet UILabel *timeLabel; 19 | 20 | -(void)configure:(ChatMessage *)message messages:(NSArray *)messages indexPath:(NSIndexPath *)indexPath viewController:(UIViewController *)viewController rowComponents:(NSMutableDictionary *)rowComponents; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatSelectGroupLocalTVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatSelectGroupLocalTVC.h 3 | // 4 | // Created by Andrea Sponziello on 26/09/2017. 5 | // Copyright © 2017 Frontiere21. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "ChatGroupsDB.h" 10 | //#import "ChatModalCallerDelegate.h" 11 | 12 | //@class ChatImageCache; 13 | @class ChatGroup; 14 | //@class ChatUser; 15 | 16 | @interface ChatSelectGroupLocalTVC : UITableViewController 17 | 18 | @property (weak, nonatomic) IBOutlet UIBarButtonItem *cancelButton; 19 | @property (nonatomic, retain) NSMutableDictionary *imageDownloadsInProgress; 20 | 21 | @property (strong, nonatomic) NSMutableArray *groups; 22 | //@property (strong, nonatomic) id modalCallerDelegate; 23 | @property (nonatomic, copy) void (^completionCallback)(ChatGroup *group, BOOL canceled); 24 | 25 | - (IBAction)CancelAction:(id)sender; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Chat21UI/util/ChatCustomView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatCustomView.m 3 | // 4 | // Created by Andrea Sponziello on 14/09/2017. 5 | // Copyright © 2017 Frontiere21. All rights reserved. 6 | // tutorial: https://github.com/milanpanchal/IBDesignables/blob/master/IBDesignables/SAMCustomView.m 7 | 8 | #import "ChatCustomView.h" 9 | 10 | @implementation ChatCustomView 11 | 12 | - (void)setCornerRadius:(CGFloat)cornerRadius { 13 | _cornerRadius = cornerRadius; 14 | self.layer.cornerRadius = cornerRadius; 15 | } 16 | 17 | - (void)setBorderColor:(UIColor *)borderColor { 18 | _borderColor = borderColor; 19 | self.layer.borderColor = borderColor.CGColor; 20 | } 21 | 22 | - (void)setBorderWidth:(CGFloat)borderWidth { 23 | _borderWidth = borderWidth; 24 | self.layer.borderWidth = borderWidth; 25 | } 26 | 27 | //-(void)layoutSubviews { 28 | // [self layoutSubviews]; 29 | // self.layer.cornerRadius = self.cornerRadius; 30 | //} 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Chat21UI/view/NotificationAlertVC.h: -------------------------------------------------------------------------------- 1 | // DEPRECATED 2 | 3 | // NotificationAlertVC.h 4 | // Chat21 5 | // 6 | // Created by Andrea Sponziello on 22/12/15. 7 | // Copyright © 2015 Frontiere21. All rights reserved. 8 | // 9 | 10 | #import 11 | #import 12 | 13 | @interface NotificationAlertVC : UIViewController 14 | 15 | @property (weak, nonatomic) IBOutlet UIImageView *userImage; 16 | @property (weak, nonatomic) IBOutlet UILabel *senderLabel; 17 | @property (weak, nonatomic) IBOutlet UILabel *messageLabel; 18 | - (IBAction)closeAction:(id)sender; 19 | @property (weak, nonatomic) IBOutlet UIButton *closeButton; 20 | 21 | @property (strong, nonatomic) NSTimer *animationTimer; 22 | @property (assign, nonatomic) BOOL animating; 23 | @property (assign, nonatomic) SystemSoundID sound; 24 | 25 | @property (strong, nonatomic) NSString *sender; 26 | 27 | -(void)animateShow; 28 | -(void)animateClose; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Chat21Core/util/ChatImageCache.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatImageCache.h 3 | // Salve Smart 4 | // 5 | // Created by Andrea Sponziello on 04/11/15. 6 | // Copyright © 2015 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @class ChatImageWrapper; 13 | 14 | @interface ChatImageCache : NSObject 15 | 16 | @property (nonatomic, strong) NSMutableDictionary *imageCache; 17 | @property (nonatomic, strong) NSString *cacheName; 18 | @property (nonatomic, assign) NSInteger maxSize; 19 | 20 | -(void)addImage:(UIImage *)image withKey:(NSString *)key; 21 | -(ChatImageWrapper *)getImage:(NSString *)key; 22 | -(void)deleteImage:(NSString*)imageKey; 23 | -(void)empty; 24 | 25 | //+(ChatImageCache *)getSharedInstance; 26 | +(NSString *)filePathInApp:(NSString *)path; 27 | //-(ChatImageWrapper *)restoreImageWithKey:(NSString *)imageKey; 28 | //-(void)listAllImagesFromDisk; 29 | 30 | +(ChatImageCache *)getSharedInstance; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatSelectGroupMembersCellConfigurator.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatSelectGroupMembersCellConfigurator.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 11/09/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @class ChatSelectGroupMembersLocal; 13 | @class ChatDiskImageCache; 14 | 15 | static int SELECT_GROUP_MEMBER_LIST_CELL_SIZE = 80; 16 | 17 | @interface ChatSelectGroupMembersCellConfigurator : NSObject 18 | 19 | @property(strong, nonatomic) ChatSelectGroupMembersLocal *vc; 20 | @property(strong, nonatomic) UITableView *tableView; 21 | @property(strong, nonatomic) ChatDiskImageCache *imageCache; 22 | @property(strong, nonatomic) NSMutableDictionary *tasks; 23 | 24 | -(id)initWith:(ChatSelectGroupMembersLocal *)vc; 25 | -(UITableViewCell *)configureCellAtIndexPath:(NSIndexPath *)indexPath; 26 | -(void)teminatePendingTasks; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Chat21UI/QBPopupMenu/QBPlasticPopupMenu.h: -------------------------------------------------------------------------------- 1 | // 2 | // QBPlasticPopupMenu.h 3 | // QBPopupMenu 4 | // 5 | // Created by Tanaka Katsuma on 2013/11/25. 6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved. 7 | // 8 | 9 | #import "QBPopupMenu.h" 10 | 11 | @interface QBPlasticPopupMenu : QBPopupMenu 12 | 13 | // NOTE: When subclassing this class, use these methods to customize the appearance. 14 | - (CGMutablePathRef)upperHeadPathInRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius CF_RETURNS_RETAINED; 15 | - (CGMutablePathRef)lowerHeadPathInRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius CF_RETURNS_RETAINED; 16 | - (CGMutablePathRef)upperTailPathInRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius CF_RETURNS_RETAINED; 17 | - (CGMutablePathRef)lowerTailPathInRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius CF_RETURNS_RETAINED; 18 | 19 | - (void)drawLeftSeparatorInRect:(CGRect)rect highlighted:(BOOL)highlighted; 20 | - (void)drawRightSeparatorInRect:(CGRect)rect highlighted:(BOOL)highlighted; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Chat21Core/DB/ChatGroupsDB.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatGroupsDB.h 3 | // 4 | // Created by Andrea Sponziello on 26/09/2017. 5 | // Copyright © 2017 Frontiere21. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | @class ChatGroup; 12 | 13 | @interface ChatGroupsDB : NSObject 14 | { 15 | NSString *databasePath; 16 | } 17 | 18 | @property (assign, nonatomic) BOOL logQuery; 19 | 20 | +(ChatGroupsDB*)getSharedInstance; 21 | -(BOOL)createDBWithName:(NSString *)name; 22 | 23 | // groups 24 | -(void)insertOrUpdateGroupSyncronized:(ChatGroup *)group completion:(void(^)()) callback; 25 | //-(void)insertGroupOnlyIfNotExistsSyncronized:(ChatGroup *)group completion:(void(^)()) callback; // only used by group-created-by-push-notification 26 | -(NSMutableArray *)getAllGroupsForUser:(NSString *)user; 27 | -(void)getGroupByIdSyncronized:(NSString *)groupId completion:(void(^)(ChatGroup *)) callback; 28 | -(void)removeGroupSyncronized:(NSString *)groupId completion:(void(^)(BOOL error)) callback; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Chat21UI/view/NotificationAlertView.h: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationAlertView.h 3 | // Chat21 4 | // 5 | // Created by Andrea Sponziello on 16/04/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface NotificationAlertView : UIView 13 | 14 | -(void)initViewWithHeight:(float)height; 15 | 16 | @property (weak, nonatomic) IBOutlet UIImageView *userImage; 17 | @property (weak, nonatomic) IBOutlet UILabel *senderLabel; 18 | @property (weak, nonatomic) IBOutlet UILabel *messageLabel; 19 | - (IBAction)closeAction:(id)sender; 20 | @property (weak, nonatomic) IBOutlet UIButton *closeButton; 21 | 22 | @property (strong, nonatomic) NSTimer *animationTimer; 23 | @property (assign, nonatomic) BOOL animating; 24 | @property (assign, nonatomic) SystemSoundID sound; 25 | 26 | @property (strong, nonatomic) UIWindow *myWindow; 27 | 28 | @property (strong, nonatomic) NSString *sender; 29 | 30 | -(void)animateShow; 31 | -(void)animateClose; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Chat21Core/lib/ChatConnectionStatusHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatConnectionStatusHandler.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 01/01/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ChatEventType.h" 11 | 12 | @import Firebase; 13 | 14 | @interface ChatConnectionStatusHandler : NSObject 15 | 16 | @property (strong, nonatomic) FIRDatabaseReference *connectedRef; 17 | @property (assign, nonatomic) FIRDatabaseHandle connectedRefHandle; 18 | 19 | -(void)isStatusConnectedWithCompletionBlock:(void (^)(BOOL connected, NSError* error))callback; 20 | 21 | // observer 22 | @property (strong, nonatomic) NSMutableDictionary *eventObservers; 23 | @property (assign, atomic) volatile int64_t lastEventHandle; 24 | -(NSUInteger)observeEvent:(ChatConnectionStatusEventType)eventType withCallback:(void (^)())callback; 25 | -(void)removeObserverWithHandle:(NSUInteger)event_handle; 26 | -(void)removeAllObservers; 27 | 28 | -(void)connect; 29 | -(void)dispose; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatUserCellConfigurator.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatUserCellConfigurator.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 10/09/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | @class ChatImageCache; 12 | @class ChatDiskImageCache; 13 | @class ChatUser; 14 | @class ChatGroup; 15 | @class ChatSelectUserLocalVC; 16 | @class NSURLSessionDataTask; 17 | 18 | static int SELECT_USER_LIST_CELL_SIZE = 80; 19 | 20 | @interface ChatUserCellConfigurator : NSObject 21 | 22 | @property(strong, nonatomic) ChatSelectUserLocalVC *vc; 23 | @property(strong, nonatomic) UITableView *tableView; 24 | @property(strong, nonatomic) ChatDiskImageCache *imageCache; 25 | @property (strong, nonatomic) ChatGroup *group; 26 | @property(strong, nonatomic) NSMutableDictionary *tasks; 27 | 28 | -(id)initWith:(ChatSelectUserLocalVC *)vc; 29 | -(UITableViewCell *)configureCellAtIndexPath:(NSIndexPath *)indexPath; 30 | -(void)teminatePendingTasks; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatInfoMessageTVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatInfoMessageTVC.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 05/04/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ChatMessage; 12 | 13 | @interface ChatInfoMessageTVC : UITableViewController 14 | 15 | @property (strong, nonatomic) ChatMessage *message; 16 | 17 | @property (weak, nonatomic) IBOutlet UILabel *mtype; 18 | @property (weak, nonatomic) IBOutlet UILabel *subtype; 19 | @property (weak, nonatomic) IBOutlet UILabel *date; 20 | @property (weak, nonatomic) IBOutlet UILabel *status; 21 | @property (weak, nonatomic) IBOutlet UILabel *senderFullname; 22 | @property (weak, nonatomic) IBOutlet UILabel *senderId; 23 | @property (weak, nonatomic) IBOutlet UILabel *recipientFullname; 24 | @property (weak, nonatomic) IBOutlet UILabel *recipientId; 25 | @property (weak, nonatomic) IBOutlet UILabel *language; 26 | @property (weak, nonatomic) IBOutlet UILabel *channel; 27 | @property (weak, nonatomic) IBOutlet UILabel *messageId; 28 | @property (weak, nonatomic) IBOutlet UILabel *attributes; 29 | 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatImageMessageLeftCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatImageMessageLeftCell.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 03/05/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatImageMessageCell.h" 10 | @class ChatImageCache; 11 | 12 | @interface ChatImageMessageLeftCell : ChatImageMessageCell 13 | 14 | //@property (weak, nonatomic) IBOutlet UIView *messageBackgroundView; 15 | //@property (weak, nonatomic) IBOutlet UIImageView *messageImageView; 16 | @property (weak, nonatomic) IBOutlet UILabel *usernameLabel; 17 | //@property (weak, nonatomic) IBOutlet UILabel *dateLabel; 18 | //@property (weak, nonatomic) IBOutlet UILabel *timeLabel; 19 | //@property (weak, nonatomic) IBOutlet NSLayoutConstraint *imageHeightConstraint; 20 | //@property (weak, nonatomic) IBOutlet UIProgressView *progressView; 21 | 22 | -(void)configure:(ChatMessage *)message messages:(NSArray *)messages indexPath:(NSIndexPath *)indexPath viewController:(UIViewController *)viewController rowComponents:(NSMutableDictionary *)rowComponents imageCache:(ChatImageCache *)imageCache completion:(void(^)(UIImage *image))callback; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Chat21UI/QBPopupMenu/QBPopupMenuItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // QBPopupMenuItem.h 3 | // QBPopupMenu 4 | // 5 | // Created by Tanaka Katsuma on 2013/11/22. 6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface QBPopupMenuItem : NSObject 13 | 14 | @property (nonatomic, weak, readonly) id target; 15 | @property (nonatomic, assign, readonly) SEL action; 16 | 17 | @property (nonatomic, copy, readonly) NSString *title; 18 | @property (nonatomic, copy, readonly) UIImage *image; 19 | 20 | + (instancetype)itemWithTitle:(NSString *)title target:(id)target action:(SEL)action; 21 | + (instancetype)itemWithImage:(UIImage *)image target:(id)target action:(SEL)action; 22 | + (instancetype)itemWithTitle:(NSString *)title image:(UIImage *)image target:(id)target action:(SEL)action; 23 | 24 | - (instancetype)initWithTitle:(NSString *)title target:(id)target action:(SEL)action; 25 | - (instancetype)initWithImage:(UIImage *)image target:(id)target action:(SEL)action; 26 | - (instancetype)initWithTitle:(NSString *)title image:(UIImage *)image target:(id)target action:(SEL)action; 27 | 28 | @end 29 | 30 | -------------------------------------------------------------------------------- /Chat21Core/model/ChatEventType.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatEventType.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 21/12/2017. 6 | // Copyright © 2017 Frontiere21. All rights reserved. 7 | // 8 | 9 | #ifndef ChatEventType_h 10 | #define ChatEventType_h 11 | 12 | #import 13 | //#import "FIRDatabaseSwiftNameSupport.h" 14 | 15 | /** 16 | * This enum is the set of events that you can observe in a Conversation. 17 | */ 18 | typedef NS_ENUM(NSInteger, ChatMessageEventType) { 19 | ChatEventMessageAdded, 20 | ChatEventMessageDeleted, 21 | ChatEventMessageChanged, 22 | };// CHAT_SWIFT_NAME(DataEventType); 23 | 24 | typedef NS_ENUM(NSInteger, ChatConversationEventType) { 25 | ChatEventConversationAdded, 26 | ChatEventConversationDeleted, 27 | ChatEventConversationChanged, 28 | ChatEventConversationReadStatusChanged, 29 | ChatEventArchivedConversationAdded, 30 | ChatEventArchivedConversationRemoved 31 | };// CHAT_SWIFT_NAME(DataEventType); 32 | 33 | typedef NS_ENUM(NSInteger, ChatConnectionStatusEventType) { 34 | ChatConnectionStatusEventConnected, 35 | ChatConnectionStatusEventDisconnected 36 | };// CHAT_SWIFT_NAME(DataEventType); 37 | 38 | #endif /* ChatEventType_h */ 39 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatImageMessageRightCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatImageMessageRightCell.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 21/04/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | //#import 10 | //#import "ChatMessageCell.h" 11 | #import "ChatImageMessageCell.h" 12 | @class ChatImageCache; 13 | 14 | @interface ChatImageMessageRightCell : ChatImageMessageCell 15 | 16 | //@property (weak, nonatomic) IBOutlet UIView *messageBackgroundView; 17 | //@property (weak, nonatomic) IBOutlet UIImageView *messageImageView; 18 | //@property (weak, nonatomic) IBOutlet UILabel *dateLabel; 19 | //@property (weak, nonatomic) IBOutlet UIImageView *statusImageView; 20 | //@property (weak, nonatomic) IBOutlet UILabel *timeLabel; 21 | //@property (weak, nonatomic) IBOutlet NSLayoutConstraint *imageHeightConstraint; 22 | //@property (weak, nonatomic) IBOutlet UIProgressView *progressView; 23 | 24 | -(void)configure:(ChatMessage *)message messages:(NSArray *)messages indexPath:(NSIndexPath *)indexPath viewController:(UIViewController *)viewController rowComponents:(NSMutableDictionary *)rowComponents imageCache:(ChatImageCache *)imageCache completion:(void(^)(UIImage *image))callback; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatImageMessageCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatImageMessageCell.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 06/05/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatMessageCell.h" 10 | #import "ChatImageMessageCell.h" 11 | @class ChatImageCache; 12 | 13 | @interface ChatImageMessageCell : ChatMessageCell 14 | 15 | @property (weak, nonatomic) IBOutlet UIView *messageBackgroundView; 16 | @property (weak, nonatomic) IBOutlet UIImageView *messageImageView; 17 | @property (weak, nonatomic) IBOutlet UILabel *dateLabel; 18 | @property (weak, nonatomic) IBOutlet UIImageView *statusImageView; 19 | @property (weak, nonatomic) IBOutlet UILabel *timeLabel; 20 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *imageHeightConstraint; 21 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *imageWidthConstraint; 22 | 23 | 24 | @property (weak, nonatomic) IBOutlet UIProgressView *progressView; 25 | 26 | -(void)configure:(ChatMessage *)message messages:(NSArray *)messages indexPath:(NSIndexPath *)indexPath viewController:(UIViewController *)viewController rowComponents:(NSMutableDictionary *)rowComponents imageCache:(ChatImageCache *)imageCache completion:(void(^)(UIImage *image))callback; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Chat21UI/QBPopupMenu/QBPopupMenuPagenatorView.h: -------------------------------------------------------------------------------- 1 | // 2 | // QBPopupMenuPagenatorView.h 3 | // QBPopupMenu 4 | // 5 | // Created by Tanaka Katsuma on 2013/11/23. 6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved. 7 | // 8 | 9 | #import "QBPopupMenuItemView.h" 10 | 11 | typedef NS_ENUM(NSUInteger, QBPopupMenuPagenatorDirection) { 12 | QBPopupMenuPagenatorDirectionLeft, 13 | QBPopupMenuPagenatorDirectionRight 14 | }; 15 | 16 | @interface QBPopupMenuPagenatorView : QBPopupMenuItemView 17 | 18 | @property (nonatomic, weak) id target; 19 | @property (nonatomic, assign) SEL action; 20 | 21 | + (CGFloat)pagenatorWidth; 22 | 23 | + (instancetype)leftPagenatorViewWithTarget:(id)target action:(SEL)action; 24 | + (instancetype)rightPagenatorViewWithTarget:(id)target action:(SEL)action; 25 | 26 | - (instancetype)initWithArrowDirection:(QBPopupMenuPagenatorDirection)arrowDirection target:(id)target action:(SEL)action; 27 | 28 | // NOTE: When subclassing this class, use these methods to customize the appearance. 29 | - (CGMutablePathRef)arrowPathInRect:(CGRect)rect direction:(QBPopupMenuPagenatorDirection)direction CF_RETURNS_RETAINED; 30 | - (void)drawArrowInRect:(CGRect)rect direction:(QBPopupMenuPagenatorDirection)direction highlighted:(BOOL)highlighted; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatImagePreviewVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatImagePreviewVC.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 16/05/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatImagePreviewVC.h" 10 | 11 | @interface ChatImagePreviewVC () 12 | 13 | @end 14 | 15 | @implementation ChatImagePreviewVC 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | self.imageView.image = self.image; 20 | self.recipientFullnameLabel.text = [[NSString alloc] initWithFormat:@"> %@", self.recipientFullname]; 21 | } 22 | 23 | - (void)didReceiveMemoryWarning { 24 | [super didReceiveMemoryWarning]; 25 | // Dispose of any resources that can be recreated. 26 | } 27 | 28 | /* 29 | #pragma mark - Navigation 30 | 31 | // In a storyboard-based application, you will often want to do a little preparation before navigation 32 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 33 | // Get the new view controller using [segue destinationViewController]. 34 | // Pass the selected object to the new view controller. 35 | } 36 | */ 37 | 38 | - (IBAction)cancelAction:(id)sender { 39 | self.image = nil; 40 | [self performSegueWithIdentifier:@"unwindToMessagesVCsegue" sender:self]; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatInfoMessageCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatInfoMessageCell.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 18/04/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatInfoMessageCell.h" 10 | #import "ChatMessage.h" 11 | 12 | @implementation ChatInfoMessageCell 13 | 14 | - (void)awakeFromNib { 15 | [super awakeFromNib]; 16 | // Initialization code 17 | } 18 | 19 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 20 | [super setSelected:selected animated:animated]; 21 | 22 | // Configure the view for the selected state 23 | } 24 | 25 | -(void)configure:(ChatMessage *)message indexPath:(NSIndexPath *)indexPath rowComponents:(NSDictionary *)rowComponents { 26 | UIView *sfondo = self.backgroundView;//(UIView *)[cell viewWithTag:50]; 27 | sfondo.layer.masksToBounds = YES; 28 | sfondo.layer.shadowOffset = CGSizeMake(-15, 20); 29 | sfondo.layer.shadowRadius = 5; 30 | sfondo.layer.shadowOpacity = 0.5; 31 | self.selectionStyle = UITableViewCellSelectionStyleNone; 32 | UILabel *labelMessage = self.messageLabel;//(UILabel *)[cell viewWithTag:10]; 33 | [self attributedString:labelMessage text:message indexPath:indexPath rowComponents:rowComponents right_cell:NO]; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /Chat21Core/DB/ChatContactsDB.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatContactsDB.h 3 | // 4 | // 5 | // Created by Andrea Sponziello on 17/09/2017. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @class ChatMessage; 13 | @class ChatConversation; 14 | @class ChatGroup; 15 | @class ChatUser; 16 | 17 | @interface ChatContactsDB : NSObject 18 | { 19 | NSString *databasePath; 20 | } 21 | 22 | @property (assign, nonatomic) BOOL logQuery; 23 | 24 | +(ChatContactsDB*)getSharedInstance; 25 | -(BOOL)createDBWithName:(NSString *)name; 26 | 27 | // contacts 28 | -(void)insertOrUpdateContactSyncronized:(ChatUser *)contact completion:(void(^)(void)) callback; 29 | -(void)getContactByIdSyncronized:(NSString *)contactId completion:(void(^)(ChatUser *)) callback; 30 | -(void)getMultipleContactsByIdsSyncronized:(NSArray *)contactIds completion:(void(^)(NSArray *)) callback; 31 | -(void)searchContactsByFullnameSynchronized:(NSString *)searchString completion:(void (^)(NSArray *))callback; 32 | -(void)removeContactSynchronized:(NSString *)contactId completion:(void(^)(void)) callback; 33 | -(ChatUser *)getMostRecentContact; 34 | -(BOOL)insertContact:(ChatUser *)contact; 35 | -(NSArray*)getAllContacts; // test only 36 | -(void)drop_database; 37 | 38 | @end 39 | 40 | -------------------------------------------------------------------------------- /Chat21UI/util/ChatStyles.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatStyles.h 3 | // tiledesk 4 | // 5 | // Created by Andrea Sponziello on 03/08/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface ChatStyles : NSObject 13 | 14 | // messages 15 | @property(strong, nonatomic) UIFont *ballonFont; 16 | // left ballon 17 | @property(strong, nonatomic) UIColor *ballonLeftBackgroundColor; 18 | @property(strong, nonatomic) UIColor *ballonLeftTextColor; 19 | @property(strong, nonatomic) UIColor *ballonLeftLinkColor; 20 | @property(strong, nonatomic) UIColor *linkLeftHLBackgroundColor; 21 | @property(strong, nonatomic) UIColor *linkLeftHLTextColor; 22 | 23 | // right ballon 24 | @property(strong, nonatomic) UIColor *ballonRightBackgroundColor; 25 | @property(strong, nonatomic) UIColor *ballonRightTextColor; 26 | @property(strong, nonatomic) UIColor *ballonRightLinkColor; 27 | @property(strong, nonatomic) UIColor *linkRightHLBackgroundColor; 28 | @property(strong, nonatomic) UIColor *linkRightHLTextColor; 29 | 30 | // conversation 31 | @property(strong, nonatomic) UIColor *lastMessageTextColor; 32 | @property(strong, nonatomic) UIColor *lastMessageIsNewTextColor; 33 | @property(strong, nonatomic) UIColor *infoMessageTextColor; 34 | 35 | +(ChatStyles *)sharedInstance; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatArchivedConversationsTVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatArchivedConversationsTVC.h 3 | // tiledesk 4 | // 5 | // Created by Andrea Sponziello on 16/07/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | @class ChatConversationsHandler; 11 | @class CellConfigurator; 12 | @class ChatDiskImageCache; 13 | 14 | static const int SECTION_ARCHIVED_CONVERSATIONS_INDEX = 0; 15 | 16 | @interface ChatArchivedConversationsTVC : UITableViewController 17 | 18 | @property (strong, nonatomic) NSString *selectedConversationId; 19 | @property (strong, nonatomic) NSString *selectedRecipientId; 20 | @property (strong, nonatomic) NSString *selectedRecipientFullname; 21 | @property (strong, nonatomic) NSString *selectedGroupId; 22 | @property (strong, nonatomic) NSString *selectedGroupName; 23 | @property (assign, nonatomic) BOOL isModal; 24 | @property (strong, nonatomic) ChatDiskImageCache *imageCache; 25 | @property (strong, nonatomic) ChatConversationsHandler *conversationsHandler; 26 | @property (strong, nonatomic) CellConfigurator *cellConfigurator; 27 | 28 | // subscribers 29 | @property (assign, nonatomic) NSUInteger added_handle; 30 | @property (assign, nonatomic) NSUInteger changed_handle; 31 | @property (assign, nonatomic) NSUInteger read_status_changed_handle; 32 | @property (assign, nonatomic) NSUInteger deleted_handle; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatCreateGroupVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // SHPChatCreateGroupVC.h 3 | // Smart21 4 | // 5 | // Created by Andrea Sponziello on 25/03/15. 6 | // 7 | // 8 | 9 | #import 10 | @class ChatGroup; 11 | 12 | @interface ChatCreateGroupVC : UIViewController 13 | 14 | @property (weak, nonatomic) IBOutlet UIImageView *groupImageView; 15 | @property (weak, nonatomic) IBOutlet UILabel *addPhotoLabelOverloaded; 16 | @property (weak, nonatomic) IBOutlet UITextField *groupNameTextField; 17 | @property (weak, nonatomic) IBOutlet UIBarButtonItem *nextButton; 18 | @property (weak, nonatomic) IBOutlet UILabel *messageLabel; 19 | 20 | @property (weak, nonatomic) IBOutlet UIBarButtonItem *cancelButton; 21 | 22 | @property (nonatomic, copy) void (^completionCallback)(ChatGroup *group, BOOL canceled); 23 | 24 | // imagepicker 25 | //@property (strong, nonatomic) UIActionSheet *photoMenuSheet; 26 | @property (nonatomic, strong) UIImagePickerController *imagePickerController; 27 | @property (nonatomic, strong) UIImagePickerController *photoLibraryController; 28 | @property (nonatomic, strong) UIImage *scaledImage; 29 | //@property (nonatomic, strong) ChatImageUploadSmart21DC *uploader; 30 | @property (strong, nonatomic) UIImage *bigImage; 31 | @property (assign, nonatomic) UIImage *currentProfilePhoto; 32 | 33 | - (IBAction)nextAction:(id)sender; 34 | - (IBAction)cancelAction:(id)sender; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /Chat21UI/util/CellConfigurator.h: -------------------------------------------------------------------------------- 1 | // 2 | // CellConfigurator.h 3 | // Chat21 4 | // 5 | // Created by Andrea Sponziello on 28/03/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @class ChatConversation; 13 | @class ChatImageCache; 14 | @class ChatConversationsVC; 15 | @class ChatDiskImageCache; 16 | 17 | static int CONVERSATION_LIST_CELL_SIZE = 120; 18 | 19 | @interface CellConfigurator : NSObject 20 | 21 | @property(strong, nonatomic) NSArray *conversations; 22 | @property(strong, nonatomic) UITableView *tableView; 23 | @property(strong, nonatomic) ChatDiskImageCache *imageCache; 24 | 25 | -(id)initWithTableView:(UITableView *)tableView imageCache:(ChatDiskImageCache *)imageCache conversations:(NSArray *)conversations; 26 | -(UITableViewCell *)configureConversationCell:(ChatConversation *)conversation indexPath:(NSIndexPath *)indexPath; 27 | +(void)changeReadStatus:(ChatConversation *)conversation forCell:(UITableViewCell *)cell; 28 | +(void)archiveLabel:(UITableViewCell *)cell archived:(BOOL)archived; 29 | +(UIImage *)avatarTypeDirect:(BOOL)typeDirect; 30 | +(UIImage *)setupPhotoCell:(UIImageView *)image_view typeDirect:(BOOL)typeDirect imageURL:(NSString *)imageURL imageCache:(ChatDiskImageCache *)imageCache size:(int)size; 31 | +(BOOL)isIndexPathVisible:(NSIndexPath *)indexPath tableView:(UITableView *)tableView; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatImageMessageRightCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatImageMessageRightCell.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 21/04/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatImageMessageRightCell.h" 10 | #import "ChatMessage.h" 11 | #import "ChatUtil.h" 12 | #import "ChatLocal.h" 13 | #import "ChatMessageMetadata.h" 14 | #import "ChatImageCache.h" 15 | #import "ChatImageWrapper.h" 16 | #import "ChatImageUtil.h" 17 | 18 | @implementation ChatImageMessageRightCell 19 | 20 | - (void)awakeFromNib { 21 | [super awakeFromNib]; 22 | // Initialization code 23 | } 24 | 25 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 26 | [super setSelected:selected animated:animated]; 27 | 28 | // Configure the view for the selected state 29 | } 30 | 31 | -(void)configure:(ChatMessage *)message messages:(NSArray *)messages indexPath:(NSIndexPath *)indexPath viewController:(UIViewController *)viewController rowComponents:(NSMutableDictionary *)rowComponents imageCache:(ChatImageCache *)imageCache completion:(void(^)(UIImage *image)) callback { 32 | 33 | [super configure:message messages:messages indexPath:indexPath viewController:viewController rowComponents:rowComponents imageCache:imageCache completion:callback]; 34 | 35 | UIImageView *status_image_view = self.statusImageView; 36 | [ChatMessageCell setStatusImage:message statusImage:status_image_view]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Chat21Core/model/ChatUser.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatUser.h 3 | // 4 | // Created by Andrea Sponziello on 01/09/2017. 5 | // Copyright © 2017 Frontiere21. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | static NSString* const FIREBASE_USER_ID = @"uid"; 11 | static NSString* const FIREBASE_USER_FIRSTNAME = @"firstname"; 12 | static NSString* const FIREBASE_USER_LASTNAME = @"lastname"; 13 | static NSString* const FIREBASE_USER_IMAGEURL = @"imageurl"; 14 | static NSString* const FIREBASE_USER_TIMESTAMP = @"timestamp"; 15 | static NSString* const FIREBASE_USER_EMAIL = @"email"; 16 | 17 | @interface ChatUser : NSObject 18 | 19 | @property(nonatomic, strong) NSString *userId; 20 | @property(nonatomic, strong) NSString *firstname; 21 | @property(nonatomic, strong) NSString *lastname; 22 | @property(nonatomic, strong) NSString *fullname; 23 | @property(nonatomic, strong) NSString *imageurl; 24 | @property(nonatomic, strong) NSString *email; 25 | //@property(nonatomic, strong) NSString *password; 26 | @property(nonatomic, assign) NSInteger createdon; 27 | @property(nonatomic, strong) NSDate *createdonAsDate; 28 | 29 | @property(nonatomic, strong) NSString *profileImagePath; 30 | //@property(nonatomic, strong) NSString *profileThumbImagePath; 31 | @property(nonatomic, strong) NSString *profileImageURL; 32 | @property(nonatomic, strong) NSString *profileThumbImageURL; 33 | 34 | -(NSDictionary *)asDictionary; 35 | -(id)init:(NSString *)userid fullname:(NSString *)fullname; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Chat21Core/lib/ChatPresenceHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatPresenceHandler.h 3 | // Chat21 4 | // 5 | // Created by Andrea Sponziello on 02/01/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @import Firebase; 12 | 13 | @class FirebaseCustomAuthHelper; 14 | @class Firebase; 15 | @class ChatUser; 16 | 17 | @interface ChatPresenceHandler : NSObject 18 | 19 | @property (strong, nonatomic) ChatUser *loggeduser; 20 | @property (strong, nonatomic) FirebaseCustomAuthHelper *authHelper; 21 | 22 | @property (strong, nonatomic) NSString *firebaseToken; 23 | @property (strong, nonatomic) FIRDatabaseReference *rootRef; 24 | @property (strong, nonatomic) NSString *tenant; 25 | @property (assign, nonatomic) FIRDatabaseHandle connectionsRefHandle; 26 | @property (strong, nonatomic) FIRDatabaseReference *deviceConnectionRef; 27 | @property (strong, nonatomic) NSString *deviceConnectionKey; 28 | 29 | //-(id)initWithFirebaseRef:(NSString *)firebaseRef tenant:(NSString *)tenant user:(SHPUser *)user; 30 | -(id)initWithTenant:(NSString *)tenant user:(ChatUser *)user; 31 | +(FIRDatabaseReference *)onlineRefForUser:(NSString *)userid; 32 | +(FIRDatabaseReference *)lastOnlineRefForUser:(NSString *)userid; 33 | -(void)setupMyPresence; 34 | -(void)onlineStatusForUser:(NSString *)userid withCallback:(void (^)(BOOL status))callback; 35 | -(void)lastOnlineDateForUser:(NSString *)userid withCallback:(void (^)(NSDate *lastOnlineDate))callback; 36 | -(void)goOffline; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Chat21Core/lib/FirebaseCustomAuthHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // FirebaseCustomAuthHelper.m 3 | // Soleto 4 | // 5 | // Created by Andrea Sponziello on 13/11/14. 6 | // 7 | 8 | #import "FirebaseCustomAuthHelper.h" 9 | //#import 10 | 11 | @import Firebase; 12 | 13 | @implementation FirebaseCustomAuthHelper 14 | 15 | - (id) initWithFirebaseRef:(FIRDatabaseReference *)ref token:(NSString *)token { 16 | self = [super init]; 17 | if (self) { 18 | NSLog(@" ref: %@ token: %@", ref, token); 19 | self.ref = ref; 20 | self.token = token; 21 | } 22 | return self; 23 | } 24 | 25 | - (void) authenticate:(void (^)(NSError *, FAuthData *authData))callback { 26 | NSLog(@"authenticate: WARNING! NOT IMPLEMENTED!"); 27 | // [self.ref authWithCustomToken:self.token withCompletionBlock:^(NSError *error, FAuthData *authData) { 28 | //// NSLog(@"End Login:\nError:%@\nauth:%@\nuid:%@\nprovider:%@\ntoken:%@\nproviderData:%@", error, authData.auth, authData.uid, authData.provider, authData.token, authData.providerData); 29 | // NSLog(@"email: %@", [authData.auth objectForKey:@"email"]); 30 | // NSLog(@"uid: %@", [authData.auth objectForKey:@"uid"]); 31 | // NSLog(@"username: %@", [authData.auth objectForKey:@"username"]); 32 | // if (error) { 33 | // NSLog(@"Login Failed! %@", error); 34 | // } else { 35 | // NSLog(@"Login succeeded! %@", authData); 36 | // callback(error, authData); 37 | // } 38 | // }]; 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatMiniBrowserVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatMiniBrowserVC.h 3 | // 4 | // Created by Andrea Sponziello on 27/07/2017. 5 | // Copyright © 2017 Frontiere21. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "ChatModalCallerDelegate.h" 10 | 11 | @interface ChatMiniBrowserVC : UIViewController 12 | { 13 | UIBarButtonItem *refreshButtonItem; 14 | UIActivityIndicatorView *activityIndicator; 15 | UIBarButtonItem *activityButtonItem; 16 | UIColor *tintColor; 17 | UIColor *colorBackground; 18 | 19 | enum actionSheetButtonIndex { 20 | // kChatSendButtonIndex, 21 | kCopyURLButtonIndex, 22 | kSafariButtonIndex, 23 | kChromeButtonIndex, 24 | }; 25 | } 26 | 27 | @property (nonatomic, strong) NSString *urlPage; 28 | @property (nonatomic, strong) NSString *webviewURLAbsoluteString; 29 | @property (nonatomic, strong) NSString *titlePage; 30 | 31 | @property (weak, nonatomic) IBOutlet UIToolbar *toolBar; 32 | @property (assign, nonatomic) BOOL hiddenToolBar; 33 | @property (weak, nonatomic) IBOutlet UIWebView *webView; 34 | @property (weak, nonatomic) IBOutlet UIBarButtonItem *forwardButton; 35 | @property (strong, nonatomic) NSString *username; 36 | @property (strong, nonatomic) NSString *password; 37 | 38 | - (IBAction)actionCloseView:(id)sender; 39 | - (IBAction)forwardLink:(id)sender; 40 | - (IBAction)reloadPage:(id)sender; 41 | - (IBAction)nextPage:(id)sender; 42 | - (IBAction)backPage:(id)sender; 43 | @end 44 | -------------------------------------------------------------------------------- /Chat21Core/lib/ChatMessageMetadata.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatMessageMetadata.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 10/04/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatMessageMetadata.h" 10 | #import "ChatMessage.h" 11 | 12 | @implementation ChatMessageMetadata 13 | 14 | -(NSDictionary *)asDictionary { 15 | NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; 16 | if (self.src) { 17 | [dict setObject:self.src forKey:MSG_METADATA_ATTACHMENT_SRC]; 18 | } 19 | if (self.width) { 20 | [dict setObject:@(self.width) forKey:MSG_METADATA_IMAGE_WIDTH]; 21 | } 22 | if (self.height) { 23 | [dict setObject:@(self.height) forKey:MSG_METADATA_IMAGE_HEIGHT]; 24 | } 25 | return dict; 26 | } 27 | 28 | +(ChatMessageMetadata *)fromDictionaryFactory:(NSDictionary *)metadata { 29 | if (!metadata) { 30 | return nil; 31 | } else if (![metadata isKindOfClass:[NSDictionary class]]) { 32 | return nil; 33 | } 34 | // NSLog(@"metadata: %@", metadata); 35 | ChatMessageMetadata *metadata_obj = [[ChatMessageMetadata alloc] init]; 36 | NSString *src = metadata[MSG_METADATA_ATTACHMENT_SRC]; 37 | metadata_obj.src = src; 38 | if (metadata[MSG_METADATA_IMAGE_HEIGHT]) { 39 | metadata_obj.height = (NSInteger) metadata[MSG_METADATA_IMAGE_HEIGHT]; 40 | } 41 | if (metadata[MSG_METADATA_IMAGE_WIDTH]) { 42 | metadata_obj.width = (NSInteger) metadata[MSG_METADATA_IMAGE_WIDTH]; 43 | } 44 | return metadata_obj; 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatGroupInfoVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatGroupInfoVC.h 3 | // Smart21 4 | // 5 | // Created by Andrea Sponziello on 04/05/15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @class ChatGroup; 12 | @class ChatDiskImageCache; 13 | 14 | @interface ChatGroupInfoVC : UITableViewController 15 | 16 | //@property(strong, nonatomic) NSString *groupId; 17 | @property(strong, nonatomic) ChatGroup *group; 18 | 19 | @property (weak, nonatomic) IBOutlet UIImageView *profilePhotoImageView; 20 | //@property (weak, nonatomic) IBOutlet UILabel *addPhotoLabelOverloaded; 21 | // imagepicker 22 | @property (nonatomic, strong) UIImagePickerController *imagePickerController; 23 | @property (nonatomic, strong) UIImagePickerController *photoLibraryController; 24 | @property (nonatomic, strong) UIImage *scaledImage; 25 | @property (strong, nonatomic) UIImage *bigImage; 26 | @property (assign, nonatomic) UIImage *currentProfilePhoto; 27 | @property (assign, nonatomic) NSString *profileId; 28 | 29 | @property (strong, nonatomic) ChatDiskImageCache *imageCache; 30 | 31 | @property (weak, nonatomic) IBOutlet UILabel *groupNameLabel; 32 | @property (weak, nonatomic) IBOutlet UILabel *membersLabel; 33 | @property (weak, nonatomic) IBOutlet UILabel *createdOnLabel; 34 | @property (weak, nonatomic) IBOutlet UILabel *createdByLabel; 35 | @property (weak, nonatomic) IBOutlet UILabel *adminLabel; 36 | @property (weak, nonatomic) IBOutlet UILabel *idLabel; 37 | 38 | 39 | - (IBAction)unwindToGroupInfoVC:(UIStoryboardSegue*)sender; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /Chat21Core/lib/ChatContactsSynchronizer.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatContactsSynchronizer.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 09/09/2017. 6 | // Copyright © 2017 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ChatSynchDelegate.h" 11 | 12 | @import Firebase; 13 | @class ChatUser; 14 | 15 | @interface ChatContactsSynchronizer : NSObject 16 | 17 | @property (strong, nonatomic) ChatUser * _Nullable loggeduser; 18 | @property (nonatomic, strong) FIRDatabaseReference * _Nullable rootRef; 19 | @property (strong, nonatomic) NSString * _Nullable tenant; 20 | @property (strong, nonatomic) FIRDatabaseReference * _Nullable contactsRef; 21 | @property (assign, nonatomic) FIRDatabaseHandle contact_ref_handle_added; 22 | @property (assign, nonatomic) FIRDatabaseHandle contact_ref_handle_changed; 23 | @property (assign, nonatomic) FIRDatabaseHandle contact_ref_handle_removed; 24 | @property (strong, nonatomic) NSTimer * _Nullable synchTimer; 25 | @property (assign, nonatomic) BOOL synchronizing; 26 | @property (strong, nonnull) NSMutableArray> *synchSubscribers; 27 | 28 | -(id _Nonnull )initWithTenant:(NSString *_Nonnull)tenant user:(ChatUser *_Nonnull)user; 29 | -(void)startSynchro; 30 | //-(void)stopSynchro; 31 | //+(void)insertOrUpdateContactOnDB:(ChatUser *)user; 32 | +(ChatUser *_Nullable)contactFromDictionaryFactory:(NSDictionary *_Nonnull)snapshot; 33 | -(void)dispose; 34 | -(void)addSynchSubscriber:(id_Nonnull)subscriber; 35 | -(void)removeSynchSubscriber:(id_Nonnull)subscriber; 36 | +(ChatUser *_Nonnull)contactFromSnapshotFactory:(FIRDataSnapshot *_Nonnull)snapshot; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Chat21Core/util/ChatUploadsController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatUploadsController.m 3 | // Chat21 4 | // 5 | // Created by Andrea Sponziello on 20/04/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatUploadsController.h" 10 | #import "ChatUpload.h" 11 | 12 | static ChatUploadsController *sharedInstance = nil; 13 | 14 | @implementation ChatUploadsController 15 | 16 | -(id)init { 17 | NSLog(@"Initializing ChatUploadsController..."); 18 | self = [super init]; 19 | if (self) { 20 | self.currentUploads = [[NSMutableDictionary alloc] init]; 21 | } 22 | return self; 23 | } 24 | 25 | +(ChatUploadsController *)getSharedInstance { 26 | NSLog(@"Creating ChatUploadsController instance..."); 27 | if (!sharedInstance) { 28 | NSLog(@"Instance created..."); 29 | sharedInstance = [[super alloc] init]; 30 | } 31 | return sharedInstance; 32 | } 33 | 34 | -(void)addDataController:(ChatUpload *)dc { 35 | dc.uploadsController = self; 36 | [self.currentUploads setObject:dc forKey:dc.uploadId]; 37 | NSLog(@"Added controller. Total %lu", (unsigned long)self.currentUploads.count); 38 | } 39 | 40 | -(void)didFinishConnection:(ChatUpload *)dc withError:(NSError *)error { 41 | NSLog(@"Connection for %@ finished. Finding and removing...", dc); 42 | NSLog(@"Total controllers: %lu", (unsigned long)self.currentUploads.count); 43 | [self removeDataController:dc]; 44 | } 45 | 46 | -(void)removeDataController:(ChatUpload *)dc { 47 | [dc cancel]; 48 | [self.currentUploads removeObjectForKey:dc.uploadId]; 49 | NSLog(@"Controller was removed. Total %lu", (unsigned long)self.currentUploads.count); 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatSelectUserLocalVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatSelectUserLocalVC.h 3 | // 4 | // Created by Andrea Sponziello on 13/09/2017. 5 | // Copyright © 2017 Frontiere21. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "ChatSynchDelegate.h" 10 | 11 | @class ChatImageCache; 12 | @class ChatGroup; 13 | @class ChatUser; 14 | @class ChatDiskImageCache; 15 | @class ChatUserCellConfigurator; 16 | 17 | @interface ChatSelectUserLocalVC : UIViewController 18 | 19 | @property (strong, nonatomic) ChatUser *userSelected; 20 | @property (strong, nonatomic) NSArray *users; 21 | //@property (strong, nonatomic) NSMutableArray *recentUsers; 22 | //@property (strong, nonatomic) NSMutableArray *allUsers; 23 | //@property (nonatomic, retain) NSMutableDictionary *imageDownloadsInProgress; 24 | @property (nonatomic, copy) void (^completionCallback)(ChatUser *contact, BOOL canceled); 25 | @property (weak, nonatomic) IBOutlet UITableView *tableView; 26 | @property (weak, nonatomic) IBOutlet UISearchBar *searchBar; 27 | @property (strong, nonatomic) NSString *searchBarPlaceholder; 28 | @property (strong, nonatomic) NSString *textToSearch; 29 | @property (strong, nonatomic) NSTimer *searchTimer; 30 | @property (strong, nonatomic) NSString *lastUsersTextSearch; 31 | @property (strong, nonatomic) ChatGroup *group; 32 | @property (assign, nonatomic) BOOL synchronizing; 33 | @property (strong, nonatomic) UIActivityIndicatorView *activityIndicator; 34 | @property (strong, nonatomic) ChatUserCellConfigurator *cellConfigurator; 35 | @property (weak, nonatomic) IBOutlet UIBarButtonItem *cancelButton; 36 | 37 | //-(void)networkError; 38 | - (IBAction)CancelAction:(id)sender; 39 | 40 | @end 41 | 42 | -------------------------------------------------------------------------------- /Chat21UI/util/ChatImageUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatImageUtil.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 07/04/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface ChatImageUtil : NSObject 13 | 14 | +(UIImage *)adjustEXIF:(UIImage *)image; 15 | +(CGSize)fitSize:(CGSize)size intoSize:(CGSize)size; 16 | +(CGSize)fitSizeWidth:(CGSize)size intoSize:(CGSize)newSize; 17 | //+(CGSize)imageSizeForProduct:(SHPProduct *)p constrainedInto:(CGSize)intoSize; 18 | +(UIImage *)scaleImage:(UIImage*)image toSize:(CGSize)size; 19 | +(UIImage *)previewThumbOnImage:(UIImage *)thumb image:(UIImage *)image; 20 | +(UIImage *)tintImage:(UIImage *)image withColor:(UIColor *)tintColor; 21 | +(void)drawViewShadow:(UIView *)view; 22 | +(UIImage *)imageWithColor:(UIColor *)tintColor withRect:(CGRect)drawRect; 23 | + (UIColor *)colorWithHexString:(NSString *)colorString; 24 | + (UIColor *)colorWithHexValue:(int)hexValue; 25 | 26 | +(void)customIcon:(UIImageView *)iconImage; 27 | +(void)arroundImage:(float)borderRadius borderWidth:(float)borderWidth layer:(CALayer *)layer; 28 | +(UIImage *)circleImage:(UIImage *)image; 29 | +(void)rotateImageView:(UIImageView *)imageView angle:(float)angle; 30 | +(void)rotateImageViewWithAnimation:(UIImageView *)imageView duration:(float)duration angle:(float)angle; 31 | //+(void)saveImageAsJPEG:(UIImage *)image withName:(NSString*)fileName inFolder:(NSString *)folderName; 32 | +(void)saveImageAsPNG:(UIImage *)image withName:(NSString*)fileName inFolder:(NSString *)folderName; 33 | +(UIImage *)squareImageFromImage:(UIImage *)image scaledToSize:(CGFloat)newSize; 34 | +(UIImage *)blur:(UIImage*)theImage radius:(CGFloat)radius; 35 | + (UIImage *)imageWithColor:(UIColor *)color; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Chat21Core/util/ChatDiskImageCache.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatDiskImageCache.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 27/08/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface ChatDiskImageCache : NSObject 13 | 14 | @property (nonatomic, strong) NSMutableDictionary *memoryObjects; 15 | @property (nonatomic, strong) NSString *cacheFolder; 16 | @property (nonatomic, assign) NSInteger maxSize; 17 | @property(strong, nonatomic) NSMutableDictionary *tasks; 18 | 19 | -(NSURLSessionDataTask *)getImage:(NSString *)imageURL completionHandler:(void(^)(NSString *imageURL, UIImage *image))callback; 20 | - (NSURLSessionDataTask *)getImage:(NSString *)imageURL sized:(long)size circle:(BOOL)circle completionHandler:(void(^)(NSString *imageURL, UIImage *image))callback; 21 | 22 | -(UIImage *)getCachedImage:(NSString *)key; 23 | -(UIImage *)getCachedImage:(NSString *)key sized:(long)size circle:(BOOL)circle; 24 | -(void)addImageToCache:(UIImage *)image withKey:(NSString *)key; 25 | -(void)addImageToMemoryCache:(UIImage *)image withKey:(NSString *)key; 26 | -(void)deleteImageFromCacheWithKey:(NSString *)key; 27 | -(void)removeCachedImage:(NSString *)key sized:(long)size; 28 | -(void)deleteFilesFromCacheStartingWith:(NSString *)partial_key; 29 | -(void)deleteFilesFromDiskCacheOfProfile:(NSString *)profileId; 30 | -(NSString *)urlAsKey:(NSURL *)url; 31 | -(void)updateProfile:(NSString *)profileId image:(UIImage *)image; 32 | 33 | +(NSString *)sizedKey:(NSString *)key size:(long) size; 34 | //+(void)saveImageAsJPEG:(UIImage *)image withName:(NSString*)name inFolder:(NSString *)folderName; 35 | +(UIImage *)loadImage:(NSString *)name inFolder:(NSString *)folderName; 36 | +(ChatDiskImageCache *)getSharedInstance; 37 | 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatSelectGroupMembersLocal.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatSelectGroupMembersLocal.h 3 | // 4 | // Created by Andrea Sponziello on 14/09/2017. 5 | // Copyright © 2017 Frontiere21. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "ChatModalCallerDelegate.h" 10 | 11 | @class HelloApplicationContext; 12 | @class ChatImageCache; 13 | @class ChatGroup; 14 | @class ChatUser; 15 | @class ChatDiskImageCache; 16 | @class ChatSelectGroupMembersCellConfigurator; 17 | 18 | @interface ChatSelectGroupMembersLocal : UIViewController 19 | 20 | @property (strong, nonatomic) HelloApplicationContext *applicationContext; 21 | @property (strong, nonatomic) ChatUser *userSelected; 22 | @property (strong, nonatomic) NSArray *users; 23 | @property (strong, nonatomic) NSMutableArray *members; 24 | @property (strong, nonatomic) NSString *groupName; 25 | @property (nonatomic, copy) void (^completionCallback)(ChatGroup *group, BOOL canceled); 26 | 27 | @property (strong, nonatomic) id modalCallerDelegate; 28 | 29 | @property (weak, nonatomic) IBOutlet UIBarButtonItem *createButton; 30 | @property (weak, nonatomic) IBOutlet UITableView *tableView; 31 | @property (weak, nonatomic) IBOutlet UISearchBar *searchBar; 32 | - (IBAction)createGroupAction:(id)sender; 33 | 34 | @property (strong, nonatomic) NSString *searchBarPlaceholder; 35 | @property (strong, nonatomic) NSString *textToSearch; 36 | @property (strong, nonatomic) NSTimer *searchTimer; 37 | @property (strong, nonatomic) NSString *lastUsersTextSearch; 38 | 39 | @property (strong, nonatomic) UIImage *profileImage; 40 | 41 | @property (strong, nonatomic) ChatSelectGroupMembersCellConfigurator *cellConfigurator; 42 | 43 | -(void)removeButtonPressed:(id)sender; 44 | @end 45 | -------------------------------------------------------------------------------- /Chat21UI/util/ChatStyles.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatStyles.m 3 | // tiledesk 4 | // 5 | // Created by Andrea Sponziello on 03/08/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatStyles.h" 10 | 11 | static ChatStyles *sharedInstance = nil; 12 | 13 | @implementation ChatStyles 14 | 15 | -(id)init 16 | { 17 | if (self = [super init]) 18 | { 19 | self.ballonLeftTextColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:1.0]; 20 | self.ballonLeftBackgroundColor = [UIColor colorWithRed:0.945 green:0.945 blue:0.945 alpha:1.0]; 21 | self.ballonLeftLinkColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:1.0]; 22 | self.linkLeftHLBackgroundColor = [UIColor colorWithRed:0 green:0 blue:1 alpha:1.0]; 23 | self.linkLeftHLTextColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:1.0]; 24 | 25 | self.ballonRightBackgroundColor = [UIColor colorWithRed:0.207 green:0.525 blue:0.968 alpha:1.0]; 26 | self.ballonRightTextColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:1.0]; 27 | self.ballonRightLinkColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:1.0]; 28 | self.linkRightHLBackgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:1.0]; 29 | self.linkRightHLTextColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:1.0]; 30 | 31 | self.ballonFont = [UIFont fontWithName:@"Arial" size:20]; 32 | self.lastMessageTextColor = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0]; 33 | self.lastMessageIsNewTextColor = [UIColor blackColor]; 34 | self.lastMessageTextColor = self.lastMessageTextColor; 35 | } 36 | return self; 37 | } 38 | 39 | +(ChatStyles *)sharedInstance { 40 | if (!sharedInstance) { 41 | sharedInstance = [[super alloc] init]; 42 | } 43 | return sharedInstance; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Chat21UI/QBPopupMenu/QBPopupMenuItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // QBPopupMenuItem.m 3 | // QBPopupMenu 4 | // 5 | // Created by Tanaka Katsuma on 2013/11/22. 6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved. 7 | // 8 | 9 | #import "QBPopupMenuItem.h" 10 | 11 | @interface QBPopupMenuItem () 12 | 13 | @property (nonatomic, weak, readwrite) id target; 14 | @property (nonatomic, assign, readwrite) SEL action; 15 | 16 | @property (nonatomic, copy, readwrite) NSString *title; 17 | @property (nonatomic, copy, readwrite) UIImage *image; 18 | 19 | @end 20 | 21 | @implementation QBPopupMenuItem 22 | 23 | + (instancetype)itemWithTitle:(NSString *)title target:(id)target action:(SEL)action 24 | { 25 | return [[self alloc] initWithTitle:title target:target action:action]; 26 | } 27 | 28 | + (instancetype)itemWithImage:(UIImage *)image target:(id)target action:(SEL)action 29 | { 30 | return [[self alloc] initWithImage:image target:target action:action]; 31 | } 32 | 33 | + (instancetype)itemWithTitle:(NSString *)title image:(UIImage *)image target:(id)target action:(SEL)action 34 | { 35 | return [[self alloc] initWithTitle:title image:image target:target action:action]; 36 | } 37 | 38 | - (instancetype)initWithTitle:(NSString *)title target:(id)target action:(SEL)action 39 | { 40 | return [self initWithTitle:title image:nil target:target action:action]; 41 | } 42 | 43 | - (instancetype)initWithImage:(UIImage *)image target:(id)target action:(SEL)action 44 | { 45 | return [self initWithTitle:nil image:image target:target action:action]; 46 | } 47 | 48 | - (instancetype)initWithTitle:(NSString *)title image:(UIImage *)image target:(id)target action:(SEL)action 49 | { 50 | self = [super init]; 51 | 52 | if (self) { 53 | self.target = target; 54 | self.action = action; 55 | 56 | self.title = title; 57 | self.image = image; 58 | } 59 | 60 | return self; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatMessagesTVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatMessagesTVC.h 3 | // Chat21 4 | // 5 | // Created by Dario De Pascalis on 22/03/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "QBPopupMenu.h" 11 | #import "NYTPhotosViewController.h" 12 | 13 | 14 | @class ChatMessagesVC; 15 | @class ChatMessage; 16 | @class ChatConversationHandler; 17 | @class QBPopupMenu; 18 | @class ChatImageCache; 19 | 20 | @interface ChatMessagesTVC : UITableViewController{ 21 | // for activity 22 | } 23 | @property (weak, nonatomic) ChatMessagesVC *vc; 24 | @property (weak, nonatomic) ChatConversationHandler *conversationHandler; 25 | @property (strong, nonatomic) NSMutableDictionary *rowHeights; 26 | @property (strong, nonatomic) NSMutableDictionary *rowComponents; 27 | @property (strong, nonatomic) NSTimer *highlightTimer; 28 | @property (strong, nonatomic) UILabel *selectedHighlightLabel; 29 | @property (assign, nonatomic) BOOL right_cell_hl; 30 | @property (assign, nonatomic) NSRange selectedHighlightRange; 31 | @property (strong, nonatomic) NSString *selectedHighlightLink; 32 | @property (strong, nonatomic) NSString *selectedImageURL; 33 | @property (strong, nonatomic) ChatImageCache *imageCache; 34 | //@property (strong, nonatomic) UIActionSheet *linkMenu; 35 | 36 | @property (strong, nonatomic) QBPopupMenu *popupMenu; 37 | @property (strong, nonatomic) NSString *selectedText; 38 | @property (strong, nonatomic) ChatMessage *selectedMessage; 39 | 40 | - (void)reloadDataTableView; 41 | - (void)reloadDataTableViewOnIndex:(NSInteger)index; 42 | - (void)scrollToLastMessage:(BOOL)animated; 43 | 44 | // events 45 | -(void)messageUpdated:(ChatMessage *)message; 46 | -(void)messageDeleted:(ChatMessage *)message; 47 | // TODO messageReceived (move from VC to TVC) 48 | //-(void)messageReceived:(ChatMessage *)message; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /Chat21Core/lib/ChatGroupsHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatGroupsHandler.h 3 | // Smart21 4 | // 5 | // Created by Andrea Sponziello on 02/05/15. 6 | // 7 | // 8 | 9 | #import 10 | #import "ChatGroupsSubscriber.h" 11 | 12 | @import Firebase; 13 | 14 | @class FirebaseCustomAuthHelper; 15 | @class Firebase; 16 | @class ChatUser; 17 | @class ChatGroup; 18 | 19 | @interface ChatGroupsHandler : NSObject 20 | 21 | @property (strong, nonatomic) ChatUser * _Nullable loggeduser; 22 | @property (strong, nonatomic) NSString *me; 23 | @property (strong, nonatomic) FirebaseCustomAuthHelper *authHelper; 24 | @property (strong, nonatomic) NSMutableDictionary *groups; 25 | //@property (strong, nonatomic) NSMutableDictionary *groupsDictionary; // easy search by group_id 26 | 27 | //@property (strong, nonatomic) NSMutableArray *groups; 28 | @property (strong, nonatomic) NSString *firebaseToken; 29 | @property (strong, nonatomic) FIRDatabaseReference *groupsRef; 30 | @property (assign, nonatomic) FIRDatabaseHandle groups_ref_handle_added; 31 | @property (assign, nonatomic) FIRDatabaseHandle groups_ref_handle_changed; 32 | @property (assign, nonatomic) FIRDatabaseHandle groups_ref_handle_removed; 33 | //@property (strong, nonatomic) NSString *firebaseRef; 34 | @property (nonatomic, strong) FIRDatabaseReference *rootRef; 35 | @property (strong, nonatomic) NSString *tenant; 36 | @property (strong, nonnull) NSMutableArray> *subscribers; 37 | 38 | //-(id)initWithFirebaseRef:(NSString *)firebaseRef tenant:(NSString *)tenant user:(SHPUser *)user; 39 | -(id)initWithTenant:(NSString *)tenant user:(ChatUser *)user; 40 | -(void)restoreGroupsFromDB; 41 | -(void)connect; 42 | -(void)dispose; 43 | -(ChatGroup *)groupById:(NSString *)groupId; 44 | //-(void)insertOrUpdateGroup:(ChatGroup *)group; 45 | -(void)insertOrUpdateGroup:(ChatGroup *)group completion:(void(^)()) callback; 46 | -(void)insertInMemory:(ChatGroup *)group; 47 | //+(void)createGroupFromPushNotification:(ChatGroup *)group; 48 | -(void)addSubscriber:(id)subscriber; 49 | -(void)removeSubscriber:(id)subscriber; 50 | @end 51 | -------------------------------------------------------------------------------- /Chat21Core/model/ChatGroup.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatGroup.h 3 | // Smart21 4 | // 5 | // Created by Andrea Sponziello on 27/03/15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | static NSString* const NOTIFICATION_TYPE_MEMBER_ADDED_TO_GROUP = @"group_member_added"; 12 | static NSString* const GROUP_OWNER = @"owner"; 13 | static NSString* const GROUP_CREATEDON = @"createdOn"; 14 | static NSString* const GROUP_NAME = @"name"; 15 | static NSString* const GROUP_MEMBERS = @"members"; 16 | static NSString* const GROUP_ICON_ID = @"iconID"; 17 | 18 | @import Firebase; 19 | 20 | @class FDataSnapshot; 21 | @class HelloApplicationContext; 22 | @class ChatUser; 23 | 24 | @interface ChatGroup : NSObject 25 | 26 | @property (nonatomic, strong) NSString *key; 27 | //@property (nonatomic, strong) Firebase *ref; 28 | @property (nonatomic, strong) NSString *groupId; 29 | @property (nonatomic, strong) NSString *tempId; 30 | @property (nonatomic, strong) NSString *user; // used to query groups on local DB ( DB partioning by user ) 31 | @property (nonatomic, strong) NSString *name; 32 | @property (nonatomic, strong) NSString *owner; 33 | //@property (nonatomic, strong) NSString *iconID; 34 | @property (nonatomic, strong) NSDate *createdOn; 35 | @property (nonatomic, strong) NSMutableDictionary *members; 36 | @property (nonatomic, strong) NSArray *membersFull; 37 | //@property (assign, nonatomic) BOOL completeData; 38 | @property (assign, nonatomic) BOOL imAdmin; 39 | 40 | //-(NSString *)iconUrl; 41 | -(FIRDatabaseReference *)reference; 42 | -(NSString *)memberPath:(NSString *)memberId; 43 | -(FIRDatabaseReference *)memberReference:(NSString *)memberId; 44 | -(BOOL)isMember:(NSString *)user_id; 45 | -(NSMutableDictionary *)asDictionary; 46 | +(NSMutableDictionary *)membersArray2Dictionary:(NSArray *)membersIds; 47 | +(NSMutableArray *)membersDictionary2Array:(NSDictionary *)membersDict; 48 | +(NSString *)membersDictionary2String:(NSDictionary *)membersDictionary; 49 | +(NSMutableDictionary *)membersString2Dictionary:(NSString *)membersString; 50 | -(NSString *)ownerFullname; 51 | -(void)completeGroupMembersMetadataWithCompletionBlock:(void(^)()) callback; 52 | -(id)initWithGroupId:(NSString *)groupId name:(NSString *)name; 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /Chat21Core/util/ChatUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatUtil.h 3 | // Soleto 4 | // 5 | // Created by Andrea Sponziello on 02/12/14. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @import UIKit; 12 | @class Firebase; 13 | @class ChatNotificationView; 14 | @class ChatUser; 15 | @class ChatDiskImageCache; 16 | 17 | @import Firebase; 18 | 19 | @interface ChatUtil : NSObject 20 | 21 | +(FIRDatabaseReference *)conversationRefForUser:(NSString *)userId conversationId:(NSString *)conversationId; 22 | +(FIRDatabaseReference *)conversationMessagesRef:(NSString *)recipient_id; 23 | 24 | // firebase paths 25 | +(NSString *)conversationPathForUser:(NSString *)user_id conversationId:(NSString *)conversationId; 26 | +(NSString *)conversationsPathForUserId:(NSString *)user_id; 27 | +(NSString *)archivedConversationsPathForUserId:(NSString *)user_id; 28 | +(NSString *)contactsPath; 29 | +(NSString *)contactPathOfUser:(NSString *)userid; 30 | +(NSString *)groupsPath; 31 | +(NSString *)mainGroupsPath; 32 | 33 | +(NSMutableDictionary *)groupMembersAsDictionary:(NSArray *)membersArray; 34 | +(NSMutableArray *)groupMembersAsArray:(NSDictionary *)membersDictionary; 35 | +(NSString *)groupMembersAsStringForUI:(NSDictionary *)membersDictionary; 36 | +(NSString *)groupMembersFullnamesAsStringForUI:(NSArray *)members; 37 | +(NSString *)randomString:(NSInteger)length; 38 | 39 | +(NSString *)groupImagesRelativePath; 40 | +(NSString *)groupImageDownloadUrl; 41 | +(NSString *)groupImageDeleteUrl; 42 | //+(NSString *)groupImageUrlById:(NSString *)imageID; 43 | //+(NSString *)imageIDFilename:(NSString *)imageID; 44 | +(NSString *)userPath:(NSString *)userId; 45 | +(NSString *)sanitizedNode:(NSString *)node_name; 46 | +(NSString *)sanitizedUserId:(NSString *)userId; 47 | 48 | // *** Strings *** 49 | 50 | +(NSString *)timeFromNowToStringFormattedForConversation:(NSDate *)date; 51 | +(BOOL)isYesterday:(NSDate *)date; 52 | + (NSInteger)daysBetweenDate:(NSDate*)fromDateTime andDate:(NSDate*)toDateTime; 53 | 54 | +(NSString *)absoluteFolderPath:(NSString *)folderName; 55 | 56 | // *** Images *** 57 | +(UIImage *)circleImage:(UIImage *)image; 58 | 59 | //+(void)updateProfileInCache:(ChatDiskImageCache *)imageCache profile:(NSString *)profileId image:(UIImage *)image; 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /Chat21UI/ChatUIManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatUIManager.h 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 06/12/2017. 6 | // Copyright © 2017 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | @class ChatUser; 12 | @class ChatMessagesVC; 13 | @class ChatGroup; 14 | @class ChatMessage; 15 | 16 | @interface ChatUIManager : NSObject 17 | 18 | @property (nonatomic, copy) void (^pushProfileCallback)(ChatUser *user, ChatMessagesVC *vc); 19 | 20 | +(ChatUIManager *)getInstance; 21 | -(void)openConversationsViewAsModal:(UIViewController *)vc withCompletionBlock:(void (^)())completionBlock; 22 | -(void)openConversationMessagesViewAsModalWith:(ChatUser *)recipient viewController:(UIViewController *)vc attributes:(NSDictionary *)attributes withCompletionBlock:(void (^)())completionBlock; 23 | -(void)openSelectContactViewAsModal:(UIViewController *)vc withCompletionBlock:(void (^)(ChatUser *contact, BOOL canceled))completionBlock; 24 | -(void)openCreateGroupViewAsModal:(UIViewController *)vc withCompletionBlock:(void (^)(ChatGroup *group, BOOL canceled))completionBlock; 25 | -(void)openSelectGroupViewAsModal:(UIViewController *)vc withCompletionBlock:(void (^)(ChatGroup *group, BOOL canceled))completionBlock; 26 | -(void)pushArchivedConversationsView:(UIViewController *)vc; 27 | -(UINavigationController *)getSelectContactViewController; 28 | -(UINavigationController *)getConversationsViewController; 29 | -(UINavigationController *)getMessagesViewController; 30 | -(UINavigationController *)getCreateGroupViewController; 31 | -(UINavigationController *)getSelectGroupViewController; 32 | 33 | @property (assign, nonatomic) NSInteger tabBarIndex; 34 | 35 | // this methods work only with a tabbed application and Chat-Info.plist > tabbar-index property correctly configured to the tab index containing the ConversationsView 36 | +(void)moveToConversationViewWithUser:(ChatUser *)user; 37 | +(void)moveToConversationViewWithUser:(ChatUser *)user sendMessage:(NSString *)message; 38 | +(void)moveToConversationViewWithGroup:(ChatGroup *)group; 39 | +(void)moveToConversationViewWithGroup:(ChatGroup *)group sendMessage:(NSString *)message; 40 | +(void)moveToConversationViewWithUser:(ChatUser *)user orGroup:(ChatGroup *)group sendMessage:(NSString *)message attributes:(NSDictionary *)attributes; 41 | 42 | +(void)showNotificationWithMessage:(NSString *)message image:(UIImage *)image sender:(NSString *)sender senderFullname:(NSString *)senderFullname; 43 | 44 | @end 45 | 46 | -------------------------------------------------------------------------------- /Chat21UI/QBPopupMenu/QBPopupMenuOverlayView.m: -------------------------------------------------------------------------------- 1 | // 2 | // QBPopupMenuOverlayView.m 3 | // QBPopupMenu 4 | // 5 | // Created by Tanaka Katsuma on 2013/11/24. 6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved. 7 | // 8 | 9 | #import "QBPopupMenuOverlayView.h" 10 | 11 | #import "QBPopupMenu.h" 12 | 13 | @implementation QBPopupMenuOverlayView 14 | 15 | - (instancetype)initWithFrame:(CGRect)frame 16 | { 17 | self = [super initWithFrame:frame]; 18 | 19 | if (self) { 20 | self.backgroundColor = [UIColor clearColor]; 21 | UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapReceived:)]; 22 | [tapGestureRecognizer setDelegate:self]; 23 | tapGestureRecognizer.cancelsTouchesInView = NO; 24 | [self addGestureRecognizer:tapGestureRecognizer]; 25 | 26 | UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(tapReceived:)]; 27 | [panGestureRecognizer setDelegate:self]; 28 | panGestureRecognizer.cancelsTouchesInView = NO; 29 | [self addGestureRecognizer:panGestureRecognizer]; 30 | 31 | } 32 | 33 | return self; 34 | } 35 | 36 | -(void)tapReceived:(UITapGestureRecognizer *)tapGestureRecognizer 37 | { 38 | NSLog(@".......TAPPED!!!"); 39 | [self.popupMenu dismissAnimated:YES]; 40 | } 41 | 42 | //- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 43 | //{ 44 | // NSLog(@"TOUCHED!!!"); 45 | // UITouch *touch = [touches anyObject]; 46 | // UIView *view = touch.view; 47 | // 48 | // if (view == self) { 49 | // // Close popup menu 50 | // [self.popupMenu dismissAnimated:YES]; 51 | // } 52 | //} 53 | 54 | //- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 55 | // NSLog(@"BEGAN!!!"); 56 | // UITouch *touch = [touches anyObject]; 57 | // UIView *view = touch.view; 58 | // 59 | // if (view == self) { 60 | // // Close popup menu 61 | // [self.popupMenu dismissAnimated:YES]; 62 | // } 63 | // 64 | //} 65 | 66 | 67 | //- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 68 | // NSLog(@"MOVED!!!"); 69 | // UITouch *touch = [touches anyObject]; 70 | // UIView *view = touch.view; 71 | // 72 | // if (view == self) { 73 | // NSLog(@"CLOSING MOVED!!!"); 74 | // // Close popup menu 75 | // [self.popupMenu dismissAnimated:YES]; 76 | // } 77 | //} 78 | 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /Chat21Core/lib/ChatConversationsHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatConversationsHandler.h 3 | // Soleto 4 | // 5 | // Created by Andrea Sponziello on 29/12/14. 6 | // 7 | 8 | #import 9 | //#import "SHPConversationsViewDelegate.h" 10 | #import "ChatEventType.h" 11 | 12 | @import Firebase; 13 | 14 | @class ChatUser; 15 | @class ChatConversation; 16 | 17 | @interface ChatConversationsHandler : NSObject 18 | 19 | @property (strong, nonatomic) ChatUser *loggeduser; 20 | @property (strong, nonatomic) NSString *me; 21 | //@property (strong, nonatomic) FirebaseCustomAuthHelper *authHelper; 22 | @property (strong, nonatomic) NSMutableArray *conversations; 23 | @property (strong, nonatomic) NSMutableArray *archivedConversations; 24 | @property (strong, nonatomic) NSString *firebaseToken; 25 | @property (strong, nonatomic) FIRDatabaseReference *conversationsRef; 26 | @property (strong, nonatomic) FIRDatabaseReference *archivedConversationsRef; 27 | @property (assign, nonatomic) FIRDatabaseHandle conversations_ref_handle_added; 28 | @property (assign, nonatomic) FIRDatabaseHandle conversations_ref_handle_changed; 29 | @property (assign, nonatomic) FIRDatabaseHandle conversations_ref_handle_removed; 30 | @property (assign, nonatomic) FIRDatabaseHandle archived_conversations_ref_handle_added; 31 | @property (assign, nonatomic) FIRDatabaseHandle archived_conversations_ref_handle_removed; 32 | //@property (assign, nonatomic) id delegateView; 33 | @property (strong, nonatomic) NSString *currentOpenConversationId; 34 | @property (nonatomic, strong) FIRDatabaseReference *rootRef; 35 | @property (strong, nonatomic) NSString *tenant; 36 | 37 | // observer 38 | @property (strong, nonatomic) NSMutableDictionary *eventObservers; // ( event_enum : DictionaryOfCallbacks (event_handle : event_callback) ) 39 | @property (assign, atomic) volatile int64_t lastEventHandler; 40 | -(NSUInteger)observeEvent:(ChatConversationEventType)eventType withCallback:(void (^)(ChatConversation *conversation))callback; 41 | -(void)removeObserverWithHandle:(NSUInteger)event_handler; 42 | -(void)removeAllObservers; 43 | 44 | -(id)initWithTenant:(NSString *)tenant user:(ChatUser *)user; 45 | -(void)connect; 46 | -(void)dispose; 47 | -(void)restoreConversationsFromDB; 48 | -(void)updateLocalConversation:(ChatConversation *)conversation completion:(void(^)(void)) callback; 49 | //-(void)removeLocalConversation:(ChatConversation *)conversation; 50 | -(void)removeLocalConversation:(ChatConversation *)conversation completion:(void(^)(void)) callback; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /Chat21Core/DB/ChatDB.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatDB.h 3 | // Soleto 4 | // 5 | // Created by Andrea Sponziello on 05/12/14. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @class ChatMessage; 13 | @class ChatConversation; 14 | @class ChatGroup; 15 | //@class ChatUser; 16 | 17 | @interface ChatDB : NSObject 18 | { 19 | NSString *databasePath; 20 | } 21 | 22 | @property (assign, nonatomic) BOOL logQuery; 23 | 24 | +(ChatDB*)getSharedInstance; 25 | //-(BOOL)createDB; 26 | -(BOOL)createDBWithName:(NSString *)name; 27 | 28 | // messages 29 | //-(void)insertMessageIfNotExists:(ChatMessage *)message; 30 | //-(BOOL)insertMessage:(ChatMessage *)message; 31 | 32 | // SYNC OK 33 | -(void)updateMessageSynchronized:(NSString *)messageId withStatus:(int)status completion:(void(^)(void)) callback; 34 | //-(BOOL)updateMessage:(NSString *)messageId withStatus:(int)status; 35 | // SYNC TODO 36 | -(BOOL)updateMessage:(NSString *)messageId status:(int)status text:(NSString *)text snapshotAsJSONString:(NSString *)snapshotAsJSONString; 37 | // SYNC OK 38 | -(void)removeAllMessagesForConversationSynchronized:(NSString *)conversationId completion:(void(^)(void)) callback; 39 | // SYNC OK 40 | -(void)insertMessageIfNotExistsSyncronized:(ChatMessage *)message completion:(void(^)(void)) callback; 41 | // SYNC OK 42 | -(void)getMessageByIdSyncronized:(NSString *)messageId completion:(void(^)(ChatMessage *)) callback; 43 | 44 | -(NSArray*)getAllMessages; 45 | -(NSArray*)getAllMessagesForConversation:(NSString *)conversationId start:(int)start count:(int)count; 46 | -(NSArray*)getAllMessagesForConversation:(NSString *)conversationId; 47 | //-(ChatMessage *)getMessageById:(NSString *)messageId; 48 | 49 | 50 | // conversations 51 | 52 | // SYNC OK 53 | -(void)insertOrUpdateConversationSyncronized:(ChatConversation *)conversation completion:(void(^)(void)) callback; 54 | //-(BOOL)insertOrUpdateConversation:(ChatConversation *)conversation; 55 | //-(BOOL)insertConversation:(ChatConversation *)conversation; 56 | //-(BOOL)updateConversation:(ChatConversation *)conversation; 57 | // SYNC OK 58 | - (void)removeConversationSynchronized:(NSString *)conversationId completion:(void(^)(void)) callback; 59 | //-(BOOL)removeConversation:(NSString *)conversationId; 60 | -(NSArray*)getAllConversations; 61 | - (NSArray*)getAllConversationsForUser:(NSString *)user archived:(BOOL)archived limit:(int)limit; 62 | - (void)getConversationByIdSynchronized:(NSString *)conversationId completion:(void(^)(ChatConversation *)) callback; 63 | //-(ChatConversation *)getConversationById:(NSString *)conversationId; 64 | 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /Chat21Core/lib/ChatImageDownloadManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatImageDownloadManager.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 06/05/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatImageDownloadManager.h" 10 | #import "ChatMessage.h" 11 | #import "ChatImageCache.h" 12 | 13 | @implementation ChatImageDownloadManager 14 | 15 | -(id)init { 16 | self = [super init]; 17 | if (self) { 18 | self.tasks = [[NSMutableDictionary alloc] init]; 19 | } 20 | return self; 21 | } 22 | 23 | - (void)downloadImage:(ChatMessage *)message onIndexPath:(NSIndexPath *)indexPath completionHandler:(void(^)(NSIndexPath* indexPath, UIImage *image, NSError *error))callback { 24 | NSURLSessionDataTask *currentTask = [self.tasks objectForKey:message.messageId]; 25 | if (currentTask) { 26 | NSLog(@"Image %@ already downloading (messageId: %@).", message.imageURL, message.messageId); 27 | return; 28 | } 29 | NSURLSessionConfiguration *_config = [NSURLSessionConfiguration defaultSessionConfiguration]; 30 | NSURLSession *_session = [NSURLSession sessionWithConfiguration:_config]; 31 | NSURL *url = [NSURL URLWithString:message.imageURL]; 32 | NSLog(@"Downloading image. URL: %@", message.imageURL); 33 | if (!url) { 34 | NSLog(@"ERROR - Can't download image, URL is null"); 35 | return; 36 | } 37 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; 38 | NSURLSessionDataTask *task = [_session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 39 | NSLog(@"Image downloaded: %@", message.imageURL); 40 | [self.tasks removeObjectForKey:message.messageId]; 41 | if (error) { 42 | NSLog(@"%@", error); 43 | callback(indexPath, nil, error); 44 | return; 45 | } 46 | 47 | if (data) { 48 | UIImage *image = [UIImage imageWithData:data]; 49 | if (image) { 50 | NSData* imageData = [NSData dataWithData:UIImagePNGRepresentation(image)]; 51 | NSString *path = [message imagePathFromMediaFolder]; 52 | NSLog(@"Saving image to: %@", path); 53 | NSError *writeError = nil; 54 | [message createMediaFolderPathIfNotExists]; 55 | if(![imageData writeToFile:path options:NSDataWritingAtomic error:&writeError]) { 56 | NSLog(@"%@: Error saving image: %@", [self class], [writeError localizedDescription]); 57 | } 58 | dispatch_async(dispatch_get_main_queue(), ^{ 59 | callback(indexPath, image, nil); 60 | }); 61 | } 62 | } 63 | }]; 64 | [self.tasks setObject:task forKey:message.messageId]; 65 | [task resume]; 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatChangeGroupNameVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatChangeGroupNameVC.m 3 | // Chat21 4 | // 5 | // Created by Andrea Sponziello on 28/04/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatChangeGroupNameVC.h" 10 | #import "ChatGroup.h" 11 | #import "ChatManager.h" 12 | #import "ChatLocal.h" 13 | 14 | @interface ChatChangeGroupNameVC () 15 | 16 | @end 17 | 18 | @implementation ChatChangeGroupNameVC 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view. 23 | 24 | NSLog(@"group %@", self.group); 25 | self.navigationItem.title = [ChatLocal translate:@"GroupNameTitle"]; 26 | self.saveButton.title = [ChatLocal translate:@"ChatSave"]; 27 | self.cancelButton.title = [ChatLocal translate:@"ChatCancel"]; 28 | self.groupNameTextField.placeholder = [ChatLocal translate:@"GroupNamePlaceholder"]; 29 | self.groupNameTextField.text = self.group.name; 30 | self.navigationItem.rightBarButtonItem.enabled = YES; 31 | [self addControlChangeTextField:self.groupNameTextField]; 32 | [self.groupNameTextField becomeFirstResponder]; 33 | } 34 | 35 | -(void)addControlChangeTextField:(UITextField *)textField 36 | { 37 | [textField addTarget:self 38 | action:@selector(textFieldDidChange:) 39 | forControlEvents:UIControlEventEditingChanged]; 40 | } 41 | // 42 | 43 | 44 | -(void)textFieldDidChange:(UITextField *)textField { 45 | NSString *text = [textField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 46 | if ([text length] > 0) { 47 | self.navigationItem.rightBarButtonItem.enabled = YES; 48 | } 49 | else { 50 | self.navigationItem.rightBarButtonItem.enabled = NO; 51 | } 52 | } 53 | 54 | - (void)didReceiveMemoryWarning { 55 | [super didReceiveMemoryWarning]; 56 | // Dispose of any resources that can be recreated. 57 | } 58 | 59 | /* 60 | #pragma mark - Navigation 61 | 62 | // In a storyboard-based application, you will often want to do a little preparation before navigation 63 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 64 | // Get the new view controller using [segue destinationViewController]. 65 | // Pass the selected object to the new view controller. 66 | } 67 | */ 68 | 69 | - (IBAction)cancelAction:(id)sender { 70 | [self performSegueWithIdentifier:@"unwindToGroupInfoVC" sender:self]; 71 | } 72 | 73 | - (IBAction)saveAction:(id)sender { 74 | NSLog(@"saving..."); 75 | NSString *text = [self.groupNameTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 76 | ChatManager *chatm = [ChatManager getInstance]; 77 | [chatm updateGroupName:text forGroup:self.group withCompletionBlock:^(NSError *error) { 78 | if (!error) { 79 | NSLog(@"Group name successfully changed."); 80 | [self performSegueWithIdentifier:@"unwindToGroupInfoVC" sender:self]; 81 | } else { 82 | NSLog(@"Problems updating group name."); 83 | } 84 | 85 | }]; 86 | } 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /Chat21Core/model/ChatConversation.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatConversation.h 3 | // Soleto 4 | // 5 | // Created by Andrea Sponziello on 22/11/14. 6 | // 7 | // 8 | 9 | #import 10 | 11 | static int const CONV_STATUS_FAILED = -1000; 12 | static int const CONV_STATUS_JUST_CREATED = -900; // for group management 13 | static int const CONV_STATUS_LAST_MESSAGE = 0; 14 | 15 | static NSString* const CONV_LAST_MESSAGE_TEXT_KEY = @"last_message_text"; 16 | static NSString* const CONV_RECIPIENT_KEY = @"recipient"; 17 | static NSString* const CONV_SENDER_KEY = @"sender"; 18 | static NSString* const CONV_SENDER_FULLNAME_KEY = @"sender_fullname"; 19 | static NSString* const CONV_RECIPIENT_FULLNAME_KEY = @"recipient_fullname"; 20 | static NSString* const CONV_TIMESTAMP_KEY = @"timestamp"; 21 | static NSString* const CONV_IS_NEW_KEY = @"is_new"; 22 | static NSString* const CONV_CONVERS_WITH_KEY = @"convers_with"; 23 | //static NSString* const CONV_CONVERS_WITH_FULLNAME_KEY = @"convers_with_fullname"; 24 | //static NSString* const CONV_GROUP_ID_KEY = @"group_id"; 25 | //static NSString* const CONV_GROUP_NAME_KEY = @"group_name"; 26 | static NSString* const CONV_CHANNEL_TYPE_KEY = @"channel_type"; 27 | static NSString* const CONV_STATUS_KEY = @"status"; 28 | static NSString* const CONV_ATTRIBUTES_KEY = @"attributes"; 29 | 30 | @import Firebase; 31 | @class ChatUser; 32 | 33 | //@class Firebase; 34 | //@class FDataSnapshot; 35 | 36 | @interface ChatConversation : NSObject 37 | 38 | @property (nonatomic, strong) NSString *key; 39 | @property (nonatomic, strong) FIRDatabaseReference *ref; 40 | @property (nonatomic, strong) NSString *conversationId; 41 | @property (nonatomic, strong) NSString *user; // used to query conversations on local DB 42 | @property (nonatomic, strong) NSString *last_message_text; 43 | @property (nonatomic, assign) BOOL is_new; 44 | @property (nonatomic, assign) BOOL archived; 45 | @property (nonatomic, strong) NSDate *date; 46 | @property (nonatomic, strong) NSString *sender; 47 | @property (nonatomic, strong) NSString *senderFullname; 48 | @property (nonatomic, strong) NSString *recipient; 49 | @property (nonatomic, strong) NSString *recipientFullname; 50 | @property (nonatomic, strong) NSString *conversWith; 51 | @property (nonatomic, strong) NSString *conversWith_fullname; 52 | @property (nonatomic, strong) NSString *channel_type; 53 | @property (nonatomic, strong) NSString *thumbImageURL; 54 | @property (nonatomic, assign) int status; 55 | @property (nonatomic, assign) int indexInMemory; 56 | @property (nonatomic, strong) NSDictionary *attributes; // firebase 57 | @property (nonatomic, strong, nonnull) NSString *mtype; // firebase 58 | 59 | @property (nonatomic, strong) NSDictionary * _Nullable snapshot; 60 | @property (nonatomic, strong) NSString * _Nullable snapshotAsJSONString; 61 | 62 | @property (nonatomic, assign) BOOL isDirect; 63 | 64 | -(NSString *_Nullable)dateFormattedForListView; 65 | 66 | -(NSString *_Nullable)textForLastMessage:(NSString *_Nullable)me; 67 | 68 | +(ChatConversation *_Nullable)conversationFromSnapshotFactory:(FIRDataSnapshot *_Nonnull)snapshot me:(ChatUser *_Nonnull)me; 69 | 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /Chat21Core/model/ChatUser.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatUser.m 3 | // 4 | // Created by Andrea Sponziello on 01/09/2017. 5 | // Copyright © 2017 Frontiere21. All rights reserved. 6 | // 7 | 8 | #import "ChatUser.h" 9 | #import "ChatUtil.h" 10 | #import "ChatManager.h" 11 | 12 | @implementation ChatUser 13 | 14 | -(id)init { 15 | if (self = [super init]) { 16 | self.imageurl = @""; 17 | self.lastname = @""; 18 | self.firstname = @""; 19 | self.userId = nil; 20 | } 21 | return self; 22 | } 23 | 24 | -(id)init:(NSString *)userid fullname:(NSString *)fullname { 25 | if (self = [super init]) { 26 | self.imageurl = @""; 27 | self.lastname = @""; 28 | self.firstname = @""; 29 | _fullname = fullname; 30 | self.userId = userid; 31 | } 32 | return self; 33 | } 34 | 35 | // Fullname custom getter 36 | - (NSString*) fullname { 37 | if (!_fullname) { 38 | NSString *__firstName = self.firstname ? self.firstname : @""; 39 | NSString *__lastName = self.lastname ? self.lastname : @""; 40 | NSString *fullname = [NSString stringWithFormat:@"%@ %@", __firstName, __lastName]; 41 | return fullname; 42 | } 43 | else { 44 | return _fullname; 45 | } 46 | } 47 | 48 | -(NSDictionary *)asDictionary { 49 | NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; 50 | if (self.userId) { 51 | [dict setObject:self.userId forKey:FIREBASE_USER_ID]; 52 | } 53 | if (self.firstname) { 54 | [dict setObject:self.firstname forKey:FIREBASE_USER_FIRSTNAME]; 55 | } 56 | if (self.lastname) { 57 | [dict setObject:self.lastname forKey:FIREBASE_USER_LASTNAME]; 58 | } 59 | if (self.email) { 60 | [dict setObject:self.email forKey:FIREBASE_USER_EMAIL]; 61 | } 62 | if (self.imageurl) { 63 | [dict setObject:self.imageurl forKey:FIREBASE_USER_IMAGEURL]; 64 | } 65 | 66 | return dict; 67 | } 68 | 69 | -(NSDate *)createdonAsDate { 70 | return [NSDate dateWithTimeIntervalSince1970:self.createdon]; 71 | } 72 | 73 | - (BOOL)isEqual:(id)other { 74 | if (other == self) 75 | return YES; 76 | if (!other || ![other isKindOfClass:[self class]]) 77 | return NO; 78 | return [self isEqualToUser:other]; 79 | } 80 | 81 | - (BOOL)isEqualToUser:(ChatUser *)user { 82 | if (self == user) 83 | return YES; 84 | if (![(id)[self lastname] isEqual:[user lastname]]) 85 | return NO; 86 | if (![[self firstname] isEqual:[user firstname]]) 87 | return NO; 88 | if (![[self email] isEqual:[user email]]) 89 | return NO; 90 | // if (![[self imageurl] isEqual:[user imageurl]]) 91 | // return NO; 92 | if (![[self userId] isEqual:[user userId]]) 93 | return NO; 94 | return YES; 95 | } 96 | 97 | -(NSString *)profileImagePath { 98 | return [ChatManager profileImagePathOf:self.userId]; 99 | } 100 | 101 | -(NSString *)profileImageURL { 102 | return [ChatManager profileImageURLOf:self.userId]; 103 | } 104 | 105 | -(NSString *)profileThumbImageURL { 106 | return [ChatManager profileThumbImageURLOf:self.userId]; 107 | } 108 | 109 | @end 110 | -------------------------------------------------------------------------------- /Chat21UI/QBPopupMenu/QBPopupMenu.h: -------------------------------------------------------------------------------- 1 | // 2 | // QBPopupMenu.h 3 | // QBPopupMenu 4 | // 5 | // Created by Tanaka Katsuma on 2013/11/22. 6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "QBPopupMenuItem.h" 12 | 13 | @class QBPopupMenu; 14 | @class QBPopupMenuItemView; 15 | @class QBPopupMenuPagenatorView; 16 | 17 | @protocol QBPopupMenuDelegate 18 | 19 | @optional 20 | - (void)popupMenuWillAppear:(QBPopupMenu *)popupMenu; 21 | - (void)popupMenuDidAppear:(QBPopupMenu *)popupMenu; 22 | - (void)popupMenuWillDisappear:(QBPopupMenu *)popupMenu; 23 | - (void)popupMenuDidDisappear:(QBPopupMenu *)popupMenu; 24 | 25 | @end 26 | 27 | typedef NS_ENUM(NSUInteger, QBPopupMenuArrowDirection) { 28 | QBPopupMenuArrowDirectionDefault, 29 | QBPopupMenuArrowDirectionUp, 30 | QBPopupMenuArrowDirectionDown, 31 | QBPopupMenuArrowDirectionLeft, 32 | QBPopupMenuArrowDirectionRight 33 | }; 34 | 35 | @interface QBPopupMenu : UIView 36 | 37 | @property (nonatomic, weak) id delegate; 38 | 39 | @property (nonatomic, assign, getter = isVisible, readonly) BOOL visible; 40 | @property (nonatomic, copy) NSArray *items; 41 | @property (nonatomic, assign) CGFloat height; 42 | @property (nonatomic, assign) CGFloat cornerRadius; 43 | @property (nonatomic, assign) CGFloat arrowSize; 44 | @property (nonatomic, assign) QBPopupMenuArrowDirection arrowDirection; 45 | @property (nonatomic, assign) UIEdgeInsets popupMenuInsets; 46 | @property (nonatomic, assign) CGFloat margin; 47 | 48 | @property (nonatomic, strong) UIColor *color; 49 | @property (nonatomic, strong) UIColor *highlightedColor; 50 | 51 | // sponzy 52 | @property (nonatomic, assign) float navigationBarHeight; 53 | @property (nonatomic, assign) float statusBarHeight; 54 | 55 | + (instancetype)popupMenuWithItems:(NSArray *)items; 56 | - (instancetype)initWithItems:(NSArray *)items; 57 | 58 | - (void)showInView:(UIView *)view targetRect:(CGRect)targetRect animated:(BOOL)animated; 59 | - (void)dismissAnimated:(BOOL)animated; 60 | - (void)updateWithTargetRect:(CGRect)targetRect; 61 | 62 | // NOTE: When subclassing this class, use these methods to customize the appearance. 63 | + (Class)itemViewClass; 64 | + (Class)pagenatorViewClass; 65 | 66 | - (CGMutablePathRef)arrowPathInRect:(CGRect)rect direction:(QBPopupMenuArrowDirection)direction CF_RETURNS_RETAINED; 67 | - (CGMutablePathRef)headPathInRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius CF_RETURNS_RETAINED; 68 | - (CGMutablePathRef)tailPathInRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius CF_RETURNS_RETAINED; 69 | - (CGMutablePathRef)bodyPathInRect:(CGRect)rect CF_RETURNS_RETAINED; 70 | 71 | - (void)drawArrowAtPoint:(CGPoint)point arrowSize:(CGFloat)arrowSize arrowDirection:(QBPopupMenuArrowDirection)arrowDirection highlighted:(BOOL)highlighted; 72 | - (void)drawArrowInRect:(CGRect)rect direction:(QBPopupMenuArrowDirection)direction highlighted:(BOOL)highlighted; 73 | - (void)drawHeadInRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius highlighted:(BOOL)highlighted; 74 | - (void)drawTailInRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius highlighted:(BOOL)highlighted; 75 | - (void)drawBodyInRect:(CGRect)rect firstItem:(BOOL)firstItem lastItem:(BOOL)lastItem highlighted:(BOOL)highlighted; 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatInfoMessageAttributesTVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatInfoMessageAttributesTVC.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 05/04/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatInfoMessageAttributesTVC.h" 10 | 11 | @interface ChatInfoMessageAttributesTVC () { 12 | NSArray *allKeys; 13 | } 14 | 15 | @end 16 | 17 | @implementation ChatInfoMessageAttributesTVC 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | allKeys = self.attributes.allKeys; 22 | self.title = @"Attributes"; 23 | } 24 | 25 | - (void)didReceiveMemoryWarning { 26 | [super didReceiveMemoryWarning]; 27 | // Dispose of any resources that can be recreated. 28 | } 29 | 30 | #pragma mark - Table view data source 31 | 32 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 33 | return 1; 34 | } 35 | 36 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 37 | return allKeys.count; 38 | } 39 | 40 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 41 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"property-cell" forIndexPath:indexPath]; 42 | NSString *key = allKeys[indexPath.row]; 43 | id value = self.attributes[key]; 44 | UILabel *key_label = [cell viewWithTag:10]; 45 | UILabel *value_label = [cell viewWithTag:20]; 46 | key_label.text = key; 47 | value_label.text = [[NSString alloc] initWithFormat:@"%@", value]; 48 | return cell; 49 | } 50 | 51 | /* 52 | // Override to support conditional editing of the table view. 53 | - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { 54 | // Return NO if you do not want the specified item to be editable. 55 | return YES; 56 | } 57 | */ 58 | 59 | /* 60 | // Override to support editing the table view. 61 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { 62 | if (editingStyle == UITableViewCellEditingStyleDelete) { 63 | // Delete the row from the data source 64 | [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; 65 | } else if (editingStyle == UITableViewCellEditingStyleInsert) { 66 | // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view 67 | } 68 | } 69 | */ 70 | 71 | /* 72 | // Override to support rearranging the table view. 73 | - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { 74 | } 75 | */ 76 | 77 | /* 78 | // Override to support conditional rearranging of the table view. 79 | - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { 80 | // Return NO if you do not want the item to be re-orderable. 81 | return YES; 82 | } 83 | */ 84 | 85 | /* 86 | #pragma mark - Navigation 87 | 88 | // In a storyboard-based application, you will often want to do a little preparation before navigation 89 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 90 | // Get the new view controller using [segue destinationViewController]. 91 | // Pass the selected object to the new view controller. 92 | } 93 | */ 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /Chat21Core/util/ChatStringUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatStringUtil.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 07/12/2017. 6 | // Copyright © 2017 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatStringUtil.h" 10 | #import "ChatLocal.h" 11 | 12 | @implementation ChatStringUtil 13 | 14 | +(NSString *)timeFromNowToString:(NSDate *)date { 15 | /* 16 | Model from Facebook 17 | 18 | a few seconds ago 19 | about a minute ago 20 | 15 minutes ago 21 | about one hour ago 22 | 2 hours ago 23 | 23 hours ago 24 | Yesterday at 5:07pm TODO 25 | October 11 26 | */ 27 | NSString *timeMessagePart; 28 | NSString *unitMessagePart; 29 | NSDate *now = [[NSDate alloc] init]; 30 | double nowInSeconds = [now timeIntervalSince1970]; 31 | double startDateInSeconds = [date timeIntervalSince1970]; 32 | double secondsElapsed = nowInSeconds - startDateInSeconds; 33 | if (secondsElapsed < 60) { 34 | timeMessagePart = [ChatLocal translate:@"FewSecondsAgoLKey"]; 35 | unitMessagePart = @""; 36 | } 37 | else if (secondsElapsed >= 60 && secondsElapsed <120) { 38 | timeMessagePart = [ChatLocal translate:@"AboutAMinuteAgoLKey"]; 39 | unitMessagePart = @""; 40 | } 41 | else if (secondsElapsed >= 120 && secondsElapsed <3600) { 42 | int minutes = secondsElapsed / 60.0; 43 | timeMessagePart = [[NSString alloc] initWithFormat:@"%d ", minutes]; 44 | unitMessagePart = [ChatLocal translate:@"MinutesAgoLKey"]; 45 | } 46 | else if (secondsElapsed >=3600 && secondsElapsed < 5400) { 47 | timeMessagePart = [ChatLocal translate:@"AboutAnHourAgoLKey"]; 48 | unitMessagePart = @""; 49 | } 50 | else if (secondsElapsed >= 5400 && secondsElapsed <= 86400) { 51 | int hours = secondsElapsed / 3600.0; 52 | timeMessagePart = [[NSString alloc] initWithFormat:@"%d ", hours]; 53 | unitMessagePart = [ChatLocal translate:@"HoursAgoLKey"]; 54 | } 55 | else { 56 | NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init]; 57 | // http://mobiledevelopertips.com/cocoa/date-formatters-examples-take-2.html 58 | [dateFormat setDateFormat:[ChatLocal translate:@"TimeToStringDateFormat"]]; 59 | NSString *dateString = [[dateFormat stringFromDate:date] capitalizedString]; 60 | // timeMessagePart = [NSString stringWithFormat:@"%@ %@",NSLocalizedString(@"theLKey", nil), dateString]; 61 | timeMessagePart = dateString; 62 | unitMessagePart = @""; 63 | } 64 | NSString *timeString = [[NSString alloc] initWithFormat:@"%@%@", timeMessagePart, unitMessagePart]; 65 | return timeString; 66 | } 67 | 68 | + (NSInteger)daysBetweenDate:(NSDate*)fromDateTime andDate:(NSDate*)toDateTime 69 | { 70 | NSDate *fromDate; 71 | NSDate *toDate; 72 | 73 | NSCalendar *calendar = [NSCalendar currentCalendar]; 74 | 75 | [calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate 76 | interval:NULL forDate:fromDateTime]; 77 | [calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate 78 | interval:NULL forDate:toDateTime]; 79 | 80 | NSDateComponents *difference = [calendar components:NSDayCalendarUnit 81 | fromDate:fromDate toDate:toDate options:0]; 82 | 83 | return [difference day]; 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /Chat21Core/lib/ChatAuth.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatAuth.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 05/02/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatAuth.h" 10 | #import "ChatUser.h" 11 | @import Firebase; 12 | 13 | @implementation ChatAuth 14 | 15 | +(void)authWithEmail:(NSString *)email password:(NSString *)password completion:(void (^)(ChatUser *user, NSError *))callback { 16 | [[FIRAuth auth] signInWithEmail:email password:password completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { 17 | if (error) { 18 | NSLog(@"Firebase Auth error for email %@/%@: %@", email, password, error); 19 | callback(nil, error); 20 | } 21 | else { 22 | FIRUser *user = authResult.user; 23 | NSLog(@"Firebase Auth success. email: %@, emailverified: %d, userid: %@", user.email, user.emailVerified, user.uid); 24 | ChatUser *chatuser = [[ChatUser alloc] init]; 25 | chatuser.userId = user.uid; 26 | chatuser.email = user.email; 27 | callback(chatuser, nil); 28 | } 29 | }]; 30 | // [[FIRAuth auth] signInWithEmail:email password:password completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { 31 | // if (error) { 32 | // NSLog(@"Firebase Auth error for email %@/%@: %@", email, password, error); 33 | // callback(nil, error); 34 | // } 35 | // else { 36 | // FIRUser *user = authResult.user; 37 | // NSLog(@"Firebase Auth success. email: %@, emailverified: %d, userid: %@", user.email, user.emailVerified, user.uid); 38 | // ChatUser *chatuser = [[ChatUser alloc] init]; 39 | // chatuser.userId = user.uid; 40 | // chatuser.email = user.email; 41 | // callback(chatuser, nil); 42 | // } 43 | // }]; 44 | } 45 | 46 | +(void)authWithCustomToken:(NSString *)token completion:(void (^)(ChatUser *user, NSError *))callback { 47 | [[FIRAuth auth] signInWithCustomToken:token completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { 48 | if (error) { 49 | NSLog(@"Firebase Auth error for token %@: %@", token, error); 50 | callback(nil, error); 51 | } 52 | else { 53 | FIRUser *user = authResult.user; 54 | NSLog(@"Firebase Auth success. email: %@, emailverified: %d, userid: %@", user.email, user.emailVerified, user.uid); 55 | ChatUser *chatuser = [[ChatUser alloc] init]; 56 | chatuser.userId = user.uid; 57 | chatuser.email = user.email; 58 | callback(chatuser, nil); 59 | } 60 | }]; 61 | // [[FIRAuth auth] signInWithCustomToken:token completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { 62 | // if (error) { 63 | // NSLog(@"Firebase Auth error for token %@: %@", token, error); 64 | // callback(nil, error); 65 | // } 66 | // else { 67 | // FIRUser *user = authResult.user; 68 | // NSLog(@"Firebase Auth success. email: %@, emailverified: %d, userid: %@", user.email, user.emailVerified, user.uid); 69 | // ChatUser *chatuser = [[ChatUser alloc] init]; 70 | // chatuser.userId = user.uid; 71 | // chatuser.email = user.email; 72 | // callback(chatuser, nil); 73 | // } 74 | // }]; 75 | // completion:^(FIRUser *user, NSError *error) { 76 | 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatTextMessageLeftCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatTextMessageLeftCell.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 17/04/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatTextMessageLeftCell.h" 10 | #import "ChatMessage.h" 11 | #import "ChatUtil.h" 12 | #import "ChatLocal.h" 13 | #import "ChatStyles.h" 14 | 15 | @implementation ChatTextMessageLeftCell 16 | 17 | - (void)awakeFromNib { 18 | [super awakeFromNib]; 19 | // Initialization code 20 | } 21 | 22 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 23 | [super setSelected:selected animated:animated]; 24 | 25 | // Configure the view for the selected state 26 | } 27 | 28 | -(void)configure:(ChatMessage *)message messages:(NSArray *)messages indexPath:(NSIndexPath *)indexPath viewController:(UIViewController *)viewController rowComponents:(NSMutableDictionary *)rowComponents { 29 | ChatStyles *styles = [ChatStyles sharedInstance]; 30 | NSString *dateChat; 31 | NSDate *dateToday = [NSDate date]; 32 | int numberDaysPrevChat = 0; 33 | int numberDaysNextChat = 0; 34 | ChatMessage *previousMessage; 35 | ChatMessage *nextMessage; 36 | UIView *backBox = self.messageBackgroundView;//(UIView *)[cell viewWithTag:50]; 37 | backBox.backgroundColor = styles.ballonLeftBackgroundColor; 38 | backBox.layer.masksToBounds = YES; 39 | backBox.layer.cornerRadius = 8.0; 40 | 41 | self.selectionStyle = UITableViewCellSelectionStyleNone; 42 | 43 | UILabel *labelMessage = self.messageLabel;//(UILabel *)[cell viewWithTag:10]; 44 | 45 | UIGestureRecognizer *longTapGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:viewController action:@selector(longTapOnCell:)]; 46 | UIGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:viewController action:@selector(tapOnCell:)]; 47 | [labelMessage addGestureRecognizer:longTapGestureRecognizer]; 48 | [labelMessage addGestureRecognizer:tapGestureRecognizer]; 49 | labelMessage.userInteractionEnabled = YES; 50 | [labelMessage setFont:styles.ballonFont]; 51 | [labelMessage setTextColor:styles.ballonLeftTextColor]; 52 | 53 | [self attributedString:labelMessage text:message indexPath:indexPath rowComponents:rowComponents right_cell:NO]; 54 | 55 | UILabel *labelTime = self.timeLabel;//(UILabel *)[cell viewWithTag:40]; 56 | labelTime.text = [message dateFormattedForListView]; 57 | 58 | // UILabel *labelNameUser = self.usernameLabel; 59 | NSString *text_name_user = [self displayUserOfMessage:message]; 60 | self.usernameLabel.text = text_name_user; 61 | 62 | UILabel *labelDay = self.dateLabel;//(UILabel *)[cell viewWithTag:30]; 63 | if(indexPath.row>0){ 64 | previousMessage = (ChatMessage *)[messages objectAtIndex:(indexPath.row-1)]; 65 | if(messages.count > (indexPath.row+1)){ 66 | nextMessage = (ChatMessage *)[messages objectAtIndex:(indexPath.row+1)]; 67 | numberDaysNextChat = (int)[ChatUtil daysBetweenDate:message.date andDate:nextMessage.date]; 68 | } 69 | numberDaysPrevChat = (int)[ChatUtil daysBetweenDate:previousMessage.date andDate:message.date]; 70 | dateChat = [self formatDateMessage:numberDaysPrevChat message:message row:indexPath.row]; 71 | }else{ 72 | numberDaysPrevChat = (int)[ChatUtil daysBetweenDate:message.date andDate:dateToday]; 73 | dateChat = [self formatDateMessage:numberDaysPrevChat message:message row:indexPath.row]; 74 | } 75 | if(numberDaysPrevChat>0){ 76 | labelDay.text = dateChat; 77 | }else{ 78 | labelDay.text = @""; 79 | } 80 | 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatTextMessageRightCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatTextMessageRightCell.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 18/04/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatTextMessageRightCell.h" 10 | #import "ChatMessage.h" 11 | #import "ChatUtil.h" 12 | #import "ChatLocal.h" 13 | #import "ChatStyles.h" 14 | 15 | @implementation ChatTextMessageRightCell 16 | 17 | - (void)awakeFromNib { 18 | [super awakeFromNib]; 19 | // Initialization code 20 | } 21 | 22 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 23 | [super setSelected:selected animated:animated]; 24 | 25 | // Configure the view for the selected state 26 | } 27 | 28 | -(void)configure:(ChatMessage *)message messages:(NSArray *)messages indexPath:(NSIndexPath *)indexPath viewController:(UIViewController *)viewController rowComponents:(NSMutableDictionary *)rowComponents { 29 | ChatStyles *styles = [ChatStyles sharedInstance]; 30 | NSString *dateChat; 31 | NSDate *dateToday = [NSDate date]; 32 | int numberDaysPrevChat = 0; 33 | int numberDaysNextChat = 0; 34 | ChatMessage *previousMessage; 35 | ChatMessage *nextMessage; 36 | UIView *backBox = self.messageBackgroundView; 37 | backBox.backgroundColor = styles.ballonRightBackgroundColor; 38 | backBox.layer.masksToBounds = YES; 39 | backBox.layer.cornerRadius = 8.0; 40 | 41 | self.selectionStyle = UITableViewCellSelectionStyleNone; 42 | 43 | UILabel *labelMessage = self.messageLabel; 44 | 45 | UIGestureRecognizer *longTapGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:viewController action:@selector(longTapOnCell:)]; 46 | UIGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:viewController action:@selector(tapOnCell:)]; 47 | [labelMessage addGestureRecognizer:longTapGestureRecognizer]; 48 | [labelMessage addGestureRecognizer:tapGestureRecognizer]; 49 | labelMessage.userInteractionEnabled = YES; 50 | [labelMessage setFont:styles.ballonFont]; 51 | [labelMessage setTextColor:styles.ballonRightTextColor]; 52 | 53 | [self attributedString:labelMessage text:message indexPath:indexPath rowComponents:rowComponents right_cell:YES]; 54 | 55 | UILabel *labelTime = self.timeLabel;//(UILabel *)[cell viewWithTag:40]; 56 | labelTime.text = [message dateFormattedForListView]; 57 | 58 | // UILabel *labelNameUser = self.user//(UILabel *)[cell viewWithTag:20]; 59 | // NSString *text_name_user = [self displayUserOfMessage:message]; 60 | // labelNameUser.text = text_name_user; 61 | 62 | UILabel *labelDay = self.dateLabel;//(UILabel *)[cell viewWithTag:30]; 63 | if(indexPath.row>0){ 64 | previousMessage = (ChatMessage *)[messages objectAtIndex:(indexPath.row-1)]; 65 | if(messages.count > (indexPath.row+1)){ 66 | nextMessage = (ChatMessage *)[messages objectAtIndex:(indexPath.row+1)]; 67 | numberDaysNextChat = (int)[ChatUtil daysBetweenDate:message.date andDate:nextMessage.date]; 68 | } 69 | numberDaysPrevChat = (int)[ChatUtil daysBetweenDate:previousMessage.date andDate:message.date]; 70 | dateChat = [self formatDateMessage:numberDaysPrevChat message:message row:indexPath.row]; 71 | }else{ 72 | numberDaysPrevChat = (int)[ChatUtil daysBetweenDate:message.date andDate:dateToday]; 73 | dateChat = [self formatDateMessage:numberDaysPrevChat message:message row:indexPath.row]; 74 | } 75 | if(numberDaysPrevChat>0){ 76 | labelDay.text = dateChat; 77 | }else{ 78 | labelDay.text = @""; 79 | } 80 | 81 | UIImageView *status_image_view = self.statusImageView; 82 | [ChatMessageCell setStatusImage:message statusImage:status_image_view]; 83 | } 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatConversationsVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatConversationsVC.h 3 | // Soleto 4 | // 5 | // Created by Andrea Sponziello on 07/11/14. 6 | // 7 | // 8 | 9 | #import 10 | #import "ChatPresenceHandler.h" 11 | //#import "ChatModalCallerDelegate.h" 12 | #import "ChatUser.h" 13 | 14 | @import Firebase; 15 | 16 | @class ChatConversationsHandler; 17 | @class ChatGroupsHandler; 18 | @class ChatImageCache; 19 | @class ChatPresenceHandler; 20 | @class SHPUserDC; 21 | @class ChatContactsSynchronizer; 22 | @class ChatGroup; 23 | @class CellConfigurator; 24 | @class ChatDiskImageCache; 25 | @class ChatConversation; 26 | 27 | static const int SECTION_GROUP_MENU_INDEX = 0; 28 | static const int SECTION_CONVERSATIONS_INDEX = 1; 29 | 30 | @interface ChatConversationsVC : UITableViewController 31 | - (IBAction)newGroupAction:(id)sender; 32 | - (IBAction)groupsAction:(id)sender; 33 | 34 | @property (strong, nonatomic) NSString *selectedConversationId; 35 | @property (strong, nonatomic) NSIndexPath *selectedConversationIndexPath; 36 | @property (strong, nonatomic) NSString *selectedRecipientId; 37 | @property (strong, nonatomic) NSString *selectedRecipientFullname; 38 | @property (strong, nonatomic) NSString *selectedRecipientTextToSend; 39 | @property (strong, nonatomic) NSDictionary *selectedRecipientAttributesToSend; 40 | @property (assign, nonatomic) BOOL groupsMode; 41 | @property (strong, nonatomic) NSString *selectedGroupId; 42 | @property (strong, nonatomic) NSString *selectedGroupName; 43 | @property (strong, nonatomic) ChatUser *me; 44 | //@property (strong, nonatomic) NSIndexPath *removingConversationAtIndexPath; 45 | @property (strong, nonatomic) UIBarButtonItem *backButton; 46 | @property (strong, nonatomic) ChatDiskImageCache *imageCache; 47 | @property (assign, nonatomic) int unread_count; 48 | @property (strong, nonatomic) NSDictionary *settings; 49 | @property (assign, nonatomic) BOOL isModal; 50 | @property (strong, nonatomic) CellConfigurator *cellConfigurator; 51 | @property (nonatomic, copy) void (^dismissModalCallback)(); 52 | 53 | // connection status 54 | @property (assign, nonatomic) FIRDatabaseHandle connectedRefHandle; 55 | @property (strong, nonatomic) UIButton *usernameButton; 56 | @property (strong, nonatomic) UILabel *statusLabel; 57 | @property (strong, nonatomic) UIActivityIndicatorView *activityIndicator; 58 | 59 | // subscribers 60 | @property (assign, nonatomic) NSUInteger added_handle; 61 | @property (assign, nonatomic) NSUInteger changed_handle; 62 | @property (assign, nonatomic) NSUInteger read_status_changed_handle; 63 | @property (assign, nonatomic) NSUInteger deleted_handle; 64 | // status 65 | @property (assign, nonatomic) NSUInteger connectedHandle; 66 | @property (assign, nonatomic) NSUInteger disconnectedHandle; 67 | 68 | // user thumbs 69 | @property (nonatomic, retain) NSMutableDictionary *imageDownloadsInProgress; 70 | 71 | //// user info 72 | //@property (strong, nonatomic) SHPUserDC *userLoader; 73 | 74 | @property (strong, nonatomic) ChatConversationsHandler *conversationsHandler; 75 | @property (strong, nonatomic) ChatPresenceHandler *presenceHandler; 76 | 77 | -(void)initializeWithSignedUser; // call this on every signin 78 | -(void)resetCurrentConversation; 79 | @property (weak, nonatomic) IBOutlet UIBarButtonItem *cancelButton; 80 | - (IBAction)cancelAction:(id)sender; 81 | 82 | - (IBAction)actionNewMessage:(id)sender; 83 | 84 | - (IBAction)unwindToConversationsView:(UIStoryboardSegue*)sender; 85 | 86 | -(void)openConversationWithUser:(ChatUser *)user; 87 | -(void)openConversationWithUser:(ChatUser *)user orGroup:(ChatGroup *)group sendMessage:(NSString *)text attributes:(NSDictionary *)attributes; 88 | 89 | -(void)setUIStatusDisconnected; 90 | -(void)setUIStatusConnected; 91 | 92 | +(void)updateReadStatusForConversationCell:(ChatConversation *)conversation atIndexPath:(NSIndexPath *)indexPath inTableView:(UITableView *)tableView; 93 | 94 | //-(void)logout; 95 | 96 | @end 97 | 98 | -------------------------------------------------------------------------------- /Chat21Core/lib/ChatConnectionStatusHandler.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatConnectionStatusHandler.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 01/01/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatConnectionStatusHandler.h" 10 | #import 11 | 12 | @implementation ChatConnectionStatusHandler 13 | 14 | -(void)connect { 15 | NSLog(@"Connection status."); 16 | NSString *url = @"/.info/connected"; 17 | FIRDatabaseReference *rootRef = [[FIRDatabase database] reference]; 18 | self.connectedRef = [rootRef child:url]; 19 | 20 | // event 21 | if (!self.connectedRefHandle) { 22 | self.connectedRefHandle = [self.connectedRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { 23 | NSLog(@"snapshot %@ - %d", snapshot, [snapshot.value boolValue]); 24 | BOOL status = [snapshot.value boolValue]; 25 | if(status) { 26 | NSLog(@".connected."); 27 | [self notifyEvent:ChatConnectionStatusEventConnected]; 28 | } else { 29 | NSLog(@".not connected."); 30 | [self notifyEvent:ChatConnectionStatusEventDisconnected]; 31 | } 32 | }]; 33 | } 34 | } 35 | 36 | -(void)isStatusConnectedWithCompletionBlock:(void (^)(BOOL connected, NSError* error))callback { 37 | // once 38 | [self.connectedRef observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) { 39 | // Get user value 40 | if([snapshot.value boolValue]) { 41 | // NSLog(@"..connected once.."); 42 | callback(YES, nil); 43 | } 44 | else { 45 | // NSLog(@"..not connected once.."); 46 | callback(NO, nil); 47 | } 48 | } withCancelBlock:^(NSError * _Nonnull error) { 49 | NSLog(@"%@", error.localizedDescription); 50 | callback(NO, error); 51 | }]; 52 | } 53 | 54 | -(void)dispose { 55 | [self.connectedRef removeAllObservers]; 56 | [self removeAllObservers]; 57 | self.connectedRef = nil; 58 | self.connectedRefHandle = 0; 59 | } 60 | 61 | // observer 62 | 63 | -(void)notifyEvent:(ChatConnectionStatusEventType)event { 64 | if (!self.eventObservers) { 65 | return; 66 | } 67 | NSMutableDictionary *eventCallbacks = [self.eventObservers objectForKey:@(event)]; 68 | if (!eventCallbacks) { 69 | return; 70 | } 71 | for (NSNumber *event_handle_key in eventCallbacks.allKeys) { 72 | void (^callback)() = [eventCallbacks objectForKey:event_handle_key]; 73 | callback(); 74 | } 75 | } 76 | 77 | -(NSUInteger)observeEvent:(ChatConnectionStatusEventType)eventType withCallback:(void (^)())callback { 78 | if (!self.eventObservers) { 79 | self.eventObservers = [[NSMutableDictionary alloc] init]; 80 | } 81 | NSMutableDictionary *eventCallbacks = [self.eventObservers objectForKey:@(eventType)]; 82 | if (!eventCallbacks) { 83 | eventCallbacks = [[NSMutableDictionary alloc] init]; 84 | [self.eventObservers setObject:eventCallbacks forKey:@(eventType)]; 85 | } 86 | NSUInteger callback_handle = (NSUInteger) OSAtomicIncrement64Barrier(&_lastEventHandle); 87 | [eventCallbacks setObject:callback forKey:@(callback_handle)]; 88 | return callback_handle; 89 | } 90 | 91 | -(void)removeObserverWithHandle:(NSUInteger)event_handle { 92 | if (!self.eventObservers) { 93 | return; 94 | } 95 | // iterate all keys (events) 96 | for (NSNumber *event_key in self.eventObservers) { 97 | NSMutableDictionary *eventCallbacks = [self.eventObservers objectForKey:event_key]; 98 | [eventCallbacks removeObjectForKey:@(event_handle)]; 99 | } 100 | } 101 | 102 | -(void)removeAllObservers { 103 | if (!self.eventObservers) { 104 | return; 105 | } 106 | // iterate all keys (events) 107 | for (NSNumber *event_key in self.eventObservers) { 108 | NSMutableDictionary *eventCallbacks = [self.eventObservers objectForKey:event_key]; 109 | [eventCallbacks removeAllObjects]; 110 | } 111 | } 112 | 113 | @end 114 | -------------------------------------------------------------------------------- /Chat21Core/lib/ChatConversationHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatConversationHandler.h 3 | // Soleto 4 | // 5 | // Created by Andrea Sponziello on 19/12/14. 6 | // 7 | // 8 | 9 | #import 10 | #import "ChatEventType.h" 11 | #import 12 | 13 | @import Firebase; 14 | 15 | @class FAuthData; 16 | @class FirebaseCustomAuthHelper; 17 | @class Firebase; 18 | @class ChatUser; 19 | @class ChatGroup; 20 | @class ChatMessage; 21 | @class ChatMessageMetadata; 22 | @class ChatImageDownloadManager; 23 | 24 | @interface ChatConversationHandler : NSObject // 25 | 26 | @property (strong, nonatomic) ChatUser *user; 27 | @property (strong, nonatomic) NSString *recipientId; 28 | @property (strong, nonatomic) NSString *recipientFullname; 29 | //@property (strong, nonatomic) NSString *groupName; 30 | //@property (strong, nonatomic) NSString *groupId; 31 | 32 | @property (strong, nonatomic) NSString *senderId; 33 | 34 | @property (strong, nonatomic) NSString *conversationId; 35 | @property (strong, nonatomic) NSMutableArray *messages; 36 | @property (strong, nonatomic) NSString *firebaseToken; 37 | @property (strong, nonatomic) FIRDatabaseReference *messagesRef; 38 | @property (strong, nonatomic) FIRDatabaseReference *conversationOnSenderRef; 39 | @property (strong, nonatomic) FIRDatabaseReference *conversationOnReceiverRef; 40 | @property (assign, nonatomic) FIRDatabaseHandle messages_ref_handle; 41 | @property (assign, nonatomic) FIRDatabaseHandle updated_messages_ref_handle; 42 | @property (strong, nonatomic) FirebaseCustomAuthHelper *authHelper; 43 | @property (strong, nonatomic) NSString *channel_type; 44 | @property (strong, nonatomic) ChatImageDownloadManager *imageDownloader; 45 | 46 | // observer 47 | @property (strong, nonatomic) NSMutableDictionary *eventObservers; 48 | @property (assign, atomic) volatile int64_t lastEventHandle; 49 | -(NSUInteger)observeEvent:(ChatMessageEventType)eventType withCallback:(void (^)(ChatMessage *message))callback; 50 | -(void)removeObserverWithHandle:(NSUInteger)event_handle; 51 | -(void)removeAllObservers; 52 | 53 | @property (assign, nonatomic) double lastSentReadNotificationTime; 54 | 55 | -(id)initWithRecipient:(NSString *)recipientId recipientFullName:(NSString *)recipientFullName; 56 | -(id)initWithGroupId:(NSString *)groupId groupName:(NSString *)groupName; 57 | -(void)connect; 58 | -(void)dispose; 59 | -(void)sendTextMessage:(NSString *)text completion:(void(^)(ChatMessage *message, NSError *error)) callback; 60 | //-(void)sendTextMessage:(NSString *)text subtype:(NSString *)subtype attributes:(NSDictionary *)attributes completion:(void(^)(ChatMessage *message, NSError *error)) callback; 61 | -(void)sendTextMessage:(NSString *)text subtype:(NSString *)subtype attributes:(NSDictionary *)attributes completion:(void(^)(ChatMessage *message, NSError *error)) callback; 62 | //-(void)appendImagePlaceholderMessageWithFilename:(NSString *)imageFilename metadata:(ChatMessageMetadata *)metadata attributes:(NSDictionary *)attributes completion:(void(^)(ChatMessage *message, NSError *error))callback; 63 | -(void)appendImagePlaceholderMessageWithImage:(UIImage *)image attributes:(NSDictionary *)attributes completion:(void(^)(ChatMessage *message, NSError *error)) callback; 64 | -(void)sendImagePlaceholderMessage:(ChatMessage *)message completion:(void (^)(ChatMessage *, NSError *))callback; 65 | -(void)sendMessageType:(NSString *)type subtype:(NSString *)subtype text:(NSString *)text imageURL:(NSString *)imageURL metadata:(ChatMessageMetadata *)metadata attributes:(NSDictionary *)attributes completion:(void(^)(ChatMessage *message, NSError *error)) callback; 66 | -(void)restoreMessagesFromDB; 67 | -(NSString *)mediaFolderPath; 68 | +(NSString *)mediaFolderPathOfRecipient:(NSString *)recipiendId; 69 | -(void)saveImageToRecipientMediaFolderAsPNG:(UIImage *)image imageFileName:(NSString *)imageFileName; 70 | -(void)uploadImage:(UIImage *)image fileName:(NSString *)fileName completion:(void(^)(NSURL *downloadURL, NSError *error)) callback progressCallback:(void(^)(double fraction))progressCallback; 71 | //-(void)updateMessageStatus:(int)status forMessage:(ChatMessage *)message; 72 | -(void)updateMessageStatusSynchronized:(NSString *)messageId withStatus:(int)status completion:(void(^)(void))callback; 73 | -(void)resendMessageWithId:(NSString *)messageId completion:(void(^)(ChatMessage *message, NSError *error)) callback; 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /Chat21UI/QBPopupMenu/QBPopupMenuItemView.m: -------------------------------------------------------------------------------- 1 | // 2 | // QBPopupMenuItemView.m 3 | // QBPopupMenu 4 | // 5 | // Created by Tanaka Katsuma on 2013/11/22. 6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved. 7 | // 8 | 9 | #import "QBPopupMenuItemView.h" 10 | 11 | #import "QBPopupMenu.h" 12 | #import "QBPopupMenuItem.h" 13 | 14 | @interface QBPopupMenuItemView () 15 | 16 | @property (nonatomic, strong, readwrite) UIButton *button; 17 | 18 | @end 19 | 20 | @implementation QBPopupMenuItemView 21 | 22 | + (instancetype)itemViewWithItem:(QBPopupMenuItem *)item 23 | { 24 | return [[self alloc] initWithItem:item]; 25 | } 26 | 27 | - (instancetype)initWithItem:(QBPopupMenuItem *)item 28 | { 29 | self = [super initWithFrame:CGRectZero]; 30 | 31 | if (self) { 32 | // View settings 33 | self.opaque = NO; 34 | self.backgroundColor = [UIColor clearColor]; 35 | self.clipsToBounds = YES; 36 | 37 | // Create button 38 | self.button = ({ 39 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 40 | button.frame = self.bounds; 41 | button.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 42 | [button addTarget:self action:@selector(performAction) forControlEvents:UIControlEventTouchUpInside]; 43 | 44 | // Set style 45 | button.contentMode = UIViewContentModeScaleAspectFit; 46 | button.titleLabel.font = [UIFont systemFontOfSize:14.0]; 47 | button.imageView.contentMode = UIViewContentModeScaleAspectFit; 48 | [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 49 | [button setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted]; 50 | 51 | button; 52 | }); 53 | [self addSubview:self.button]; 54 | 55 | // Property settings 56 | self.item = item; 57 | } 58 | 59 | return self; 60 | } 61 | 62 | 63 | #pragma mark - Accessors 64 | 65 | - (void)setImage:(UIImage *)image 66 | { 67 | [self.button setBackgroundImage:image forState:UIControlStateNormal]; 68 | } 69 | 70 | - (UIImage *)image 71 | { 72 | return [self.button backgroundImageForState:UIControlStateNormal]; 73 | } 74 | 75 | - (void)setHighlightedImage:(UIImage *)highlightedImage 76 | { 77 | [self.button setBackgroundImage:highlightedImage forState:UIControlStateHighlighted]; 78 | } 79 | 80 | - (UIImage *)highlightedImage 81 | { 82 | return [self.button backgroundImageForState:UIControlStateHighlighted]; 83 | } 84 | 85 | - (void)setItem:(QBPopupMenuItem *)item 86 | { 87 | _item = item; 88 | 89 | // Update 90 | [self configureButton]; 91 | } 92 | 93 | 94 | #pragma mark - Actions 95 | 96 | - (void)performAction 97 | { 98 | if (self.item.target && self.item.action) { 99 | [self.item.target performSelector:self.item.action withObject:nil afterDelay:0]; 100 | } 101 | 102 | // Close popup menu 103 | [self.popupMenu dismissAnimated:YES]; 104 | } 105 | 106 | 107 | #pragma mark - Updating the View 108 | 109 | - (void)sizeToFit 110 | { 111 | CGSize size = [self sizeThatFits:CGSizeZero]; 112 | 113 | CGRect frame = self.frame; 114 | frame.size = size; 115 | self.frame = frame; 116 | } 117 | 118 | - (CGSize)sizeThatFits:(CGSize)size 119 | { 120 | CGSize buttonSize = [self.button sizeThatFits:CGSizeZero]; 121 | buttonSize.width += 10 * 2; 122 | 123 | return buttonSize; 124 | } 125 | 126 | - (void)configureButton 127 | { 128 | // Title 129 | [self.button setTitle:self.item.title forState:UIControlStateNormal]; 130 | 131 | // Image 132 | [self.button setImage:self.item.image forState:UIControlStateNormal]; 133 | [self.button setImage:self.item.image forState:UIControlStateHighlighted]; 134 | 135 | // Content edge insets 136 | if (self.item.title && self.item.image) { 137 | self.button.titleEdgeInsets = UIEdgeInsetsMake(0, 6, 0, 0); 138 | self.button.imageEdgeInsets = UIEdgeInsetsMake(0, -3, 0, 0); 139 | } else { 140 | self.button.titleEdgeInsets = UIEdgeInsetsZero; 141 | self.button.imageEdgeInsets = UIEdgeInsetsZero; 142 | } 143 | } 144 | 145 | @end 146 | -------------------------------------------------------------------------------- /Chat21Core/Base.lproj/Chat.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Chat.strings 3 | chat21 4 | 5 | Created by Andrea Sponziello on 05/02/2018. 6 | Copyright © 2018 Frontiere21. All rights reserved. 7 | */ 8 | 9 | //"Done" = "Done"; 10 | //"Close" = "Close"; 11 | //"Back" = "Back"; 12 | //"Send" = "Public"; 13 | //"RequiredPlaceholderLKey" = "Required"; 14 | //"ConnectionErrorLKey" = "Connection error!"; 15 | "TimeToStringDateFormat" = "MMMM d"; 16 | //"Saving" = "Saving..."; 17 | //"NetworkErrorTitle" = "Network error"; 18 | //"NetworkError" = "An network error occurred during connection."; 19 | 20 | "FewSecondsAgoLKey" = "few seconds ago"; 21 | "AboutAMinuteAgoLKey" = "about a minute ago"; 22 | "MinutesAgoLKey" = "minutes ago"; 23 | "AboutAnHourAgoLKey" = "about one hour ago"; 24 | "HoursAgoLKey" = "hours ago"; 25 | 26 | // *** tabbar menu labels *** 27 | 28 | "Authenticating" = "Authenticating..."; 29 | "UsernamePasswordInvalid" = "Username o password non validi"; 30 | "NetworkError" = "A network error occurred"; 31 | "NetworkErrorTitle" = "Netword error"; 32 | "UnknownError" = "Unknown error"; 33 | 34 | "Chat" = "Chat"; 35 | "Profile" = "Profile"; 36 | "Help" = "Need help?"; 37 | 38 | // *** chat labels *** 39 | 40 | "NoConversationsYet" = "No Conversation yet"; 41 | "DeleteConversationTitle" = "Delete this Conversation?"; 42 | "DeleteConversationMessage" = "Are you sure you want to delete this Conversation? Don't worry, all the messages will stay safe. Just reopen the chat with the group or user to see all exchanged messages"; 43 | "ChatConnected" = "connected"; 44 | "ChatDisconnected" = "connecting..."; 45 | "ChatSend" = "Send"; 46 | 47 | // settings view 48 | "ChatTerms" = "Terms of use"; 49 | "ChatPrivacy" = "Privacy"; 50 | "ChatLicences" = "Licences"; 51 | 52 | // select user (new message) view 53 | "NewMessage" = "New Message"; 54 | "NewGroup" = "New group"; 55 | "Groups" = "Groups"; 56 | 57 | "last seen" = "last seen"; 58 | "group summary" = "group"; 59 | 60 | // chat profile 61 | "Synchronizing contacts" = "Synchronizing contacts..."; 62 | "ChatProfile" = "Profile"; 63 | "ChatSignoutAlert" = "Are you sure?"; 64 | "ChatCancel" = "Cancel"; 65 | "ChatSave" = "Save"; 66 | "ChatSettingsMenuOption" = "Settings"; 67 | "ChatHelpMenuOption" = "Help and support"; 68 | "ChatLogoutMenuOption" = "Logout"; 69 | "ChatLogoutChatOption" = "Chat"; 70 | "Open link" = "Open in Safari"; 71 | "Copy link" = "Copy"; 72 | "Group administrator" = "administrator"; 73 | "Select group" = "Select group"; 74 | "Member info" = "Info"; 75 | "Member remove" = "Remove member"; 76 | "Send message" = "Send message"; 77 | "Members" = "Members"; 78 | "Add user to group" = "Add %@ to group %@?"; 79 | "Add" = "Add"; 80 | "Just in group" = "Just in group"; 81 | "Add member" = "Add Member"; 82 | "You was added to group" = "You was added to group \"%@\""; 83 | "You created the group" = "You created the group \"%@\""; 84 | "You" = "You"; 85 | "ShortDateFormat" = "yyyy/M/d"; 86 | "yesterday" = "yesterday"; 87 | "today" = "today"; 88 | "group info" = "Group info"; 89 | "new group" = "New group"; 90 | "create group info message" = "Setup the group's subject and icon (optional)"; 91 | "group name" = "Group's name"; 92 | "Creating group" = "Creating group..."; 93 | "Group creation error" = "Group creation error"; 94 | "Add Photo" = "Add Photo"; 95 | "next" = "Next"; 96 | "cancel" = "Cancel"; 97 | "create" = "Create"; 98 | "contact name" = "Contact name"; 99 | "remove" = "Remove"; 100 | "add members" = "Add members"; 101 | "group created by" = "Group created by %@"; 102 | "group created on" = "Created on %@"; 103 | "Group name placeholder" = "Set the group name"; 104 | "GroupNameTitle" = "Group name"; 105 | "all contacts" = "All contacts"; 106 | "change fullname" = "Change Full name"; 107 | "change password" = "Change Password"; 108 | "settingsLegalSection" = "Legal"; 109 | "Remove this member?" = "Remove this member?"; 110 | "Remove" = "Remove"; 111 | "copy" = "Copy"; 112 | "info" = "Info"; 113 | "resend" = "Resend"; 114 | "digit message" = "Digit a message"; 115 | "ReadConversationAction" = "Read"; 116 | "UnreadConversationAction" = "Unread"; 117 | 118 | "ArchiveConversationAction" = "Close"; 119 | "UnarchiveConversationAction" = "Reopen"; 120 | "ArchivedConversationsTitle" = "Closed"; 121 | "ArchivedBadgeLabel" = " CLOSED "; 122 | 123 | //"ArchiveConversationAction" = "Archive"; 124 | //"UnarchiveConversationAction" = "Unarchive"; 125 | //"ArchivedConversationsTitle" = "Archived"; 126 | //"ArchivedBadgeLabel" = " ARCHIVED "; 127 | 128 | "Network Error Title" = "Network error"; 129 | "Network Error Message" = "A network error occurred."; 130 | -------------------------------------------------------------------------------- /Chat21Core/en.lproj/Chat.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Chat.strings 3 | chat21 4 | 5 | Created by Andrea Sponziello on 05/02/2018. 6 | Copyright © 2018 Frontiere21. All rights reserved. 7 | */ 8 | 9 | //"Done" = "Done"; 10 | //"Close" = "Close"; 11 | //"Back" = "Back"; 12 | //"Send" = "Public"; 13 | //"RequiredPlaceholderLKey" = "Required"; 14 | //"ConnectionErrorLKey" = "Connection error!"; 15 | "TimeToStringDateFormat" = "MMMM d"; 16 | //"Saving" = "Saving..."; 17 | //"NetworkErrorTitle" = "Network error"; 18 | //"NetworkError" = "An network error occurred during connection."; 19 | 20 | "FewSecondsAgoLKey" = "few seconds ago"; 21 | "AboutAMinuteAgoLKey" = "about a minute ago"; 22 | "MinutesAgoLKey" = "minutes ago"; 23 | "AboutAnHourAgoLKey" = "about one hour ago"; 24 | "HoursAgoLKey" = "hours ago"; 25 | 26 | // *** tabbar menu labels *** 27 | 28 | "Authenticating" = "Authenticating..."; 29 | "UsernamePasswordInvalid" = "Username o password non validi"; 30 | "NetworkError" = "A network error occurred"; 31 | "NetworkErrorTitle" = "Netword error"; 32 | "UnknownError" = "Unknown error"; 33 | 34 | "Chat" = "Chat"; 35 | "Profile" = "Profile"; 36 | "Help" = "Need help?"; 37 | 38 | // *** chat labels *** 39 | 40 | "NoConversationsYet" = "No Conversation yet"; 41 | "DeleteConversationTitle" = "Delete this Conversation?"; 42 | "DeleteConversationMessage" = "Are you sure you want to delete this Conversation? Don't worry, all the messages will stay safe. Just reopen the chat with the group or user to see all exchanged messages"; 43 | "ChatConnected" = "connected"; 44 | "ChatDisconnected" = "connecting..."; 45 | "ChatSend" = "Send"; 46 | 47 | // settings view 48 | "ChatTerms" = "Terms of use"; 49 | "ChatPrivacy" = "Privacy"; 50 | "ChatLicences" = "Licences"; 51 | 52 | // select user (new message) view 53 | "NewMessage" = "New Message"; 54 | "NewGroup" = "New group"; 55 | "Groups" = "Groups"; 56 | 57 | "last seen" = "last seen"; 58 | "group summary" = "group"; 59 | 60 | // chat profile 61 | "Synchronizing contacts" = "Synchronizing contacts..."; 62 | "ChatProfile" = "Profile"; 63 | "ChatSignoutAlert" = "Are you sure?"; 64 | "ChatCancel" = "Cancel"; 65 | "ChatSave" = "Save"; 66 | "ChatSettingsMenuOption" = "Settings"; 67 | "ChatHelpMenuOption" = "Help and support"; 68 | "ChatLogoutMenuOption" = "Logout"; 69 | "ChatLogoutChatOption" = "Chat"; 70 | "Open link" = "Open in Safari"; 71 | "Copy link" = "Copy"; 72 | "Group administrator" = "administrator"; 73 | "Select group" = "Select group"; 74 | "Member info" = "Info"; 75 | "Member remove" = "Remove member"; 76 | "Send message" = "Send message"; 77 | "Members" = "Members"; 78 | "Add user to group" = "Add %@ to group %@?"; 79 | "Add" = "Add"; 80 | "Just in group" = "Just in group"; 81 | "Add member" = "Add Member"; 82 | "You was added to group" = "You was added to group \"%@\""; 83 | "You created the group" = "You created the group \"%@\""; 84 | "You" = "You"; 85 | "ShortDateFormat" = "yyyy/M/d"; 86 | "yesterday" = "yesterday"; 87 | "today" = "today"; 88 | "group info" = "Group info"; 89 | "new group" = "New group"; 90 | "create group info message" = "Setup the group's subject and icon (optional)"; 91 | "group name" = "Group's name"; 92 | "Creating group" = "Creating group..."; 93 | "Group creation error" = "Group creation error"; 94 | "Add Photo" = "Add Photo"; 95 | "next" = "Next"; 96 | "cancel" = "Cancel"; 97 | "create" = "Create"; 98 | "contact name" = "Contact name"; 99 | "remove" = "Remove"; 100 | "add members" = "Add members"; 101 | "group created by" = "Group created by %@"; 102 | "group created on" = "Created on %@"; 103 | "Group name placeholder" = "Set the group name"; 104 | "GroupNameTitle" = "Group name"; 105 | "all contacts" = "All contacts"; 106 | "change fullname" = "Change Full name"; 107 | "change password" = "Change Password"; 108 | "settingsLegalSection" = "Legal"; 109 | "Remove this member?" = "Remove this member?"; 110 | "Remove" = "Remove"; 111 | "copy" = "Copy"; 112 | "info" = "Info"; 113 | "resend" = "Resend"; 114 | "digit message" = "Digit a message"; 115 | "ReadConversationAction" = "Read"; 116 | "UnreadConversationAction" = "Unread"; 117 | 118 | "ArchiveConversationAction" = "Close"; 119 | "UnarchiveConversationAction" = "Reopen"; 120 | "ArchivedConversationsTitle" = "Closed"; 121 | "ArchivedBadgeLabel" = " CLOSED "; 122 | 123 | //"ArchiveConversationAction" = "Archive"; 124 | //"UnarchiveConversationAction" = "Unarchive"; 125 | //"ArchivedConversationsTitle" = "Archived"; 126 | //"ArchivedBadgeLabel" = " ARCHIVED "; 127 | 128 | "Network Error Title" = "Network error"; 129 | "Network Error Message" = "A network error occurred."; 130 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatSelectGroupLocalTVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatSelectGroupLocalTVC.m 3 | // 4 | // Created by Andrea Sponziello on 26/09/2017. 5 | // Copyright © 2017 Frontiere21. All rights reserved. 6 | // 7 | 8 | #import "ChatSelectGroupLocalTVC.h" 9 | //#import "ChatModalCallerDelegate.h" 10 | #import "ChatImageCache.h" 11 | #import "ChatImageWrapper.h" 12 | #import "ChatGroup.h" 13 | #import "ChatManager.h" 14 | #import "ChatUser.h" 15 | #import "ChatUtil.h" 16 | #import "ChatLocal.h" 17 | 18 | @interface ChatSelectGroupLocalTVC () 19 | 20 | @end 21 | 22 | @implementation ChatSelectGroupLocalTVC 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | 27 | self.groups = nil; 28 | 29 | self.navigationItem.title = [ChatLocal translate:@"Select group"]; 30 | self.cancelButton.title = [ChatLocal translate:@"cancel"]; 31 | self.imageDownloadsInProgress = [NSMutableDictionary dictionary]; 32 | 33 | self.tableView.delegate = self; 34 | self.tableView.dataSource = self; 35 | 36 | [self loadGroups]; 37 | // [self initImageCache]; 38 | } 39 | 40 | -(void)disposeResources { 41 | NSLog(@"Disposing pending image connections..."); 42 | } 43 | 44 | - (void)didReceiveMemoryWarning { 45 | [super didReceiveMemoryWarning]; 46 | // Dispose of any resources that can be recreated. 47 | } 48 | 49 | #pragma mark - Table view data source 50 | 51 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 52 | return 1; 53 | } 54 | 55 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 56 | if (self.groups) { 57 | return self.groups.count; 58 | } 59 | else { 60 | return 0; 61 | } 62 | 63 | } 64 | 65 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 66 | UITableViewCell *cell; 67 | if (self.groups) { 68 | long groupIndex = indexPath.row; 69 | cell = [self.tableView dequeueReusableCellWithIdentifier:@"group_cell"]; 70 | ChatGroup *group = [self.groups objectAtIndex:groupIndex]; 71 | UILabel *name_label = (UILabel *)[cell viewWithTag:2]; 72 | name_label.text = group.name; 73 | UIImage *circled = [ChatUtil circleImage:[UIImage imageNamed:@"group-conversation-avatar"]]; 74 | UIImageView *image_view = (UIImageView *)[cell viewWithTag:1]; 75 | image_view.image = circled; 76 | } 77 | else { 78 | cell = [self.tableView dequeueReusableCellWithIdentifier:@"message_cell"]; 79 | } 80 | return cell; 81 | } 82 | 83 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 84 | 85 | [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; 86 | NSInteger userIndex = indexPath.row; 87 | ChatGroup *selectedGroup = nil; 88 | if (self.groups) { 89 | selectedGroup = [self.groups objectAtIndex:userIndex]; 90 | } 91 | [self selectGroup:selectedGroup]; 92 | } 93 | 94 | -(void)selectGroup:(ChatGroup *)selectedGroup { 95 | // NSMutableDictionary *options = [[NSMutableDictionary alloc] init]; 96 | // [options setObject:selectedGroup forKey:@"group"]; 97 | // [self.modalCallerDelegate setupViewController:self didFinishSetupWithInfo:options]; 98 | if (self.completionCallback) { 99 | [self dismissViewControllerAnimated:YES completion:^{ 100 | self.completionCallback(selectedGroup, NO); 101 | }]; 102 | } 103 | } 104 | 105 | -(void)loadGroups { 106 | // ChatGroupsDB *db = [ChatGroupsDB getSharedInstance]; 107 | // ChatUser *user = [ChatManager getSharedInstance].loggedUser; 108 | // NSMutableArray *groups = [db getAllGroupsForUserSyncronized:user.userId]; 109 | NSDictionary *groups_dict = [[ChatManager getInstance] allGroups]; 110 | NSLog(@"GROUPS LOADED! %lu", (unsigned long) self.groups.count); 111 | self.groups = [[NSMutableArray alloc] init]; 112 | for(NSString *key in [groups_dict allKeys]) { 113 | [self.groups addObject:[groups_dict objectForKey:key]]; 114 | } 115 | [self.tableView reloadData]; 116 | } 117 | 118 | // dismiss modal 119 | 120 | - (IBAction)CancelAction:(id)sender { 121 | // NSLog(@"dismissing %@", self.modalCallerDelegate); 122 | [self disposeResources]; 123 | [self.view endEditing:YES]; 124 | // [self.modalCallerDelegate setupViewController:self didCancelSetupWithInfo:nil]; 125 | if (self.completionCallback) { 126 | [self dismissViewControllerAnimated:YES completion:^{ 127 | self.completionCallback(nil, YES); 128 | }]; 129 | } 130 | } 131 | 132 | -(void)dealloc { 133 | NSLog(@"SEARCH USERS VIEW DEALLOCATING..."); 134 | } 135 | 136 | @end 137 | -------------------------------------------------------------------------------- /Chat21Core/util/ChatImageCache.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatImageCache.m 3 | // Salve Smart 4 | // 5 | // Created by Andrea Sponziello on 04/11/15. 6 | // Copyright © 2015 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatImageCache.h" 10 | #import 11 | #import "ChatImageWrapper.h" 12 | 13 | static ChatImageCache *sharedInstance = nil; 14 | 15 | //@interface ImageWrapper: NSObject 16 | // 17 | //@property (nonatomic, strong) NSDate *lastReadTime; 18 | //@property (nonatomic, strong) NSDate *creationTime; 19 | //@property (nonatomic, strong) UIImage *image; 20 | //@property (nonatomic, strong) NSString *key; 21 | // 22 | //@end 23 | // 24 | //@implementation ImageWrapper 25 | // 26 | //@synthesize lastReadTime; 27 | //@synthesize creationTime; 28 | //@synthesize image; 29 | //@synthesize key; 30 | // 31 | ////static int compareSelector(id w1, id w2, void *context) { 32 | //// SEL methodSelector = (SEL)context; 33 | //// NSDate d1 = [w1 performSelector:methodSelector]; 34 | //// NSDate d2 = [w2 performSelector:methodSelector]; 35 | //// return [value1 compare:value2]; 36 | ////} 37 | // 38 | //@end 39 | 40 | 41 | @implementation ChatImageCache 42 | 43 | -(id)init 44 | { 45 | if (self = [super init]) 46 | { 47 | self.imageCache = [[NSMutableDictionary alloc] init]; 48 | self.cacheName = @"defaultImageCache"; 49 | self.maxSize = 50; 50 | } 51 | return self; 52 | } 53 | 54 | +(ChatImageCache *)getSharedInstance { 55 | if (!sharedInstance) { 56 | sharedInstance = [[super alloc] init]; 57 | } 58 | return sharedInstance; 59 | } 60 | 61 | -(void)addImage:(UIImage *)image withKey:(NSString *)key { 62 | [self removeOldestImage]; 63 | ChatImageWrapper *wrapper = [[ChatImageWrapper alloc] init]; 64 | wrapper.image = image; 65 | wrapper.lastReadTime = [[NSDate alloc] init]; 66 | wrapper.createdTime = wrapper.lastReadTime; 67 | wrapper.key = key; 68 | [self.imageCache setObject:wrapper forKey:key]; 69 | } 70 | 71 | -(void)removeOldestImage { 72 | // ATTENZIONE! perchè questo metodo abbia senso bisognerebbe 73 | // rendere persistente anche il dizionario! 74 | 75 | if ([self.imageCache count] == self.maxSize) { 76 | // NSLog(@"Removing oldest element"); 77 | // remove oldest element 78 | 79 | NSMutableArray *wrappers = [[NSMutableArray alloc] init]; 80 | for (NSString* key in self.imageCache) { 81 | ChatImageWrapper *wrapper = [self.imageCache objectForKey:key]; 82 | [wrappers addObject:wrapper]; 83 | // NSLog(@"found: %@", wrapper); 84 | } 85 | // sort by lastDate 86 | 87 | // Ascending order 88 | NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"lastReadTime" ascending:YES]; 89 | [wrappers sortUsingDescriptors:[NSArray arrayWithObject:descriptor]]; 90 | // first element is the oldest one 91 | ChatImageWrapper *wrapperToRemove = [wrappers objectAtIndex:0]; 92 | wrapperToRemove.image = nil; 93 | [self.imageCache removeObjectForKey:wrapperToRemove.key]; 94 | [self deleteImage:wrapperToRemove.key]; 95 | } 96 | } 97 | 98 | -(ChatImageWrapper *)getImage:(NSString *)key { 99 | ChatImageWrapper *wrapper = nil; 100 | // hit memory first 101 | wrapper = (ChatImageWrapper *)[self.imageCache objectForKey:key]; 102 | if (wrapper) { 103 | wrapper.lastReadTime = [[NSDate alloc] init]; 104 | // then hit disk 105 | } 106 | return wrapper; 107 | } 108 | 109 | -(void)empty { 110 | // DON'T USE THIS: "for (NSString *key in imageCache)". This returns 111 | // an enumerator tha cannot be modified during iteration! 112 | // EXCEPTION was: "mutated while being enumerated" 113 | NSArray *keys = [self.imageCache allKeys]; 114 | for (NSString* key in keys) { 115 | ChatImageWrapper *wrapperToRemove = [self.imageCache objectForKey:key]; 116 | wrapperToRemove.image = nil; // really useful? The wrapper has a strong reference to the image... 117 | [self.imageCache removeObjectForKey:key]; // removeAllObjects (as next) or this? :( 118 | } 119 | [self.imageCache removeAllObjects]; 120 | } 121 | 122 | -(void)deleteImage:(NSString*)imageKey { 123 | [self.imageCache removeObjectForKey:imageKey]; 124 | } 125 | 126 | +(NSString *)filePathInApp:(NSString *)path { 127 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 128 | NSString *documentsDirectory = [paths objectAtIndex:0]; 129 | NSString *file = [documentsDirectory stringByAppendingPathComponent:path]; 130 | return file; 131 | } 132 | 133 | @end 134 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatMessagesVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatMessagesVC.h 3 | // Chat21 4 | // 5 | // Created by Dario De Pascalis on 22/03/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "QBPopupMenu.h" 11 | #import 12 | #import "ChatGroupsSubscriber.h" 13 | 14 | @class ChatConversationHandler; 15 | @class QBPopupMenu; 16 | @class ChatImageCache; 17 | @class ChatMessagesTVC; 18 | @class ChatTitleVC; 19 | @class ChatGroup; 20 | @class ChatUser; 21 | 22 | @interface ChatMessagesVC : UIViewController { 23 | BOOL keyboardShow; 24 | CGFloat heightTable; 25 | CGFloat originalViewHeight; 26 | ChatMessagesTVC *containerTVC; 27 | 28 | } 29 | 30 | @property (strong, nonatomic) ChatImageCache *imageCache; 31 | @property (strong, nonatomic) QBPopupMenu *popupMenu; 32 | @property (strong, nonatomic) ChatConversationHandler *conversationHandler; 33 | @property (strong, nonatomic) ChatUser *me; 34 | 35 | @property (strong, nonatomic) ChatUser *recipient; 36 | @property (strong, nonatomic) NSString *senderId; 37 | @property (strong, nonatomic) NSString *senderFullname; 38 | @property (strong, nonatomic) NSString *textToSendAsChatOpens; 39 | @property (strong, nonatomic) NSDictionary *attributesToSendAsChatOpens; 40 | @property (assign, nonatomic) BOOL bottomReached; 41 | @property (strong, nonatomic) UILabel *unreadLabel; 42 | @property (assign, nonatomic) int unread_count; 43 | @property (strong, nonatomic) UIStoryboard *profileSB; 44 | @property (strong, nonatomic) UINavigationController *profileNC; 45 | @property (strong, nonatomic) NSString *selectedText; 46 | @property (strong, nonatomic) UITapGestureRecognizer *tapToDismissKB; 47 | @property (assign, nonatomic) BOOL isModal; 48 | @property (nonatomic, copy) void (^dismissModalCallback)(); 49 | 50 | // subscribers 51 | @property (assign, nonatomic) NSUInteger added_handle; 52 | @property (assign, nonatomic) NSUInteger changed_handle; 53 | @property (assign, nonatomic) NSUInteger deleted_handle; 54 | 55 | 56 | // user thumbs 57 | @property (nonatomic, retain) NSMutableDictionary *imageDownloadsInProgress; 58 | 59 | // imagepicker 60 | @property (nonatomic, strong) UIImagePickerController *imagePickerController; 61 | @property (nonatomic, strong) UIImagePickerController *photoLibraryController; 62 | @property (nonatomic, strong) UIImage *scaledImage; 63 | @property (strong, nonatomic) UIImage *bigImage; 64 | 65 | // GROUP_MOD 66 | @property (strong, nonatomic) ChatGroup *group; 67 | 68 | // titleView references 69 | @property (strong, nonatomic) ChatTitleVC *titleVC; 70 | @property (weak, nonatomic) UIButton *usernameButton; 71 | @property (weak, nonatomic) UILabel *statusLabel; 72 | @property (weak, nonatomic) UIActivityIndicatorView *activityIndicator; 73 | // status 74 | @property (assign, nonatomic) NSUInteger connectedHandle; 75 | @property (assign, nonatomic) NSUInteger disconnectedHandle; 76 | //@property (strong, nonatomic) FIRDatabaseReference *connectedRef; 77 | //@property (assign, nonatomic) FIRDatabaseHandle connectedRefHandle; 78 | 79 | @property (strong, nonatomic) NSDate *lastOnline; 80 | //@property (strong, nonatomic) FIRDatabaseReference *onlineRef; 81 | //@property (strong, nonatomic) FIRDatabaseReference *lastOnlineRef; 82 | //@property (assign, nonatomic) FIRDatabaseHandle online_ref_handle; 83 | //@property (assign, nonatomic) FIRDatabaseHandle last_online_ref_handle; 84 | @property (assign, nonatomic) BOOL online; 85 | 86 | // sound 87 | @property (strong, nonatomic) NSTimer *messageTimer; 88 | @property (assign, nonatomic) BOOL messagesArriving; 89 | //@property (assign, nonatomic) double lastMessageArrivedTime; 90 | 91 | @property (weak, nonatomic) IBOutlet UIView *viewContainer; 92 | @property (weak, nonatomic) IBOutlet UITextField *messageTextField; 93 | @property (weak, nonatomic) IBOutlet UIButton *sendButton; 94 | @property (weak, nonatomic) IBOutlet UIView *bottomView; 95 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *layoutConstraintBottomTableTopBarMessage; 96 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *layoutContraintBottomBarMessageBottomView; 97 | - (IBAction)sendAction:(id)sender; 98 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomViewHeightConstraint; 99 | - (IBAction)addContentAction:(id)sender; 100 | 101 | -(void)dismissKeyboardFromTableView:(BOOL)activate; 102 | -(void)updateUnreadMessagesCount; 103 | @property (weak, nonatomic) IBOutlet UIBarButtonItem *stopTestButton; 104 | - (IBAction)stopTestAction:(id)sender; 105 | 106 | - (IBAction)unwindToMessagesVC:(UIStoryboardSegue*)sender; 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /Chat21Core/it.lproj/Chat.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Chat.strings 3 | chat21 4 | 5 | Created by Andrea Sponziello on 05/02/2018. 6 | Copyright © 2018 Frontiere21. All rights reserved. 7 | */ 8 | 9 | //"CancelLKey" = "Annulla"; 10 | //"DoneLKey" = "Fatto"; 11 | //"CloseLKey" = "Chiudi"; 12 | //"BackLKey" = "Indietro"; 13 | //"SendLKey" = "Pubblica"; 14 | //"RequiredPlaceholderLKey" = "Obbligatorio"; 15 | //"ConnectionErrorLKey" = "Errore di Connessione!"; 16 | "TimeToStringDateFormat" = "d MMMM"; 17 | //"Saving" = "Sto salvando..."; 18 | //"NetworkErrorTitle" = "Errore rete"; 19 | //"NetworkError" = "Si è verificato un errore di rete durante la connessione."; 20 | 21 | "FewSecondsAgoLKey" = "pochi secondi fa"; 22 | "AboutAMinuteAgoLKey" = "circa un minuto fa"; 23 | "MinutesAgoLKey" = "minuti fa"; 24 | "AboutAnHourAgoLKey" = "circa un'ora fa"; 25 | "HoursAgoLKey" = "ore fa"; 26 | 27 | // *** tabbar menu labels *** 28 | 29 | "Authenticating" = "Autenticazione in corso..."; 30 | "UsernamePasswordInvalid" = "Username o password non validi"; 31 | "NetworkError" = "Si è verificato un errore di rete"; 32 | "NetworkErrorTitle" = "Errore di rete"; 33 | "UnknownError" = "Errore sconosciuto"; 34 | 35 | "Chat" = "Chat"; 36 | "Profile" = "Profilo"; 37 | "Help" = "Un aiuto?"; 38 | 39 | // *** chat labels *** 40 | 41 | "NoConversationsYet" = "Ancora nessuna conversazione."; 42 | "DeleteConversationTitle" = "Elimino questa Conversazione?"; 43 | "DeleteConversationMessage" = "Sei sicuro? Non preoccuparti dei messaggi, sono al sicuro. Ti basta riaprire la chat con un gruppo o un utente per vederli di nuovo."; 44 | "ChatConnected" = "connesso"; 45 | "ChatDisconnected" = "connessione..."; 46 | "ChatSend" = "Invia"; 47 | 48 | // settings view 49 | "ChatTerms" = "Termini e Condizioni"; 50 | "ChatPrivacy" = "Privacy"; 51 | "ChatLicences" = "Licenze"; 52 | 53 | // select user (new message) view 54 | "NewMessage" = "Nuovo Messaggio"; 55 | "NewGroup" = "Nuovo gruppo"; 56 | "Groups" = "Gruppi"; 57 | 58 | "last seen" = "ultimo accesso"; 59 | "group summary" = "gruppo"; 60 | 61 | // chat profile 62 | "Synchronizing contacts" = "Sincronizzazione contatti..."; 63 | "ChatProfile" = "Profilo"; 64 | "ChatSignoutAlert" = "Sei sicuro?"; 65 | "ChatCancel" = "Annulla"; 66 | "ChatSave" = "Salva"; 67 | "ChatSettingsMenuOption" = "Impostazioni"; 68 | "ChatHelpMenuOption" = "Aiuto e assistenza"; 69 | "ChatLogoutMenuOption" = "Esci"; 70 | "ChatLogoutChatOption" = "Chat"; 71 | "Open link" = "Apri in Safari"; 72 | "Copy link" = "Copia"; 73 | "Group administrator" = "amministratore"; 74 | "Select group" = "Seleziona gruppo"; 75 | "Member info" = "Info"; 76 | "Member remove" = "Rimuovi membro"; 77 | "Send message" = "Invia messaggio"; 78 | "Members" = "Membri"; 79 | "Add user to group" = "Aggiungi %@ al gruppo %@?"; 80 | "Add" = "Aggiungi"; 81 | "Just in group" = "Già nel gruppo"; 82 | "Add member" = "Aggiungi Membro"; 83 | "You was added to group" = "Sei stato aggiunto al gruppo \"%@\""; 84 | "You created the group" = "Hai creato il gruppo \"%@\""; 85 | "You" = "Tu"; 86 | "ShortDateFormat" = "d/M/yyyy"; 87 | "yesterday" = "ieri"; 88 | "today" = "oggi"; 89 | "group info" = "Info gruppo"; 90 | "new group" = "Nuovo gruppo"; 91 | "create group info message" = "Imposta il nome del gruppo e la sua icona (opzionale)"; 92 | "group name" = "Nome del gruppo"; 93 | "Creating group" = "Creo gruppo..."; 94 | "Group creation error" = "Errore creazione gruppo"; 95 | "Add Photo" = "Aggiungi foto"; 96 | "next" = "Avanti"; 97 | "cancel" = "Annulla"; 98 | "create" = "Crea"; 99 | "contact name" = "Nome del contatto"; 100 | "remove" = "Rimuovi"; 101 | "add members" = "Aggiungi membri"; 102 | "group created by" = "Gruppo creato da %@"; 103 | "group created on" = "Creato il %@"; 104 | "Group name placeholder" = "Dai un nome al gruppo"; 105 | "GroupNameTitle" = "Nome gruppo"; 106 | "all contacts" = "Tutti i contatti"; 107 | "change fullname" = "Nome visualizzato"; 108 | "change password" = "Cambia Password"; 109 | "settingsLegalSection" = "Legale"; 110 | "Remove this member?" = "Vuoi rimuovere questo membro?"; 111 | "Remove" = "Rimuovi"; 112 | "copy" = "Copia"; 113 | "info" = "Info"; 114 | "resend" = "Invia di nuovo"; 115 | "digit message" = "Digita un messaggio"; 116 | "ReadConversationAction" = "Letto"; 117 | "UnreadConversationAction" = "Non letto"; 118 | 119 | "ArchiveConversationAction" = "Chiudi"; 120 | "UnarchiveConversationAction" = "Riapri"; 121 | "ArchivedConversationsTitle" = "Chiuse"; 122 | "ArchivedBadgeLabel" = " CHIUSA "; 123 | 124 | //"ArchiveConversationAction" = "Archivia"; 125 | //"UnarchiveConversationAction" = "Ripristina"; 126 | //"ArchivedConversationsTitle" = "Archiviate"; 127 | //"ArchivedBadgeLabel" = " ARCHIVIATA "; 128 | 129 | "Network Error Title" = "Errore di rete"; 130 | "Network Error Message" = "Si è verificato un errore di rete."; 131 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatImageMessageCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatImageMessageCell.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 06/05/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatImageMessageCell.h" 10 | #import "ChatMessage.h" 11 | #import "ChatUtil.h" 12 | #import "ChatLocal.h" 13 | #import "ChatMessageMetadata.h" 14 | #import "ChatImageCache.h" 15 | #import "ChatImageWrapper.h" 16 | #import "ChatImageUtil.h" 17 | 18 | @implementation ChatImageMessageCell 19 | 20 | - (void)awakeFromNib { 21 | [super awakeFromNib]; 22 | // Initialization code 23 | } 24 | 25 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 26 | [super setSelected:selected animated:animated]; 27 | 28 | // Configure the view for the selected state 29 | } 30 | 31 | -(void)configure:(ChatMessage *)message messages:(NSArray *)messages indexPath:(NSIndexPath *)indexPath viewController:(UIViewController *)viewController rowComponents:(NSMutableDictionary *)rowComponents imageCache:(ChatImageCache *)imageCache completion:(void(^)(UIImage *image)) callback { 32 | NSString *dateChat; 33 | NSDate *dateToday = [NSDate date]; 34 | int numberDaysPrevChat = 0; 35 | int numberDaysNextChat = 0; 36 | ChatMessage *previousMessage; 37 | ChatMessage *nextMessage; 38 | UIView *backBox = self.messageBackgroundView; 39 | backBox.layer.masksToBounds = YES; 40 | backBox.layer.cornerRadius = 8.0; 41 | 42 | self.selectionStyle = UITableViewCellSelectionStyleNone; 43 | self.timeLabel.text = [message dateFormattedForListView]; 44 | UILabel *labelDay = self.dateLabel;//(UILabel *)[cell viewWithTag:30]; 45 | if (indexPath.row>0) { 46 | previousMessage = (ChatMessage *)[messages objectAtIndex:(indexPath.row-1)]; 47 | if(messages.count > (indexPath.row+1)){ 48 | nextMessage = (ChatMessage *)[messages objectAtIndex:(indexPath.row+1)]; 49 | numberDaysNextChat = (int)[ChatUtil daysBetweenDate:message.date andDate:nextMessage.date]; 50 | } 51 | numberDaysPrevChat = (int)[ChatUtil daysBetweenDate:previousMessage.date andDate:message.date]; 52 | dateChat = [self formatDateMessage:numberDaysPrevChat message:message row:indexPath.row]; 53 | } 54 | else { 55 | numberDaysPrevChat = (int)[ChatUtil daysBetweenDate:message.date andDate:dateToday]; 56 | dateChat = [self formatDateMessage:numberDaysPrevChat message:message row:indexPath.row]; 57 | } 58 | if (numberDaysPrevChat>0) { 59 | labelDay.text = dateChat; 60 | } 61 | else { 62 | labelDay.text = @""; 63 | } 64 | UIImageView *status_image_view = self.statusImageView; 65 | [ChatMessageCell setStatusImage:message statusImage:status_image_view]; 66 | 67 | // image 68 | 69 | // events 70 | UIGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:viewController action:@selector(tapOnCell:)]; 71 | UIGestureRecognizer *longTapGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:viewController action:@selector(longTapOnCell:)]; 72 | [self.messageImageView addGestureRecognizer:tapGestureRecognizer]; 73 | [self.messageImageView addGestureRecognizer:longTapGestureRecognizer]; 74 | 75 | self.messageImageView.userInteractionEnabled = YES; 76 | 77 | UIImage *cached_image = ((ChatImageWrapper *)[imageCache getImage:message.messageId]).image; 78 | if (cached_image) { 79 | CGSize size = [message imageSize];//[ChatMessageCell computeImageSize:message]; 80 | self.imageHeightConstraint.constant = size.height; 81 | self.imageWidthConstraint.constant = size.width; 82 | self.messageImageView.image = cached_image; 83 | return; 84 | } 85 | else { 86 | CGSize size = [message imageSize]; //[ChatMessageCell computeImageSize:message]; 87 | self.imageHeightConstraint.constant = size.height; 88 | self.imageWidthConstraint.constant = size.width; 89 | // UIImage *placeholder_image = [message imagePlaceholder]; 90 | self.messageImageView.image = nil; 91 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{ 92 | if ([message imageExistsInMediaFolder]) { 93 | UIImage *image = [message imageFromMediaFolder]; 94 | UIImage *scaled_image = [ChatImageUtil scaleImage:image toSize:size]; 95 | dispatch_async(dispatch_get_main_queue(), ^{ 96 | callback(scaled_image); 97 | }); 98 | } 99 | else { 100 | dispatch_async(dispatch_get_main_queue(), ^{ 101 | callback(nil); // -> start download 102 | }); 103 | } 104 | }); 105 | } 106 | } 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /Chat21UI/QBPopupMenu/QBPopupMenuPagenatorView.m: -------------------------------------------------------------------------------- 1 | // 2 | // QBPopupMenuPagenatorView.m 3 | // QBPopupMenu 4 | // 5 | // Created by Tanaka Katsuma on 2013/11/23. 6 | // Copyright (c) 2013年 Katsuma Tanaka. All rights reserved. 7 | // 8 | 9 | #import "QBPopupMenuPagenatorView.h" 10 | 11 | @implementation QBPopupMenuPagenatorView 12 | 13 | + (CGFloat)pagenatorWidth 14 | { 15 | return 10 + 10 * 2; 16 | } 17 | 18 | + (instancetype)leftPagenatorViewWithTarget:(id)target action:(SEL)action 19 | { 20 | return [[self alloc] initWithArrowDirection:QBPopupMenuPagenatorDirectionLeft target:target action:action]; 21 | } 22 | 23 | + (instancetype)rightPagenatorViewWithTarget:(id)target action:(SEL)action 24 | { 25 | return [[self alloc] initWithArrowDirection:QBPopupMenuPagenatorDirectionRight target:target action:action]; 26 | } 27 | 28 | - (instancetype)initWithArrowDirection:(QBPopupMenuPagenatorDirection)arrowDirection target:(id)target action:(SEL)action 29 | { 30 | self = [super initWithItem:nil]; 31 | 32 | if (self) { 33 | // Property settings 34 | self.target = target; 35 | self.action = action; 36 | 37 | // Set arrow image 38 | UIImage *arrowImage = [self arrowImageWithSize:CGSizeMake(10, 10) 39 | direction:arrowDirection 40 | highlighted:NO]; 41 | [self.button setImage:arrowImage forState:UIControlStateNormal]; 42 | 43 | UIImage *highlightedArrowImage = [self arrowImageWithSize:CGSizeMake(10, 10) 44 | direction:arrowDirection 45 | highlighted:YES]; 46 | [self.button setImage:highlightedArrowImage forState:UIControlStateHighlighted]; 47 | } 48 | 49 | return self; 50 | } 51 | 52 | 53 | #pragma mark - Actions 54 | 55 | - (void)performAction 56 | { 57 | if (self.target && self.action) { 58 | [self.target performSelector:self.action withObject:nil afterDelay:0]; 59 | } 60 | } 61 | 62 | 63 | #pragma mark - Updating the View 64 | 65 | - (CGSize)sizeThatFits:(CGSize)size 66 | { 67 | CGSize buttonSize = [self.button sizeThatFits:CGSizeZero]; 68 | buttonSize.width = [[self class] pagenatorWidth]; 69 | 70 | return buttonSize; 71 | } 72 | 73 | - (UIImage *)arrowImageWithSize:(CGSize)size direction:(QBPopupMenuPagenatorDirection)direction highlighted:(BOOL)highlighted 74 | { 75 | UIGraphicsBeginImageContextWithOptions(size, NO, 0); 76 | 77 | // Draw arrow 78 | [self drawArrowInRect:CGRectMake(0, 0, size.width, size.height) direction:direction highlighted:highlighted]; 79 | 80 | // Create image from buffer 81 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 82 | 83 | UIGraphicsEndImageContext(); 84 | 85 | return image; 86 | } 87 | 88 | 89 | #pragma mark - Creating Paths 90 | 91 | - (CGMutablePathRef)arrowPathInRect:(CGRect)rect direction:(QBPopupMenuPagenatorDirection)direction 92 | { 93 | CGMutablePathRef path = CGPathCreateMutable(); 94 | 95 | switch (direction) { 96 | case QBPopupMenuPagenatorDirectionLeft: 97 | { 98 | CGPathMoveToPoint(path, NULL, rect.origin.x + rect.size.width, rect.origin.y); 99 | CGPathAddLineToPoint(path, NULL, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height); 100 | CGPathAddLineToPoint(path, NULL, rect.origin.x, rect.origin.y + rect.size.height / 2.0); 101 | CGPathCloseSubpath(path); 102 | } 103 | break; 104 | 105 | case QBPopupMenuPagenatorDirectionRight: 106 | { 107 | CGPathMoveToPoint(path, NULL, rect.origin.x, rect.origin.y); 108 | CGPathAddLineToPoint(path, NULL, rect.origin.x, rect.origin.y + rect.size.height); 109 | CGPathAddLineToPoint(path, NULL, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height / 2.0); 110 | CGPathCloseSubpath(path); 111 | } 112 | break; 113 | 114 | default: 115 | break; 116 | } 117 | 118 | return path; 119 | } 120 | 121 | 122 | #pragma mark - Drawing 123 | 124 | - (void)drawArrowInRect:(CGRect)rect direction:(QBPopupMenuPagenatorDirection)direction highlighted:(BOOL)highlighted 125 | { 126 | CGContextRef context = UIGraphicsGetCurrentContext(); 127 | CGContextSaveGState(context); 128 | 129 | CGMutablePathRef path = [self arrowPathInRect:rect direction:direction]; 130 | CGContextAddPath(context, path); 131 | 132 | CGContextSetFillColorWithColor(context, [[UIColor whiteColor] CGColor]); 133 | CGContextFillPath(context); 134 | 135 | CGPathRelease(path); 136 | 137 | CGContextRestoreGState(context); 138 | } 139 | 140 | @end 141 | -------------------------------------------------------------------------------- /Chat21Tests/ChatMessagesPersistenceTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatMessagesPersistenceTests.m 3 | // chat21 4 | // 5 | // Created by Andrea Sponziello on 16/04/2018. 6 | // Copyright © 2018 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ChatMessage.h" 11 | #import "ChatDB.h" 12 | #import "ChatMessageMetadata.h" 13 | 14 | @interface ChatMessagesPersistenceTests : XCTestCase 15 | 16 | @end 17 | 18 | @implementation ChatMessagesPersistenceTests 19 | 20 | - (void)setUp { 21 | [super setUp]; 22 | // Put setup code here. This method is called before the invocation of each test method in the class. 23 | } 24 | 25 | - (void)tearDown { 26 | // Put teardown code here. This method is called after the invocation of each test method in the class. 27 | [super tearDown]; 28 | } 29 | 30 | -(ChatMessage *)textMessage { 31 | ChatMessage *message = [[ChatMessage alloc] init]; 32 | NSString * messageId = [[NSUUID UUID] UUIDString]; 33 | message.messageId = messageId; 34 | message.sender = @"sender"; 35 | message.senderFullname = @"Sender Full Name"; 36 | message.recipient = @"recipient"; 37 | message.recipientFullName = @"Recipient Full Name"; 38 | message.text = @"Text message"; 39 | NSDate *now = [[NSDate alloc] init]; 40 | message.date = now; 41 | message.status = MSG_STATUS_SENDING; 42 | message.conversationId = message.recipient; 43 | message.lang = @"it"; 44 | return message; 45 | } 46 | 47 | - (void)testSaveTextMessage { 48 | ChatMessage *message = [self textMessage]; 49 | message.mtype = MSG_TYPE_TEXT; 50 | message.subtype = @"test subtype"; 51 | message.channel_type = MSG_CHANNEL_TYPE_DIRECT; 52 | // save 53 | ChatDB *db = [ChatDB getSharedInstance]; 54 | [db insertMessageIfNotExists:message]; 55 | // retrive 56 | ChatMessage *message_from_db = [db getMessageById:message.messageId]; 57 | XCTAssertTrue([message_from_db.messageId isEqualToString:message.messageId]); 58 | XCTAssertTrue([message_from_db.sender isEqualToString:message.sender]); 59 | XCTAssertTrue([message_from_db.senderFullname isEqualToString:message.senderFullname]); 60 | XCTAssertTrue([message_from_db.recipient isEqualToString:message.recipient]); 61 | XCTAssertTrue([message_from_db.recipientFullName isEqualToString:message.recipientFullName]); 62 | XCTAssertTrue([message_from_db.text isEqualToString:message.text]); 63 | // XCTAssertTrue([message_from_db.date timeIntervalSinceDate:message.date] == 0.000000); 64 | XCTAssertTrue(message_from_db.date.timeIntervalSince1970 == message.date.timeIntervalSince1970); 65 | XCTAssertTrue(message_from_db.status == message.status); 66 | XCTAssertTrue([message_from_db.conversationId isEqualToString:message.conversationId]); 67 | XCTAssertTrue([message_from_db.lang isEqualToString:message.lang]); 68 | XCTAssertTrue(message_from_db.archived == true); 69 | XCTAssertTrue([message_from_db.mtype isEqualToString:message.mtype]); 70 | XCTAssertTrue([message_from_db.subtype isEqualToString:message.subtype]); 71 | XCTAssertTrue([message_from_db.channel_type isEqualToString:message.channel_type]); 72 | } 73 | 74 | - (void)testSaveImageMessage { 75 | ChatMessage *message = [self textMessage]; 76 | message.mtype = MSG_TYPE_IMAGE; 77 | message.subtype = @"test subtype"; 78 | message.channel_type = MSG_CHANNEL_TYPE_DIRECT; 79 | // message.imageURL = @"http://testimageurl"; 80 | message.metadata.src = @"http://testimageurl"; 81 | message.imageFilename = @"image-test-filename.png"; 82 | // save 83 | ChatDB *db = [ChatDB getSharedInstance]; 84 | [db insertMessageIfNotExists:message]; 85 | // retrive 86 | ChatMessage *message_from_db = [db getMessageById:message.messageId]; 87 | XCTAssertTrue([message_from_db.messageId isEqualToString:message.messageId]); 88 | XCTAssertTrue([message_from_db.sender isEqualToString:message.sender]); 89 | XCTAssertTrue([message_from_db.senderFullname isEqualToString:message.senderFullname]); 90 | XCTAssertTrue([message_from_db.recipient isEqualToString:message.recipient]); 91 | XCTAssertTrue([message_from_db.recipientFullName isEqualToString:message.recipientFullName]); 92 | XCTAssertTrue([message_from_db.text isEqualToString:message.text]); 93 | XCTAssertTrue(message_from_db.date.timeIntervalSince1970 == message.date.timeIntervalSince1970); 94 | XCTAssertTrue(message_from_db.status == message.status); 95 | XCTAssertTrue([message_from_db.conversationId isEqualToString:message.conversationId]); 96 | XCTAssertTrue([message_from_db.lang isEqualToString:message.lang]); 97 | XCTAssertTrue(message_from_db.archived == true); 98 | XCTAssertTrue([message_from_db.mtype isEqualToString:message.mtype]); 99 | XCTAssertTrue([message_from_db.subtype isEqualToString:message.subtype]); 100 | XCTAssertTrue([message_from_db.channel_type isEqualToString:message.channel_type]); 101 | XCTAssertTrue([message_from_db.metadata.src isEqualToString:message.metadata.src]); 102 | XCTAssertTrue([message_from_db.imageFilename isEqualToString:message.imageFilename]); 103 | } 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /Chat21UI/xib/status_title_ios11.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 33 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /Chat21UI/view/ChatMessageCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatMessageCell.m 3 | // Chat21 4 | // 5 | // Created by Andrea Sponziello on 08/03/16. 6 | // Copyright © 2016 Frontiere21. All rights reserved. 7 | // 8 | 9 | #import "ChatMessageCell.h" 10 | #import "ChatMessage.h" 11 | #import "ChatMessageComponents.h" 12 | #import "ChatUtil.h" 13 | #import "ChatLocal.h" 14 | #import "ChatMessageMetadata.h" 15 | #import "ChatStyles.h" 16 | 17 | @implementation ChatMessageCell 18 | 19 | -(NSString*)formatDateMessage:(int)numberDaysBetweenChats message:(ChatMessage*)message row:(CGFloat)row { 20 | NSString *dateChat; 21 | if(numberDaysBetweenChats>0 || row==0){ 22 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 23 | NSDate *today; 24 | today = [NSDate date]; 25 | int days = (int)[ChatUtil daysBetweenDate:message.date andDate:today]; 26 | if(days==0){ 27 | dateChat = [ChatLocal translate:@"today"]; 28 | } 29 | else if(days==1){ 30 | dateChat = [ChatLocal translate:@"yesterday"]; 31 | } 32 | else if(days<8){ 33 | [dateFormatter setDateFormat:@"EEEE"]; 34 | dateChat = [dateFormatter stringFromDate:message.date]; 35 | } 36 | else{ 37 | [dateFormatter setDateFormat:@"dd MMM"]; 38 | dateChat = [dateFormatter stringFromDate:message.date]; 39 | } 40 | } 41 | return dateChat; 42 | } 43 | 44 | -(NSString *)displayUserOfMessage:(ChatMessage *)m { 45 | NSString *displayName; 46 | 47 | // use fullname if available 48 | if (m.senderFullname) { 49 | NSString *trimmedFullname = [m.senderFullname stringByTrimmingCharactersInSet: 50 | [NSCharacterSet whitespaceCharacterSet]]; 51 | if (trimmedFullname.length > 0 && ![trimmedFullname isEqualToString:@"(null)"]) { 52 | displayName = trimmedFullname; 53 | } 54 | } 55 | 56 | // if fullname not available use username instead 57 | if (!displayName) { 58 | displayName = m.sender; 59 | } 60 | 61 | return displayName; 62 | } 63 | 64 | -(void)attributedString:(UILabel *)label text:(ChatMessage *)message indexPath:(NSIndexPath *)indexPath rowComponents:(NSDictionary *)rowComponents right_cell:(BOOL)right_cell{ 65 | ChatStyles *styles = [ChatStyles sharedInstance]; 66 | // consider use of: https://github.com/TTTAttributedLabel/TTTAttributedLabel 67 | NSString *text = message.text; 68 | if (!text) { 69 | text = @""; 70 | } 71 | UIColor *textColor; 72 | if (right_cell) { 73 | textColor = styles.ballonRightLinkColor; 74 | } 75 | else { 76 | textColor = styles.ballonLeftTextColor; 77 | } 78 | NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:text]; 79 | [attributedString addAttributes:@{NSFontAttributeName: label.font} range:NSMakeRange(0, attributedString.string.length)]; 80 | ChatMessageComponents *components = [rowComponents objectForKey:message.messageId]; 81 | NSArray *urlMatches = components.urlsMatches; 82 | if (urlMatches) { 83 | for (NSTextCheckingResult *match in urlMatches) { 84 | [attributedString addAttribute:NSForegroundColorAttributeName value:textColor range:match.range]; 85 | } 86 | } 87 | NSArray *chatLinkMatches = components.chatLinkMatches; 88 | if (chatLinkMatches) { 89 | for (NSTextCheckingResult *match in chatLinkMatches) { 90 | [attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor brownColor] range:match.range]; 91 | } 92 | } 93 | label.attributedText = attributedString; 94 | } 95 | 96 | +(void)setStatusImage:(ChatMessage *)message statusImage:(UIImageView *)status_image_view { 97 | // NSLog(@"status: message.text: %@", message.text); 98 | switch (message.status) { 99 | case MSG_STATUS_SENDING: 100 | status_image_view.image = [UIImage imageNamed:@"chat_watch"]; 101 | break; 102 | case MSG_STATUS_UPLOADING: 103 | status_image_view.image = [UIImage imageNamed:@"chat_watch"]; 104 | break; 105 | case MSG_STATUS_SENT: 106 | status_image_view.image = [UIImage imageNamed:@"chat_check"]; 107 | break; 108 | case MSG_STATUS_RETURN_RECEIPT: 109 | status_image_view.image = [UIImage imageNamed:@"chat_double_check"]; 110 | break; 111 | case MSG_STATUS_FAILED: 112 | status_image_view.image = [UIImage imageNamed:@"chat_failed"]; 113 | break; 114 | default: 115 | break; 116 | } 117 | } 118 | 119 | //+(CGSize)computeImageSize:(ChatMessage *)message { 120 | // float max_width = 200; 121 | // float max_height = 200; 122 | // float imageview_scale_w = max_width / message.metadata.width; //image.size.width; 123 | // float imageview_height = message.metadata.width * imageview_scale_w; 124 | // if (imageview_height > max_height) { 125 | // imageview_height = 200; 126 | // } 127 | // return CGSizeMake(max_width, imageview_height); 128 | //} 129 | 130 | //+(BOOL)hasValidImageMetadata:(ChatMessage *)message { 131 | // CGSize size = [ChatMessageCell computeImageSize:message]; 132 | // if (size.height > 0 && size.width > 0) { 133 | // return YES; 134 | // } 135 | // return NO; 136 | //} 137 | 138 | @end 139 | --------------------------------------------------------------------------------