2021-05-28 08:38:33 -05:00
|
|
|
/*
|
|
|
|
* Copyright 2009-2011, Andrea Anzani. All rights reserved.
|
2021-06-18 01:39:34 -05:00
|
|
|
* Copyright 2021, Jaidyn Levesque. All rights reserved.
|
2021-05-28 08:38:33 -05:00
|
|
|
* Distributed under the terms of the MIT License.
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Andrea Anzani, andrea.anzani@gmail.com
|
2021-06-18 01:39:34 -05:00
|
|
|
* Jaidyn Levesque, jadedctrl@teknik.io
|
2021-05-28 08:38:33 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "ConversationView.h"
|
|
|
|
|
|
|
|
#include <LayoutBuilder.h>
|
2021-05-31 10:50:43 -05:00
|
|
|
#include <ListView.h>
|
2021-05-28 22:26:32 -05:00
|
|
|
#include <Notification.h>
|
2021-05-28 08:38:33 -05:00
|
|
|
#include <ScrollView.h>
|
|
|
|
#include <StringList.h>
|
2021-05-31 10:50:43 -05:00
|
|
|
#include <StringView.h>
|
2021-05-28 08:38:33 -05:00
|
|
|
|
|
|
|
#include <libinterface/BitmapView.h>
|
|
|
|
|
2021-06-20 12:44:20 -05:00
|
|
|
#include "AppMessages.h"
|
|
|
|
#include "AppPreferences.h"
|
2021-06-22 01:06:00 -05:00
|
|
|
#include "Cardie.h"
|
2021-06-20 12:44:20 -05:00
|
|
|
#include "ChatProtocolMessages.h"
|
|
|
|
#include "RenderView.h"
|
2021-05-28 08:38:33 -05:00
|
|
|
#include "Conversation.h"
|
|
|
|
#include "NotifyMessage.h"
|
2021-05-31 11:56:45 -05:00
|
|
|
#include "User.h"
|
2021-05-31 10:50:43 -05:00
|
|
|
#include "UserItem.h"
|
|
|
|
#include "UserListView.h"
|
2021-06-20 12:44:20 -05:00
|
|
|
#include "Utils.h"
|
2021-05-28 08:38:33 -05:00
|
|
|
|
|
|
|
|
2021-05-28 22:26:32 -05:00
|
|
|
ConversationView::ConversationView()
|
2021-05-28 08:38:33 -05:00
|
|
|
:
|
|
|
|
BGroupView("chatView", B_VERTICAL, B_USE_DEFAULT_SPACING),
|
2021-05-28 22:26:32 -05:00
|
|
|
fMessageQueue()
|
2021-05-28 08:38:33 -05:00
|
|
|
{
|
|
|
|
fMessageCount = 0;
|
2021-05-31 10:50:43 -05:00
|
|
|
_InitInterface();
|
2021-05-28 08:38:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-28 22:26:32 -05:00
|
|
|
ConversationView::ConversationView(Conversation* chat)
|
|
|
|
: ConversationView()
|
|
|
|
{
|
|
|
|
SetConversation(chat);
|
2021-06-06 01:49:11 -05:00
|
|
|
fUserList->SetConversation(chat);
|
2021-05-28 22:26:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-28 08:38:33 -05:00
|
|
|
bool
|
|
|
|
ConversationView::QuitRequested()
|
|
|
|
{
|
2021-06-20 12:44:20 -05:00
|
|
|
BMessage msg(APP_CLOSE_CHAT_WINDOW);
|
2021-05-28 08:38:33 -05:00
|
|
|
msg.AddString("chat_id", fConversation->GetId());
|
|
|
|
fConversation->Messenger().SendMessage(&msg);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-28 22:26:32 -05:00
|
|
|
void
|
2021-05-31 10:50:43 -05:00
|
|
|
ConversationView::AttachedToWindow()
|
2021-05-28 08:38:33 -05:00
|
|
|
{
|
2021-05-31 10:50:43 -05:00
|
|
|
while (fMessageQueue.IsEmpty() == false) {
|
|
|
|
BMessage* msg = fMessageQueue.RemoveItemAt(0);
|
|
|
|
ImMessage(msg);
|
|
|
|
}
|
2021-06-21 02:32:49 -05:00
|
|
|
if (fConversation != NULL) {
|
|
|
|
if (fNameTextView->Text() != fConversation->GetName())
|
|
|
|
fNameTextView->SetText(fConversation->GetName());
|
|
|
|
if (fSubjectTextView->Text() != fConversation->GetSubject())
|
|
|
|
fSubjectTextView->SetText(fConversation->GetSubject());
|
|
|
|
}
|
2021-05-28 08:38:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ConversationView::MessageReceived(BMessage* message)
|
|
|
|
{
|
|
|
|
switch (message->what) {
|
2021-06-20 12:44:20 -05:00
|
|
|
case APP_CHAT:
|
2021-05-28 08:38:33 -05:00
|
|
|
{
|
|
|
|
BString text = message->FindString("body");
|
|
|
|
if (text == "")
|
|
|
|
return;
|
Explicitly tie Conversations, Contacts, and Users to their ProtocolLoopers
Previously, all Conversations/Contacts/Users were stored in the Server,
each in their respective KeyMaps, identified solely by their
identifiers. This leads to the glaring problem of overlap― if the user
has multiple accounts, some users/rooms might be used or present in multiple
accounts at the same time.
Now, each accounts' Contacts, Conversations, and Users are stored in
its ProtocolLooper, making this overlap impossible. An oversight of only
allowing one user identifier to be stored (fMySelf) in Server was also fixed
this way.
This is the bulk of the work required for multi-account support― now,
the user can join the same XMPP room on two seperate accounts, and it
works perfectly.
2021-06-10 15:16:43 -05:00
|
|
|
int64 instance = fConversation->GetProtocolLooper()->GetInstance();
|
2021-05-28 08:38:33 -05:00
|
|
|
|
|
|
|
BMessage msg(IM_MESSAGE);
|
|
|
|
msg.AddInt32("im_what", IM_SEND_MESSAGE);
|
Explicitly tie Conversations, Contacts, and Users to their ProtocolLoopers
Previously, all Conversations/Contacts/Users were stored in the Server,
each in their respective KeyMaps, identified solely by their
identifiers. This leads to the glaring problem of overlap― if the user
has multiple accounts, some users/rooms might be used or present in multiple
accounts at the same time.
Now, each accounts' Contacts, Conversations, and Users are stored in
its ProtocolLooper, making this overlap impossible. An oversight of only
allowing one user identifier to be stored (fMySelf) in Server was also fixed
this way.
This is the bulk of the work required for multi-account support― now,
the user can join the same XMPP room on two seperate accounts, and it
works perfectly.
2021-06-10 15:16:43 -05:00
|
|
|
msg.AddInt64("instance", instance);
|
2021-05-28 08:38:33 -05:00
|
|
|
msg.AddString("chat_id", fConversation->GetId());
|
|
|
|
msg.AddString("body", text);
|
|
|
|
fConversation->ImMessage(&msg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IM_MESSAGE:
|
|
|
|
ImMessage(message);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
BGroupView::MessageReceived(message);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ConversationView::ImMessage(BMessage* msg)
|
|
|
|
{
|
|
|
|
int32 im_what = msg->FindInt32("im_what");
|
|
|
|
|
|
|
|
switch (im_what) {
|
2021-06-04 16:32:18 -05:00
|
|
|
case IM_ROOM_LEFT:
|
|
|
|
{
|
|
|
|
delete fConversation;
|
|
|
|
delete this;
|
|
|
|
break;
|
|
|
|
}
|
2021-05-28 08:38:33 -05:00
|
|
|
case IM_MESSAGE_RECEIVED:
|
|
|
|
{
|
|
|
|
BString id = msg->FindString("user_id");
|
|
|
|
User* sender = fConversation->UserById(id);
|
|
|
|
|
2021-05-28 22:26:32 -05:00
|
|
|
// Send a notification, if it's appropriate
|
|
|
|
if ((Window() == NULL || Window()->IsActive() == false)
|
2021-06-20 12:44:20 -05:00
|
|
|
&& (!AppPreferences::Item()->NotifyNewMessage)
|
2021-06-15 00:19:52 -05:00
|
|
|
&& sender != NULL)
|
2021-05-28 22:26:32 -05:00
|
|
|
{
|
|
|
|
fMessageCount++;
|
|
|
|
BString notify_message;
|
|
|
|
notify_message << "You've got ";
|
|
|
|
notify_message << fMessageCount;
|
|
|
|
if (fMessageCount==1)
|
|
|
|
notify_message << " new message from ";
|
|
|
|
else
|
|
|
|
notify_message << " new messages from ";
|
2021-06-15 00:19:52 -05:00
|
|
|
notify_message << sender->GetName();
|
2021-05-28 22:26:32 -05:00
|
|
|
|
|
|
|
BNotification notification(B_INFORMATION_NOTIFICATION);
|
2021-06-22 01:06:00 -05:00
|
|
|
notification.SetGroup(BString(APP_NAME));
|
2021-05-28 22:26:32 -05:00
|
|
|
notification.SetTitle(BString("New message"));
|
|
|
|
notification.SetIcon(sender->AvatarBitmap());
|
|
|
|
notification.SetContent(notify_message);
|
2021-06-15 00:19:52 -05:00
|
|
|
notification.SetMessageID(sender->GetName());
|
2021-05-28 22:26:32 -05:00
|
|
|
notification.Send();
|
|
|
|
// Check if the user want the notification
|
2021-06-20 12:44:20 -05:00
|
|
|
if (!AppPreferences::Item()->NotifyNewMessage)
|
2021-05-28 22:26:32 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-05-30 19:07:50 -05:00
|
|
|
_AppendOrEnqueueMessage(msg);
|
2021-05-28 08:38:33 -05:00
|
|
|
break;
|
|
|
|
}
|
2021-05-30 19:07:50 -05:00
|
|
|
case IM_MESSAGE_SENT:
|
2021-05-28 08:38:33 -05:00
|
|
|
case IM_LOGS_RECEIVED:
|
|
|
|
{
|
2021-05-30 19:07:50 -05:00
|
|
|
_AppendOrEnqueueMessage(msg);
|
2021-05-28 08:38:33 -05:00
|
|
|
break;
|
|
|
|
}
|
2021-06-18 01:39:34 -05:00
|
|
|
case IM_ROOM_JOINED:
|
|
|
|
{
|
|
|
|
BMessage msg;
|
|
|
|
msg.AddString("body", "** You joined the room.\n");
|
|
|
|
_AppendOrEnqueueMessage(&msg);
|
|
|
|
}
|
|
|
|
case IM_ROOM_CREATED:
|
|
|
|
{
|
|
|
|
BMessage msg;
|
|
|
|
msg.AddString("body", "** You created the room.\n");
|
|
|
|
_AppendOrEnqueueMessage(&msg);
|
|
|
|
}
|
2021-06-07 00:03:15 -05:00
|
|
|
case IM_ROOM_PARTICIPANT_JOINED:
|
|
|
|
{
|
|
|
|
_UserMessage("%user% has joined the room.\n",
|
|
|
|
"%user% has joined the room (%body%).\n", msg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IM_ROOM_PARTICIPANT_LEFT:
|
|
|
|
{
|
|
|
|
_UserMessage("%user% has left the room.\n",
|
|
|
|
"%user% has left the room (%body%).\n", msg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IM_ROOM_PARTICIPANT_KICKED:
|
|
|
|
{
|
|
|
|
_UserMessage("%user% was kicked.\n",
|
|
|
|
"%user% was kicked (%body%).\n", msg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IM_ROOM_PARTICIPANT_BANNED:
|
|
|
|
{
|
|
|
|
_UserMessage("%user% has been banned.\n",
|
|
|
|
"%user% has been banned (%body%).\n", msg);
|
|
|
|
break;
|
|
|
|
}
|
2021-05-28 08:38:33 -05:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-31 10:50:43 -05:00
|
|
|
Conversation*
|
|
|
|
ConversationView::GetConversation()
|
|
|
|
{
|
|
|
|
return fConversation;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ConversationView::SetConversation(Conversation* chat)
|
|
|
|
{
|
|
|
|
fConversation = chat;
|
2021-06-04 13:57:04 -05:00
|
|
|
fNameTextView->SetText(chat->GetName());
|
2021-06-21 02:32:49 -05:00
|
|
|
fSubjectTextView->SetText(chat->GetSubject());
|
2021-06-04 13:57:04 -05:00
|
|
|
fProtocolView->SetBitmap(chat->ProtocolBitmap());
|
2021-05-31 10:50:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2021-06-04 13:57:04 -05:00
|
|
|
ConversationView::UpdateIcon()
|
2021-05-31 10:50:43 -05:00
|
|
|
{
|
2021-06-04 13:57:04 -05:00
|
|
|
if (fConversation->IconBitmap() != NULL)
|
|
|
|
fIcon->SetBitmap(fConversation->IconBitmap());
|
2021-05-31 10:50:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ConversationView::UpdateUserList(UserMap users)
|
|
|
|
{
|
|
|
|
fUserList->MakeEmpty();
|
|
|
|
for (int i = 0; i < users.CountItems(); i++) {
|
|
|
|
User* user = users.ValueAt(i);
|
|
|
|
if (fUserList->HasItem(user->GetListItem()) == false)
|
|
|
|
fUserList->AddItem(user->GetListItem());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-28 08:38:33 -05:00
|
|
|
void
|
2021-06-03 23:39:50 -05:00
|
|
|
ConversationView::InvalidateUserList()
|
2021-05-28 08:38:33 -05:00
|
|
|
{
|
2021-06-03 23:39:50 -05:00
|
|
|
for (int i = 0; i < fUserList->CountItems(); i++)
|
|
|
|
fUserList->InvalidateItem(i);
|
2021-05-28 08:38:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2021-06-03 23:39:50 -05:00
|
|
|
ConversationView::ObserveString(int32 what, BString str)
|
2021-05-28 08:38:33 -05:00
|
|
|
{
|
2021-06-04 13:57:04 -05:00
|
|
|
switch (what)
|
|
|
|
{
|
2021-06-13 01:25:28 -05:00
|
|
|
case STR_ROOM_NAME:
|
|
|
|
{
|
|
|
|
fNameTextView->SetText(str);
|
|
|
|
break;
|
|
|
|
}
|
2021-06-04 13:57:04 -05:00
|
|
|
case STR_ROOM_SUBJECT:
|
|
|
|
{
|
|
|
|
fSubjectTextView->SetText(str);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-05-28 08:38:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-31 10:50:43 -05:00
|
|
|
void
|
|
|
|
ConversationView::_InitInterface()
|
|
|
|
{
|
2021-06-20 12:44:20 -05:00
|
|
|
fReceiveView = new RenderView("fReceiveView");
|
2021-05-31 10:50:43 -05:00
|
|
|
BScrollView* scrollViewReceive = new BScrollView("receiveScrollView",
|
|
|
|
fReceiveView, B_WILL_DRAW, false, true);
|
|
|
|
|
2021-06-04 13:57:04 -05:00
|
|
|
fNameTextView = new BTextView("roomName", B_WILL_DRAW);
|
|
|
|
fNameTextView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
|
|
|
|
fNameTextView->MakeEditable(false);
|
2021-05-31 10:50:43 -05:00
|
|
|
|
2021-06-04 13:57:04 -05:00
|
|
|
fSubjectTextView = new BTextView("roomSubject", B_WILL_DRAW);
|
|
|
|
fSubjectTextView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
|
|
|
|
fSubjectTextView->MakeEditable(false);
|
2021-05-31 10:50:43 -05:00
|
|
|
|
2021-06-04 13:57:04 -05:00
|
|
|
fIcon = new BitmapView("ContactIcon");
|
|
|
|
fIcon->SetExplicitMinSize(BSize(50, 50));
|
|
|
|
fIcon->SetExplicitPreferredSize(BSize(50, 50));
|
|
|
|
fIcon->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, B_ALIGN_MIDDLE));
|
2021-05-31 10:50:43 -05:00
|
|
|
|
|
|
|
fProtocolView = new BitmapView("protocolView");
|
|
|
|
|
|
|
|
fUserList = new UserListView("userList");
|
|
|
|
BScrollView* scrollViewUsers = new BScrollView("userScrollView",
|
|
|
|
fUserList, B_WILL_DRAW, false, true);
|
|
|
|
|
|
|
|
BLayoutBuilder::Group<>(this, B_VERTICAL)
|
|
|
|
.AddGroup(B_HORIZONTAL)
|
2021-06-04 13:57:04 -05:00
|
|
|
.Add(fIcon)
|
|
|
|
.AddGroup(B_VERTICAL)
|
|
|
|
.Add(fNameTextView)
|
|
|
|
.Add(fSubjectTextView)
|
|
|
|
.End()
|
2021-05-31 10:50:43 -05:00
|
|
|
.Add(fProtocolView)
|
|
|
|
.End()
|
|
|
|
.AddSplit(B_HORIZONTAL, 0)
|
|
|
|
.Add(scrollViewReceive, 5)
|
|
|
|
.Add(scrollViewUsers, 1)
|
|
|
|
.End();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-30 19:07:50 -05:00
|
|
|
bool
|
|
|
|
ConversationView::_AppendOrEnqueueMessage(BMessage* msg)
|
|
|
|
{
|
|
|
|
// If not attached to the chat window, then re-handle this message
|
|
|
|
// later [AttachedToWindow()], since you can't edit an unattached
|
|
|
|
// RenderView.
|
|
|
|
if (Window() == NULL) {
|
|
|
|
fMessageQueue.AddItem(new BMessage(*msg));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Alright, we're good to append!
|
|
|
|
_AppendMessage(msg);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ConversationView::_AppendMessage(BMessage* msg)
|
|
|
|
{
|
2021-06-06 12:02:26 -05:00
|
|
|
BStringList users, bodies;
|
2021-06-07 00:03:15 -05:00
|
|
|
if (msg->FindStrings("body", &bodies) != B_OK)
|
2021-06-06 12:02:26 -05:00
|
|
|
return;
|
2021-06-07 00:03:15 -05:00
|
|
|
msg->FindStrings("user_id", &users);
|
2021-06-06 12:02:26 -05:00
|
|
|
|
|
|
|
for (int i = bodies.CountStrings(); i >= 0; i--) {
|
|
|
|
User* sender = fConversation->UserById(users.StringAt(i));
|
2021-06-07 20:01:14 -05:00
|
|
|
BString sender_name = users.StringAt(i);
|
2021-06-06 12:02:26 -05:00
|
|
|
BString body = bodies.StringAt(i);
|
2021-06-07 20:01:14 -05:00
|
|
|
rgb_color userColor = ui_color(B_PANEL_TEXT_COLOR);
|
2021-06-14 16:41:25 -05:00
|
|
|
int64 timeInt;
|
|
|
|
|
|
|
|
if (msg->FindInt64("when", i, &timeInt) != B_OK)
|
|
|
|
timeInt = (int64)time(NULL);
|
2021-06-06 12:02:26 -05:00
|
|
|
|
2021-06-07 20:01:14 -05:00
|
|
|
if (sender != NULL) {
|
2021-06-06 12:02:26 -05:00
|
|
|
sender_name = sender->GetName();
|
2021-06-07 20:01:14 -05:00
|
|
|
userColor = sender->fItemColor;
|
|
|
|
}
|
2021-06-06 12:02:26 -05:00
|
|
|
|
|
|
|
if (sender_name.IsEmpty() == true) {
|
|
|
|
fReceiveView->AppendGenericMessage(body.String());
|
|
|
|
continue;
|
|
|
|
}
|
2021-05-30 19:07:50 -05:00
|
|
|
|
2021-06-14 16:41:25 -05:00
|
|
|
fReceiveView->AppendMessage(sender_name.String(), body.String(),
|
|
|
|
userColor, (time_t)timeInt);
|
2021-05-30 19:07:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-07 00:03:15 -05:00
|
|
|
void
|
|
|
|
ConversationView::_UserMessage(const char* format, const char* bodyFormat,
|
|
|
|
BMessage* msg)
|
|
|
|
{
|
|
|
|
BString user_id;
|
|
|
|
BString user_name = msg->FindString("user_name");
|
|
|
|
BString body = msg->FindString("body");
|
|
|
|
|
|
|
|
if (msg->FindString("user_id", &user_id) != B_OK)
|
|
|
|
return;
|
|
|
|
if (user_name.IsEmpty() == true)
|
|
|
|
user_name = user_id;
|
|
|
|
|
|
|
|
BString newBody("** ");
|
|
|
|
if (body.IsEmpty() == true)
|
|
|
|
newBody << format;
|
|
|
|
else {
|
|
|
|
newBody << bodyFormat;
|
|
|
|
newBody.ReplaceAll("%body%", body.String());
|
|
|
|
}
|
|
|
|
newBody.ReplaceAll("%user%", user_name.String());
|
|
|
|
|
|
|
|
BMessage newMsg;
|
|
|
|
newMsg.AddString("body", newBody);
|
|
|
|
_AppendOrEnqueueMessage(&newMsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|