implemented paging in the frontend. todo animations
This commit is contained in:
parent
57ca9d7380
commit
718e47c3ab
@ -26,7 +26,7 @@ public interface ChatMessageRepository extends JpaRepository<ChatMessage, Long>
|
|||||||
public List<ChatMessage> getNewMessages(String fromUser, String toUser, Date lastMessageTime);
|
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 "
|
@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);
|
public List<ChatMessage> getAllMessages(String fromUser, String toUser, PageRequest pageRequest);
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import org.ros.chatto.repository.ChatMessageRepository;
|
|||||||
import org.ros.chatto.repository.MessageCipherRepository;
|
import org.ros.chatto.repository.MessageCipherRepository;
|
||||||
import org.ros.chatto.repository.UserRepository;
|
import org.ros.chatto.repository.UserRepository;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@ -19,10 +19,12 @@ export class ChatModel implements Subject {
|
|||||||
*/
|
*/
|
||||||
private readonly _observers: Observer[] = [];
|
private readonly _observers: Observer[] = [];
|
||||||
private state: ChatMessageViewModel[] | null;
|
private state: ChatMessageViewModel[] | null;
|
||||||
|
private readonly _messagePageMap: Map<string, number>;
|
||||||
private readonly _messagesMap: Map<string, ChatMessageViewModel[]>;
|
private readonly _messagesMap: Map<string, ChatMessageViewModel[]>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.state = null;
|
this.state = null;
|
||||||
|
this._messagePageMap = new Map();
|
||||||
this._messagesMap = new Map();
|
this._messagesMap = new Map();
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -39,10 +41,14 @@ export class ChatModel implements Subject {
|
|||||||
console.log('Subject: Detached an observer.');
|
console.log('Subject: Detached an observer.');
|
||||||
}
|
}
|
||||||
|
|
||||||
private setUserMessages(username: string, messages: ChatMessageViewModel[]) {
|
private storeUserMessages(username: string, messages: ChatMessageViewModel[]) {
|
||||||
this._messagesMap.set(username, messages);
|
this._messagesMap.set(username, messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getStoredUserMessages(username: string): ChatMessageViewModel[] {
|
||||||
|
return this._messagesMap.get(username)!;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger an update in each subscriber.
|
* Trigger an update in each subscriber.
|
||||||
*/
|
*/
|
||||||
@ -60,13 +66,35 @@ export class ChatModel implements Subject {
|
|||||||
this.notify("some user");
|
this.notify("some user");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getmessages(contactName: string, passphrase: string, lastMessageTime: string | null): Promise<ChatMessageViewModel[]> {
|
public async getMessages(contactName: string, passphrase: string, lastMessageTime: string | null): Promise<ChatMessageViewModel[]> {
|
||||||
const cVMs = await ChatModelHelper.getMessages(contactName, passphrase, lastMessageTime, this);
|
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) {
|
if (cVMs != null) {
|
||||||
log.info('Subject: My state has just changed')
|
log.info('Subject: My state has just changed')
|
||||||
log.debug(cVMs);
|
|
||||||
// this._messagesMap.set(userName, 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;
|
JsonAPI.contactName = contactName;
|
||||||
this.notify(contactName);
|
this.notify(contactName);
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,11 @@ import { Sprintf } from "../singleton/Sprintf";
|
|||||||
export class ChatModelHelper {
|
export class ChatModelHelper {
|
||||||
private static readonly _encryptionService: EncryptionService = new SJCLEncryptionService();
|
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) {
|
switch (lastMessageTime) {
|
||||||
case null: {
|
case null: {
|
||||||
const data: ChatMessageDTO[] = await this.getAllMessagesAjax(userName);
|
const data: ChatMessageDTO[] = await this.getPaginatedMessagesAjax(userName, page!);
|
||||||
return data.map(vm => this.toChatMessageVM(vm, passphrase));
|
return data.map(vm => this.toChatMessageVM(vm, passphrase)).reverse();
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
const data: ChatMessageDTO[] = await this.getNewMessagesAjax(userName, lastMessageTime);
|
const data: ChatMessageDTO[] = await this.getNewMessagesAjax(userName, lastMessageTime);
|
||||||
@ -41,6 +41,32 @@ export class ChatModelHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static async getAllMessagesAjax(toUser: string): Promise<any> {
|
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();
|
const headers = new Headers();
|
||||||
if (JsonAPI.authToken == null) {
|
if (JsonAPI.authToken == null) {
|
||||||
log.error("authToken null");
|
log.error("authToken null");
|
||||||
@ -48,7 +74,7 @@ export class ChatModelHelper {
|
|||||||
};
|
};
|
||||||
headers.append('X-AUTH-TOKEN', JsonAPI.authToken);
|
headers.append('X-AUTH-TOKEN', JsonAPI.authToken);
|
||||||
// const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser);
|
// 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)
|
log.debug(url)
|
||||||
// const response = await fetch(`${JsonAPI.CHAT_MESSAGES_GET}/${toUser}`, {
|
// const response = await fetch(`${JsonAPI.CHAT_MESSAGES_GET}/${toUser}`, {
|
||||||
// method: 'GET',
|
// method: 'GET',
|
||||||
|
@ -7,29 +7,28 @@ export namespace JsonAPI {
|
|||||||
/**
|
/**
|
||||||
* Json API URL for retrieving all messages between two users
|
* 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
|
* ### 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.
|
|
||||||
* /api/chat/get/messages/{toUser}
|
|
||||||
*
|
*
|
||||||
|
* @param contactName
|
||||||
|
* The user whose messages wish to retrieve.
|
||||||
*/
|
*/
|
||||||
export const CHAT_MESSAGES_GET = `/api/chat/get/messages/%s`;
|
export const CHAT_MESSAGES_GET = `/api/chat/get/messages/%s`;
|
||||||
export const MESSAGE_POST = '/api/chat/post/message';
|
export const MESSAGE_POST = '/api/chat/post/message';
|
||||||
/**
|
/**
|
||||||
* Json API URL for retrieving paginated messages between two users
|
* Json API URL for retrieving paginated messages between two users
|
||||||
*
|
* Page index starts with 0
|
||||||
* @format /api/chat/get/messages/some-user?page=1&size=5 will give the first page with size 5
|
* @example /api/chat/get/messages/some-user?page=0&size=5 will give the first page where each page has size 5
|
||||||
*
|
*
|
||||||
* ### With sprintf
|
* ### 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 contactName the user whose messages wish to retrieve
|
||||||
* @param page Denotes the page required
|
* @param page denotes the page required
|
||||||
* @param denotes the size of each page
|
* @param size denotes the size of each page
|
||||||
*/
|
*/
|
||||||
export const CHAT_MESSAGE_PAGE_GET = `/api/chat/get/messages/%s?page=%d&size=%d`;
|
export const CHAT_MESSAGE_PAGE_GET = `/api/chat/get/messages/%s?page=%d&size=%d`;
|
||||||
|
|
||||||
|
@ -42,14 +42,18 @@ export class ChatView implements Observer {
|
|||||||
$('.action_menu').toggle();
|
$('.action_menu').toggle();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.chatMessagePageLoadAjax();
|
||||||
}
|
}
|
||||||
|
|
||||||
update(data: ChatMessageViewModel[]): void {
|
update(data: ChatMessageViewModel[]): void {
|
||||||
log.info('ChatView: updating view');
|
log.info('ChatView: updating view');
|
||||||
// let html: string = "";
|
// let html: string = "";
|
||||||
this._messageContainer.innerHTML = "";
|
this._messageContainer.innerHTML = "";
|
||||||
|
const rev: ChatMessageViewModel[] = Object.create(data)
|
||||||
|
rev.reverse();
|
||||||
data.forEach((vm: ChatMessageViewModel) => {
|
data.forEach((vm: ChatMessageViewModel) => {
|
||||||
const vmTemp = vm;
|
const vmTemp: ChatMessageViewModel = {...vm};
|
||||||
vmTemp.message = this._markdownService.render(vm.message);
|
vmTemp.message = this._markdownService.render(vm.message);
|
||||||
/** Very Important!!!
|
/** Very Important!!!
|
||||||
* Sanitizing HTML before displaying on webpage to prevent XSS attacks!!
|
* 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));
|
rendered = DOMPurify.sanitize(this._messageReceiveTemplate(vmTemp));
|
||||||
}
|
}
|
||||||
$(this._messageContainer).append(rendered);
|
$(this._messageContainer).append(rendered);
|
||||||
log.debug()
|
// log.debug(vm)
|
||||||
|
// log.debug(vmTemp)
|
||||||
// html += this._messageSendTemplate(vm);
|
// html += this._messageSendTemplate(vm);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -94,8 +99,7 @@ export class ChatView implements Observer {
|
|||||||
|
|
||||||
let contactName = JsonAPI.contactName;
|
let contactName = JsonAPI.contactName;
|
||||||
|
|
||||||
if(contactName == null)
|
if (contactName == null) {
|
||||||
{
|
|
||||||
log.error("Contact name is null");
|
log.error("Contact name is null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -162,4 +166,29 @@ export class ChatView implements Observer {
|
|||||||
.then(response => fetchHandler(response));
|
.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);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
@ -106,9 +106,7 @@ export class UserView implements Observer {
|
|||||||
let userName = elem.innerText;
|
let userName = elem.innerText;
|
||||||
// @ts-ignore: Object is possibly 'null'.
|
// @ts-ignore: Object is possibly 'null'.
|
||||||
document.getElementById('user-name-span').innerText = userName;
|
document.getElementById('user-name-span').innerText = userName;
|
||||||
this._chatModel.getmessages(userName, passphrase, null);
|
this._chatModel.getMessages(userName, passphrase, null);
|
||||||
// populateMessages(userName, passphrase);
|
|
||||||
sessionStorage.setItem('selectedUser', userName);
|
|
||||||
el.className += " active";
|
el.className += " active";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user