Chat-O-Matic/application/Conversation.cpp
Jaidyn Ann ad1c7b5782 Receive lists and changes in room participants from protocols
Two new messages were added to the protocol API to do this:
M_ROOM_PARTICIPANTS, which can be used when someone joins a room, or
on joining a room to send a full list of users, and IM_ROOM_PARTICIPANT_LEFT,
for when a user has left the room/disconnected.

IM_SET_STATUS no longer assumes received data comes from contacts, but
any general user.

UserItem was made to reflect changes in the User's name.

Chat messages can now be reliably received in a given room. :)
2021-06-02 16:53:03 -05:00

306 lines
5.0 KiB
C++

/*
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include "Conversation.h"
#include <DateTimeFormat.h>
#include <Locale.h>
#include <StringList.h>
#include "CayaPreferences.h"
#include "CayaProtocolMessages.h"
#include "CayaRenderView.h"
#include "CayaUtils.h"
#include "ConversationItem.h"
#include "ConversationView.h"
#include "MainWindow.h"
#include "ProtocolLooper.h"
#include "ProtocolManager.h"
#include "Server.h"
#include "TheApp.h"
Conversation::Conversation(BString id, BMessenger msgn)
:
fID(id),
fName(id),
fMessenger(msgn),
fChatView(NULL),
fLooper(NULL),
fDateFormatter()
{
fConversationItem = new ConversationItem(fName.String(), this);
}
BString
Conversation::GetId() const
{
return fID;
}
void
Conversation::ImMessage(BMessage* msg)
{
int32 im_what = msg->FindInt32("im_what");
switch(im_what)
{
case IM_MESSAGE_RECEIVED:
{
_EnsureUser(msg);
_LogChatMessage(msg);
GetView()->MessageReceived(msg);
break;
}
case IM_MESSAGE_SENT:
{
_LogChatMessage(msg);
GetView()->MessageReceived(msg);
break;
}
case IM_SEND_MESSAGE:
{
fMessenger.SendMessage(msg);
break;
}
case IM_LOGS_RECEIVED:
default:
GetView()->MessageReceived(msg);
}
}
void
Conversation::ObserveString(int32 what, BString str)
{
if (fChatView != NULL)
fChatView->ObserveString(what, str);
}
void
Conversation::ObservePointer(int32 what, void* ptr)
{
if (fChatView != NULL)
fChatView->ObservePointer(what, ptr);
}
void
Conversation::ObserveInteger(int32 what, int32 val)
{
if (fChatView != NULL)
fChatView->ObserveInteger(what, val);
}
BMessenger
Conversation::Messenger() const
{
return fMessenger;
}
void
Conversation::SetMessenger(BMessenger messenger)
{
fMessenger = messenger;
}
ProtocolLooper*
Conversation::GetProtocolLooper() const
{
return fLooper;
}
void
Conversation::SetProtocolLooper(ProtocolLooper* looper)
{
fLooper = looper;
}
BString
Conversation::GetName() const
{
return fName;
}
UserMap
Conversation::Users()
{
return fUsers;
}
User*
Conversation::UserById(BString id)
{
bool found = false;
return fUsers.ValueFor(id, &found);
}
void
Conversation::AddUser(User* user)
{
BMessage msg;
msg.AddString("user_id", user->GetId());
msg.AddString("user_name", user->GetName());
_EnsureUser(&msg);
}
void
Conversation::RemoveUser(User* user)
{
fUsers.RemoveItemFor(user->GetId());
GetView()->UpdateUserList(fUsers);
}
void
Conversation::ShowView(bool typing, bool userAction)
{
((TheApp*)be_app)->GetMainWindow()->SetConversation(this);
}
ConversationView*
Conversation::GetView()
{
if (fChatView != NULL)
return fChatView;
fChatView = new ConversationView(this);
if (fLooper->Protocol()->SaveLogs() == false)
return fChatView;
BStringList logs = _GetChatLogs();
BMessage logMsg(IM_MESSAGE);
logMsg.AddInt32("im_what", IM_LOGS_RECEIVED);
logMsg.AddStrings("body", logs);
fChatView->MessageReceived(&logMsg);
return fChatView;
}
ConversationItem*
Conversation::GetListItem()
{
return fConversationItem;
}
void
Conversation::_LogChatMessage(BMessage* msg)
{
BString date;
fDateFormatter.Format(date, time(0), B_SHORT_DATE_FORMAT, B_MEDIUM_TIME_FORMAT);
BString id = msg->FindString("user_id");
BString uname;
if (id.IsEmpty() == false)
uname = UserById(id)->GetName();
else
uname = "You";
BString logLine("[");
logLine << date;
logLine << "] ";
logLine << uname;
logLine << ": ";
logLine << msg->FindString("body");
logLine << "\n";
// TODO: Don't hardcode 21, expose maximum as a setting
BStringList logs = _GetChatLogs();
logs.Remove(21);
logs.Add(logLine, 0);
BMessage newLogMsg;
newLogMsg.AddStrings("log", logs);
BFile logFile(fLogPath.Path(), B_READ_WRITE | B_CREATE_FILE);
newLogMsg.Flatten(&logFile);
}
BStringList
Conversation::_GetChatLogs()
{
_EnsureLogPath();
BFile logFile(fLogPath.Path(), B_READ_WRITE | B_CREATE_FILE);
BMessage logMsg;
BStringList logs;
if (logMsg.Unflatten(&logFile) == B_OK) {
logMsg.FindStrings("log", &logs);
}
return logs;
}
void
Conversation::_EnsureLogPath()
{
if (fLogPath.InitCheck() == B_OK)
return;
const char* sig = fLooper->Protocol()->Signature();
CayaProtocolAddOn* protoAdd = ProtocolManager::Get()->ProtocolAddOn(sig);
fLogPath.SetTo(CayaLogPath(protoAdd->Signature(), protoAdd->ProtoSignature()));
fLogPath.Append(fID);
}
User*
Conversation::_EnsureUser(BMessage* msg)
{
BString id = msg->FindString("user_id");
if (id.IsEmpty() == true) return NULL;
User* user = UserById(id);
User* serverUser = _GetServer()->UserById(id);
if (user == NULL && serverUser != NULL) {
fUsers.AddItem(id, serverUser);
user = serverUser;
GetView()->UpdateUserList(fUsers);
}
else if (user == NULL) {
user = new User(id, _GetServer()->Looper());
user->SetProtocolLooper(fLooper);
_GetServer()->AddUser(user);
fUsers.AddItem(id, user);
GetView()->UpdateUserList(fUsers);
}
user->RegisterObserver(this);
return user;
}
Server*
Conversation::_GetServer()
{
return ((TheApp*)be_app)->GetMainWindow()->GetServer();
}