Browse Source

implemented paging in the frontend. todo animations

master
Rohan Sircar 4 years ago
committed by nova
parent
commit
718e47c3ab
  1. 2
      chatto/src/main/java/org/ros/chatto/repository/ChatMessageRepository.java
  2. 1
      chatto/src/main/java/org/ros/chatto/service/ChatServiceImpl.java
  3. 38
      chatto/src/main/javascript/ts/src/model/ChatModel.ts
  4. 34
      chatto/src/main/javascript/ts/src/model/ChatModelHelper.ts
  5. 23
      chatto/src/main/javascript/ts/src/singleton/JsonAPI.ts
  6. 47
      chatto/src/main/javascript/ts/src/view/ChatView.ts
  7. 4
      chatto/src/main/javascript/ts/src/view/UserView.ts

2
chatto/src/main/java/org/ros/chatto/repository/ChatMessageRepository.java

@ -26,7 +26,7 @@ public interface ChatMessageRepository extends JpaRepository<ChatMessage, Long>
public List<ChatMessage> getNewMessages(String fromUser, String toUser, Date lastMessageTime);
@Query("select m from ChatMessage m join fetch m.messageCipher where (m.toUser.userName = ?1 or m.toUser.userName = ?2) and "
+ "(m.fromUser.userName = ?1 or m.fromUser.userName = ?2) order by m.messageTime asc")
+ "(m.fromUser.userName = ?1 or m.fromUser.userName = ?2) order by m.messageTime desc")
public List<ChatMessage> getAllMessages(String fromUser, String toUser, PageRequest pageRequest);

1
chatto/src/main/java/org/ros/chatto/service/ChatServiceImpl.java

@ -14,6 +14,7 @@ import org.ros.chatto.repository.ChatMessageRepository;
import org.ros.chatto.repository.MessageCipherRepository;
import org.ros.chatto.repository.UserRepository;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

38
chatto/src/main/javascript/ts/src/model/ChatModel.ts

@ -19,10 +19,12 @@ export class ChatModel implements Subject {
*/
private readonly _observers: Observer[] = [];
private state: ChatMessageViewModel[] | null;
private readonly _messagePageMap: Map<string, number>;
private readonly _messagesMap: Map<string, ChatMessageViewModel[]>;
constructor() {
this.state = null;
this._messagePageMap = new Map();
this._messagesMap = new Map();
}
/**
@ -39,10 +41,14 @@ export class ChatModel implements Subject {
console.log('Subject: Detached an observer.');
}
private setUserMessages(username: string, messages: ChatMessageViewModel[]) {
private storeUserMessages(username: string, messages: ChatMessageViewModel[]) {
this._messagesMap.set(username, messages);
}
private getStoredUserMessages(username: string): ChatMessageViewModel[] {
return this._messagesMap.get(username)!;
}
/**
* Trigger an update in each subscriber.
*/
@ -60,13 +66,35 @@ export class ChatModel implements Subject {
this.notify("some user");
}
public async getmessages(contactName: string, passphrase: string, lastMessageTime: string | null): Promise<ChatMessageViewModel[]> {
const cVMs = await ChatModelHelper.getMessages(contactName, passphrase, lastMessageTime, this);
public async getMessages(contactName: string, passphrase: string, lastMessageTime: string | null): Promise<ChatMessageViewModel[]> {
if(this._messagePageMap.get(contactName) == null)
this._messagePageMap.set(contactName, 0);
else {
log.debug('page number before = ' + this._messagePageMap.get(contactName)!)
this._messagePageMap.set(contactName, this._messagePageMap.get(contactName)! + 1);
log.debug('page number after = ' + this._messagePageMap.get(contactName)!)
}
const pageNumber = this._messagePageMap.get(contactName)
const cVMs = await ChatModelHelper.getMessages(contactName, passphrase, pageNumber!, lastMessageTime, this);
if (cVMs != null) {
log.info('Subject: My state has just changed')
log.debug(cVMs);
// this._messagesMap.set(userName, cVMs);
this.setUserMessages(contactName, cVMs);
const existingMessages = this.getStoredUserMessages(contactName);
log.debug(existingMessages);
log.debug(cVMs);
if (existingMessages != null) {
// existingMessages.forEach(function (elem) {
// cVMs.push(elem);
// })
const newArr = cVMs.concat(existingMessages)
// log.debug(newArr);
this.storeUserMessages(contactName, newArr);
// this.storeUserMessages(contactName, cVMs);
}
else {
this.storeUserMessages(contactName, cVMs);
}
JsonAPI.contactName = contactName;
this.notify(contactName);
}

34
chatto/src/main/javascript/ts/src/model/ChatModelHelper.ts

@ -15,11 +15,11 @@ import { Sprintf } from "../singleton/Sprintf";
export class ChatModelHelper {
private static readonly _encryptionService: EncryptionService = new SJCLEncryptionService();
public static async getMessages(userName: string, passphrase: string, lastMessageTime: string | null, chatModel: ChatModel): Promise<ChatMessageViewModel[]> {
public static async getMessages(userName: string, passphrase: string, page: number | null, lastMessageTime: string | null, chatModel: ChatModel): Promise<ChatMessageViewModel[]> {
switch (lastMessageTime) {
case null: {
const data: ChatMessageDTO[] = await this.getAllMessagesAjax(userName);
return data.map(vm => this.toChatMessageVM(vm, passphrase));
const data: ChatMessageDTO[] = await this.getPaginatedMessagesAjax(userName, page!);
return data.map(vm => this.toChatMessageVM(vm, passphrase)).reverse();
}
default: {
const data: ChatMessageDTO[] = await this.getNewMessagesAjax(userName, lastMessageTime);
@ -41,6 +41,32 @@ export class ChatModelHelper {
}
private static async getAllMessagesAjax(toUser: string): Promise<any> {
const headers = new Headers();
if (JsonAPI.authToken == null) {
log.error("authToken null");
return;
};
headers.append('X-AUTH-TOKEN', JsonAPI.authToken);
const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser);
// const url = Sprintf(JsonAPI.CHAT_MESSAGE_PAGE_GET, toUser,1,5);
log.debug(url)
// const response = await fetch(`${JsonAPI.CHAT_MESSAGES_GET}/${toUser}`, {
// method: 'GET',
// headers: headers
// });
const response = await fetch(url, {
method: 'GET',
headers: headers
});
console.log(response.clone());
if (fetchErrorHandler(response.clone())) {
return null;
}
const data: Promise<any> = await response.json();
return data;
}
private static async getPaginatedMessagesAjax(toUser: string, page: number): Promise<any> {
const headers = new Headers();
if (JsonAPI.authToken == null) {
log.error("authToken null");
@ -48,7 +74,7 @@ export class ChatModelHelper {
};
headers.append('X-AUTH-TOKEN', JsonAPI.authToken);
// const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser);
const url = Sprintf(JsonAPI.CHAT_MESSAGE_PAGE_GET, toUser,1,5);
const url = Sprintf(JsonAPI.CHAT_MESSAGE_PAGE_GET, toUser, page, 5);
log.debug(url)
// const response = await fetch(`${JsonAPI.CHAT_MESSAGES_GET}/${toUser}`, {
// method: 'GET',

23
chatto/src/main/javascript/ts/src/singleton/JsonAPI.ts

@ -7,29 +7,28 @@ export namespace JsonAPI {
/**
* Json API URL for retrieving all messages between two users
*
* @format /api/chat/get/messages/some-user
* @format /api/chat/get/messages/{contactName}
* @example /api/chat/get/messages/some-user
*
* ### With sprintf
* const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser);
*
* @param toUser
* The user whose messages wish to retrieve. The 'from user' part of the message is given by the current user logged in.
* /api/chat/get/messages/{toUser}
* const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, contactName);
*
* @param contactName
* The user whose messages wish to retrieve.
*/
export const CHAT_MESSAGES_GET = `/api/chat/get/messages/%s`;
export const MESSAGE_POST = '/api/chat/post/message';
/**
* Json API URL for retrieving paginated messages between two users
*
* @format /api/chat/get/messages/some-user?page=1&size=5 will give the first page with size 5
* Page index starts with 0
* @example /api/chat/get/messages/some-user?page=0&size=5 will give the first page where each page has size 5
*
* ### With sprintf
* const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser);
* const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, contactName);
*
* @param toUser The user whose messages wish to retrieve. The 'from user' part of the message is given by the current user logged in.
* @param page Denotes the page required
* @param denotes the size of each page
* @param contactName the user whose messages wish to retrieve
* @param page denotes the page required
* @param size denotes the size of each page
*/
export const CHAT_MESSAGE_PAGE_GET = `/api/chat/get/messages/%s?page=%d&size=%d`;

47
chatto/src/main/javascript/ts/src/view/ChatView.ts

@ -32,24 +32,28 @@ export class ChatView implements Observer {
this._messageContainer = deps.messageContainer;
this._chatModel = deps.chatModel;
this._messageSendTemplate = deps.messageSendTemplate;
this._messageReceiveTemplate = deps.messageReceiveTemplate;
this._markdownService = deps.markdownService;
this._encryptionService = deps.encryptionService;
this._messageReceiveTemplate = deps.messageReceiveTemplate;
this._markdownService = deps.markdownService;
this._encryptionService = deps.encryptionService;
this.addEventListeners();
$(document).ready(function() {
$('#action_menu_btn').click(function() {
$(document).ready(function () {
$('#action_menu_btn').click(function () {
$('.action_menu').toggle();
});
});
this.chatMessagePageLoadAjax();
}
update(data: ChatMessageViewModel[]): void {
log.info('ChatView: updating view');
// let html: string = "";
this._messageContainer.innerHTML = "";
const rev: ChatMessageViewModel[] = Object.create(data)
rev.reverse();
data.forEach((vm: ChatMessageViewModel) => {
const vmTemp = vm;
const vmTemp: ChatMessageViewModel = {...vm};
vmTemp.message = this._markdownService.render(vm.message);
/** Very Important!!!
* Sanitizing HTML before displaying on webpage to prevent XSS attacks!!
@ -63,7 +67,8 @@ export class ChatView implements Observer {
rendered = DOMPurify.sanitize(this._messageReceiveTemplate(vmTemp));
}
$(this._messageContainer).append(rendered);
log.debug()
// log.debug(vm)
// log.debug(vmTemp)
// html += this._messageSendTemplate(vm);
});
@ -94,8 +99,7 @@ export class ChatView implements Observer {
let contactName = JsonAPI.contactName;
if(contactName == null)
{
if (contactName == null) {
log.error("Contact name is null");
return;
}
@ -162,4 +166,29 @@ export class ChatView implements Observer {
.then(response => fetchHandler(response));
}
chatMessagePageLoadAjax() {
this._messageContainer.addEventListener('scroll', (e) => {
if ($(this._messageContainer).scrollTop() == 0) {
log.debug('Reached top')
// @ts-ignore
let passphrase: string;
let passphraseInput = document.getElementById('passphrase') as HTMLInputElement;
if (passphraseInput == null) {
log.error('passphraseInput element reference is null');
return;
}
passphrase = passphraseInput.value
if (passphrase == '' || passphrase == null) {
// alert('Please input passphrase')
// alertify.error('Please enter a passphrase');
log.error('passphrase is empty or null');
return;
}
if(JsonAPI.contactName != null)
this._chatModel.getMessages(JsonAPI.contactName , passphrase, null);
}
})
}
}

4
chatto/src/main/javascript/ts/src/view/UserView.ts

@ -106,9 +106,7 @@ export class UserView implements Observer {
let userName = elem.innerText;
// @ts-ignore: Object is possibly 'null'.
document.getElementById('user-name-span').innerText = userName;
this._chatModel.getmessages(userName, passphrase, null);
// populateMessages(userName, passphrase);
sessionStorage.setItem('selectedUser', userName);
this._chatModel.getMessages(userName, passphrase, null);
el.className += " active";
}

Loading…
Cancel
Save