Files
snoop_device/third_party/socket.io-client-cpp/examples/iOS/SioChatDemo/SioChatDemo/CRViewController.mm
2025-09-08 14:36:31 +03:00

454 lines
14 KiB
Plaintext

//
// CRViewController.m
// ChatRoom
//
// Created by Melo Yao on 3/30/15.
//
#import "CRViewController.h"
#include "sio_client.h"
typedef enum MessageFlag
{
Message_System,
Message_Other,
Message_You
};
@interface MessageItem : NSObject
@property NSString* message;
@property MessageFlag flag; //0 system info, 1 other message, 2 your message
@end
@implementation MessageItem
@end
@interface CRViewController ()<UITableViewDataSource,UITableViewDelegate,NSURLConnectionDelegate,NSURLConnectionDataDelegate,UITextFieldDelegate>
{
sio::client *_io;
NSMutableArray *_receivedMessage;
NSMutableSet *_typingUsers;
NSString* _name;
NSInteger _userCount;
NSTimer* _inputTimer;
}
@property (weak, nonatomic) IBOutlet UILabel *infoLabel;
@property (weak, nonatomic) IBOutlet UILabel *typingLabel;
@property (strong, nonatomic) IBOutlet UIView *loginPage;
@property (weak, nonatomic) IBOutlet UITextField *nickName;
- (IBAction)onSend:(id)sender;
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (weak, nonatomic) IBOutlet UIButton *sendBtn;
@property (weak, nonatomic) IBOutlet UITextField *messageField;
@property (weak, nonatomic) IBOutlet UIView *messageArea;
-(void)onNewMessage:(NSString*) message from:(NSString*) name;
-(void)onUserJoined:(NSString*)user participants:(NSInteger) num;
-(void)onUserLeft:(NSString*) user participants:(NSInteger) num;
-(void)onUserTyping:(NSString*) user;
-(void)onUserStopTyping:(NSString*) user;
-(void)onLogin:(NSInteger) numParticipants;
-(void)onConnected;
-(void)onDisconnected;
-(void) updateUser:(NSString*)user count:(NSInteger) num joinOrLeft:(BOOL) isJoin;
@end
using namespace std;
using namespace sio;
void OnNewMessage(CFTypeRef ctrl,string const& name,sio::message::ptr const& data,bool needACK,sio::message::list ackResp)
{
if(data->get_flag() == message::flag_object)
{
NSString* msg = [NSString stringWithUTF8String:data->get_map()["message"]->get_string().data()];
NSString* user = [NSString stringWithUTF8String:data->get_map()["username"]->get_string().data()];
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onNewMessage:msg from:user];
});
}
}
void OnTyping(CFTypeRef ctrl,string const& name,sio::message::ptr const& data,bool needACK,sio::message::list ackResp)
{
if(data->get_flag() == message::flag_object)
{
NSString* user = [NSString stringWithUTF8String:data->get_map()["username"]->get_string().data()];
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onUserTyping:user];
});
}
}
void OnStopTyping(CFTypeRef ctrl,string const& name,sio::message::ptr const& data,bool needACK,sio::message::list ackResp)
{
if(data->get_flag() == message::flag_object)
{
NSString* user = [NSString stringWithUTF8String:data->get_map()["username"]->get_string().data()];
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onUserStopTyping:user];
});
}
}
void OnUserJoined(CFTypeRef ctrl, string const& name, sio::message::ptr const& data, bool needACK, sio::message::list ackResp)
{
if(data->get_flag() == message::flag_object)
{
NSString* user = [NSString stringWithUTF8String:data->get_map()["username"]->get_string().data()];
NSInteger num = data->get_map()["numUsers"]->get_int();
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onUserJoined:user participants:num];
});
}
}
void OnUserLeft(CFTypeRef ctrl, string const& name, sio::message::ptr const& data, bool needACK, sio::message::list ackResp)
{
if(data->get_flag() == message::flag_object)
{
NSString* user = [NSString stringWithUTF8String:data->get_map()["username"]->get_string().data()];
NSInteger num = data->get_map()["numUsers"]->get_int();
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onUserLeft:user participants:num];
});
}
}
void OnLogin(CFTypeRef ctrl, string const& name, sio::message::ptr const& data, bool needACK, sio::message::list ackResp)
{
if(data->get_flag() == message::flag_object)
{
NSInteger num = data->get_map()["numUsers"]->get_int();
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onLogin:num];
});
}
}
void OnConnected(CFTypeRef ctrl,std::string nsp)
{
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onConnected];
});
}
void OnFailed(CFTypeRef ctrl)
{
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onDisconnected];
});
}
void OnClose(CFTypeRef ctrl,sio::client::close_reason const& reason)
{
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onDisconnected];
});
}
@implementation CRViewController
-(void)awakeFromNib
{
_receivedMessage = [NSMutableArray array];
_typingUsers = [NSMutableSet set];
_io = new sio::client();
[self.loginPage setFrame:self.view.bounds];
[self.view addSubview:self.loginPage];
self.nickName.enabled = YES;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)viewWillAppear:(BOOL)animated
{
_io->set_socket_open_listener(std::bind(&OnConnected, (__bridge CFTypeRef)self,std::placeholders::_1));
_io->set_close_listener(std::bind(&OnClose, (__bridge CFTypeRef)self, std::placeholders::_1));
_io->set_fail_listener(std::bind(&OnFailed, (__bridge CFTypeRef)self));
}
-(void)keyboardWillShow:(NSNotification*)notification
{
CGFloat height = [[notification.userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height;
// Animate the current view out of the way
[UIView animateWithDuration:0.35 animations:^{
self.messageArea.transform = CGAffineTransformMakeTranslation(0, -height);
}];
}
-(void)keyboardWillHide {
[UIView animateWithDuration:0.35 animations:^{
self.messageArea.transform = CGAffineTransformIdentity;
}];
}
-(void)viewDidDisappear:(BOOL)animated
{
_io->socket()->off_all();
_io->set_open_listener(nullptr);
_io->set_close_listener(nullptr);
_io->close();
}
-(void)onNewMessage:(NSString*) message from:(NSString*) name
{
MessageItem *item = [[MessageItem alloc] init];
item.flag = [name isEqualToString:_name]?Message_You:Message_Other;
item.message = item.flag == Message_You? [NSString stringWithFormat:@"%@:%@",message,name]:[NSString stringWithFormat:@"%@:%@",name,message];
[_receivedMessage addObject:item];
[_tableView reloadData];
if(![_messageField isFirstResponder])
{
[_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[_receivedMessage count]-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}
-(void)onUserJoined:(NSString*)user participants:(NSInteger) num
{
_userCount = num;
[self updateUser:user count:num joinOrLeft:YES];
}
-(void)onUserLeft:(NSString*) user participants:(NSInteger) num
{
[_typingUsers removeObject:user];//protective removal.
[self updateTyping];
_userCount = num;
[self updateUser:user count:num joinOrLeft:NO];
}
-(void)onUserTyping:(NSString*) user
{
[_typingUsers addObject:user];
[self updateTyping];
}
-(void)onUserStopTyping:(NSString*) user
{
[_typingUsers removeObject:user];
[self updateTyping];
}
-(void)onLogin:(NSInteger) numParticipants
{
_name = _nickName.text;
_userCount = numParticipants;
[self.loginPage removeFromSuperview];
[self updateUser:nil count:_userCount joinOrLeft:YES];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)onSend:(id)sender {
if ([_messageField.text length]>0 && [_name length]>0) {
_io->socket()->emit("new message",[_messageField.text UTF8String]);
MessageItem *item = [[MessageItem alloc] init];
item.flag = Message_You;
item.message = [NSString stringWithFormat:@"%@:You",_messageField.text];
[_receivedMessage addObject:item];
[_tableView reloadData];
[_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[_receivedMessage count]-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
self.messageField.text = nil;
[self.messageField resignFirstResponder];
}
-(void)onConnected
{
_io->socket()->emit("add user", [self.nickName.text UTF8String]);
}
-(void)onDisconnected
{
if([self.loginPage superview] == nil)
{
[self.view addSubview:self.loginPage];
}
self.nickName.enabled = YES;
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}
-(void) updateUser:(NSString*)user count:(NSInteger) num joinOrLeft:(BOOL) isJoin
{
_userCount = num;
MessageItem *item = [[MessageItem alloc] init];
item.flag = Message_System;
if (user) {
item.message = [NSString stringWithFormat:@"%@ %@\n%@",user,isJoin?@"joined":@"left",num==1?@"there's 1 participant":[NSString stringWithFormat:@"there are %ld participants",num]];
}
else
{
item.message = [NSString stringWithFormat:@"Welcome to Socket.IO Chat-\n%@",num==1?@"there's 1 participant":[NSString stringWithFormat:@"there are %ld participants",num]];
}
[_receivedMessage addObject:item];
[_tableView reloadData];
if(![_messageField isFirstResponder])
{
[_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[_receivedMessage count]-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}
-(void) inputTimeout
{
_inputTimer = nil;
_io->socket()->emit("stop typing", "");
}
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(textField == self.messageField)
{
if(_inputTimer.valid)
{
[_inputTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
}
else
{
_io->socket()->emit("typing", "");
_inputTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(inputTimeout) userInfo:nil repeats:NO];
}
}
return YES;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_receivedMessage count];
}
// Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:
// Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"Msg"];
MessageItem* item = [_receivedMessage objectAtIndex:indexPath.row];
cell.textLabel.text = item.message;
switch (item.flag) {
case Message_System:
cell.textLabel.textAlignment = NSTextAlignmentCenter;
[cell.textLabel setFont:[UIFont fontWithName:[cell.textLabel.font fontName] size:12]];
break;
case Message_Other:
[cell.textLabel setFont:[UIFont fontWithName:[cell.textLabel.font fontName] size:15]];
cell.textLabel.textAlignment = NSTextAlignmentLeft;
break;
case Message_You:
[cell.textLabel setFont:[UIFont fontWithName:[cell.textLabel.font fontName] size:15]];
cell.textLabel.textAlignment = NSTextAlignmentRight;
break;
default:
break;
}
return cell;
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if ([self.messageField isFirstResponder]) {
[self.messageField resignFirstResponder];
}
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
if (textField == self.nickName) {
if ([self.nickName.text length] > 0) {
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
using std::placeholders::_4;
socket::ptr socket = _io->socket();
socket->on("new message", std::bind(&OnNewMessage, (__bridge CFTypeRef)self, _1,_2,_3,_4));
socket->on("typing", std::bind(&OnTyping, (__bridge CFTypeRef)self, _1,_2,_3,_4));
socket->on("stop typing", std::bind(&OnStopTyping, (__bridge CFTypeRef)self, _1,_2,_3,_4));
socket->on("user joined", std::bind(&OnUserJoined, (__bridge CFTypeRef)self, _1,_2,_3,_4));
socket->on("user left", std::bind(&OnUserLeft, (__bridge CFTypeRef)self, _1,_2,_3,_4));
socket->on("login", std::bind(&OnLogin, (__bridge CFTypeRef)self, _1,_2,_3,_4));
_io->connect("ws://localhost:3000");
self.nickName.enabled = NO;
}
}
else if(textField == self.messageField)
{
[self onSend:textField];
}
return YES;
}
-(void)updateTyping
{
NSString* typingMsg = nil;
NSString* name = [_typingUsers anyObject];
if (name) {
if([_typingUsers count]>1)
{
typingMsg = [NSString stringWithFormat:@"%@ and %ld more are typing",name,[_typingUsers count]];
}
else
{
typingMsg =[NSString stringWithFormat:@"%@ is typing",name];
}
}
self.typingLabel.text = typingMsg;
}
- (void)dealloc
{
delete _io;
}
@end