- vừa được xem lúc

Ezyfox Server tập 2 - Thêm tính năng chat

0 0 1

Người đăng: Bảo Ngô

Theo Viblo Asia

bài viết trước, chúng ta đã bước đầu làm quen với Ezyfox Server thông qua một ứng dụng nhỏ sử dụng giao thức WS, nơi mà client sẽ hoàn tất việc HANDSHAKELOGIN, đồng thời nhận một lời chào từ backend server và hiển thị lên trang web. Hôm nay chúng ta tiếp tục nâng cấp ứng dụng để hỗ trợ thêm tính năng realtime chat giữa hai người.

Demo (1 phút):

Everything Is AWESOME

Code hoàn chỉnh vui lòng xem ở đâyđây. Còn bây giờ bắt đầu thôi!

Client sẽ gọi active-users command và xử lý response từ Ezy Server; đồng thời gửi chat message đến server thông qua chat command khi user bấm nút Send:

Để có thể chat cho một ai đó thì trước tiên ứng dụng (cụ thể là phía frontend) phải hiển thị được các cá nhân đang online trong hệ thống, từ đó user có thể chọn một ai đó để bắt đầu chat. Để làm được điều này, client sẽ gọi command active-users đến server và server sẽ trả về một danh sách các user đang có mặt trong hệ thống.

Mình có giải thích bằng các comment //, mọi người chú ý đọc để hiểu rõ

 //sửa đoạn này (cụ thể là sau khi nhận được lời chào thì gửi command active-users) setupApp.addDataHandler("greet", function (app, data) { var message = data.message; alert(message); $('.connect').remove(); //Đoạn này là gửi command đây app.sendRequest('active-users', {}); setInterval(function () { app.sendRequest('active-users', {}); }, 1000); }); //thêm đoạn này (xử lý response của command active-users) setupApp.addDataHandler("active-users", function (app, data) { getAllActiveUser(data, app); setupClickOnAPersonAndSendBtn(app); }); //hàm này xử lý danh sách user đang online được trả về từ backend, cụ thể là render lên UI function getAllActiveUser(data, app) { var activeUsers = data['active-users'].filter(e => e.id !== app.client.me.id); var bigS = $('#result-panel'); bigS.removeAttr("hidden"); var onlineList = bigS.find('.list-group'); onlineList.empty(); activeUsers.forEach(e => { onlineList.append('<li><a data-username="' + e.name + '" class="list-group-item" id="' + e.id + '" href="javascript:void(0);">' + e.name + '<span hidden class="badge">!</span></a></li>') }); } //Khi user bấm vào một ai đó để bắt đầu chat thì hiển thị khung chat và setup nút gửi tin nhắn function setupClickOnAPersonAndSendBtn(app) { var bigChat = $('#chat-window-1'); $('.list-group-item').on("click", function (object) { bigChat.removeAttr('hidden'); var panelBody = bigChat.find('.panel-body'); panelBody.children().not(':first').remove(); var thiz = $(this); var id = thiz.attr('id'); var name = thiz.attr('data-username'); bigChat.find('#chat-title').text('Chat to ' + name) var btnChat = bigChat.find('#btn-chat'); btnChat.attr('data-user-id', id) bigChat.attr('data-user-id', id) //Đoạn này setup click event lên button Send btnChat.off('click'); btnChat.on('click', function () { var msg = bigChat.find('#btn-input').val(); if (msg && msg.length > 0) { //Dòng này gửi command chat lên server app.sendRequest('chat', {from: app.client.me.id, to: +id, content: msg}); //Thêm dingf chat lên UI nữa chứ var msgDiv = '<div class="row msg_container base_sent">' + ' <div class="col-md-10 col-xs-10">' + ' <div class="messages msg_sent">' + ' <p>' + msg + '</p>' + ' </div>' + ' </div>' + ' <div class="col-md-2 col-xs-2 avatar">' + ' <img src="http://www.bitrebels.com/wp-content/uploads/2011/02/Original-Facebook-Geek-Profile-Avatar-1.jpg"' + ' class=" img-responsive ">' + ' </div>' + '</div>'; panelBody.append(msgDiv); } }) }); }

Thêm logic xử lý active-users command từ client gửi lên:

@EzyPrototype
@EzyRequestListener("active-users")
public class ActiveUsersRequestHandler extends AbstractClientRequestHandler { protected void execute() throws EzyBadRequestException { responseFactory .newObjectResponse() .command("active-users") .param("active-users", appContext.getApp().getUserManager().getUserList().stream().map(UserResponse::new).collect(Collectors.toList())) .session(session) .execute(); }
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@EzyObjectBinding(read = false)
public class UserResponse { private Long id; private Integer zoneId; private String name; private Boolean active; public UserResponse(EzyUser ezyUser) { this(ezyUser.getId(), ezyUser.getZoneId(), ezyUser.getName(), Boolean.TRUE); } }

Server tiếp tục nhận command chat và gửi message về đúng người nhận:

Command chat mà client gửi lên có cấu trúc như sau (xem lại đoạn javascript app.sendRequest('chat', {from: app.client.me.id, to: +id, content: msg});):

@Data
@NoArgsConstructor
@AllArgsConstructor
@EzyObjectBinding
public class ChatRequest { private Long from; private Long to; private String content; }

Chúng ta cần một handler để gửi đoạn message này đến đúng người nhận (dựa vào trường to):

@EzyRequestController
@Setter
public class ChatRequestHandler extends EzyLoggable { @EzyAutoBind("appResponseFactory") protected EzyResponseFactory responseFactory; @EzyDoHandle("chat") public void secureChat(EzyUser user, EzyAppContext appContext, ChatRequest request) { responseFactory.newObjectResponse() .encrypted() .command("chat") .param("chat", new ChatResponse(request)) //Dòng này sẽ quyết định response sẽ được gửi đến user nào .user(appContext.getApp().getUserManager().getUser(request.getTo())) .execute(); }
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@EzyObjectBinding
public class ChatResponse { private Long from; private Long to; private String content; public ChatResponse(ChatRequest request) { this(request.getFrom(), request.getTo(), request.getContent()); }
}

Đến đây chúng ta thấy có 2 cách để viết một handler. Một cách là sử dụng @EzyRequestListener("command"), cách kia là sử dụng @EzyRequestController cùng với @EzyDoHandle("command"). Cách thứ 2, chúng ta có quyền thêm các params như EzyUser, EzyAppContext, ChatRequest vào method signature, trong khi cách thứ nhất thì không (chúng ta phải inject chúng vào nếu muốn sử dụng). Tùy trường hợp các bạn chọn cách tối ưu nhé.

Nhớ sửa đoạn này ở AppEntry:

 @Override protected String[] getScanablePackages() { return new String[] { "com.ezyfoxserver.app", "com.ezyfoxserver.event.handler", "com.ezyfoxserver.service", /*Ezyfox Server sẽ scan luôn package com.ezyfoxserver.dto*/ "com.ezyfoxserver.dto", }; }

Một lần nữa, các bạn chú ý đọc comment giải thích code nhé!

Client sẽ nhận message qua command chat và render lên UI:

setupApp.addDataHandler("chat", function (app, data) { var chat = data.chat; var message = chat.content; console.log(message) var from = chat.from; var bigChat = $('#chat-window-1'); var id = bigChat.attr('data-user-id'); if (id && +id === from) { console.log('equal') var msgDiv = '<div class="row msg_container base_receive">' + ' <div class="col-md-2 col-xs-2 avatar">' + ' <img src="http://www.bitrebels.com/wp-content/uploads/2011/02/Original-Facebook-Geek-Profile-Avatar-1.jpg"' + ' class=" img-responsive ">' + ' </div>' + ' <div class="col-md-10 col-xs-10">' + ' <div class="messages msg_receive">' + ' <p>' + message + '</p>' + ' </div>' + ' </div>' + '</div>'; //Dòng này render message content lên UI này bigChat.find('.panel-body').append(msgDiv); } else { //ĐOẠN NÀY CHƯA CẦN NÊN TẠM COMMENT LẠI // var bigS = $('#result-panel'); // var onlineList = bigS.find('.list-group-item'); // console.log(onlineList.find('#' + from)); } });

Tổng kết:

Đây là 1 ứng dụng chat realtime đơn giản với mục đích học tập và khám phá framework, do đó tính năng chỉ giới hạn ở mức chạy được. Các tính năng chưa support như load tin nhắn cũ, thông báo tin nhắn mới đến bằng icon sẽ được hoàn thiện sau. Code hoàn chỉnh vui lòng xem ở đâyđây.

Hẹn gặp lại các bạn trong loại bài tiếp theo!

Bình luận

Bài viết tương tự

- vừa được xem lúc

In app purchase trong Android (Phần 2)

Bài viết trước mình đã giới thiệu sơ lược về Google Billing Library và các setup môi trường. Trong bài viết này, chúng ta sẽ xem xét kỹ hơn vòng đời khi mua one-time product, cụ thể là quy trình bán và cấp cho người dùng mặt hàng kỹ thuật số mà họ đã mua trong ứng dụng của bạn.

0 0 60

- vừa được xem lúc

Những website tự học lập trình hiệu quả

Tự học lập trình để nâng cao kỹ năng luôn là nhu cầu thiết yếu của mỗi lập trình viên. Chẳng gì hơn khi tự mình tìm hiểu, trau dồi thêm kiến thức chuyên môn lập trình.

0 0 102

- vừa được xem lúc

Gluon Mobile: một framework tạo ứng dụng mobile đa nền tảng khác

Trong thế giới mobile thì React Native và Flutter quá là nổi tiếng trong việc hỗ trợ làm ứng dụng đa nền tảng vì thế là nó làm lu mờ đi phần nào các framework khác, Gluon có lẽ vì thế cũng cùng chung

0 0 20

- vừa được xem lúc

VARIABLES IN JAVA

This posts is introduce Types of variables in Java. . Local Variables. Instance Variables.

0 0 18

- vừa được xem lúc

15 JAVA CODING BEST PRACTICES CHO NGƯỜI MỚI

Ngay từ đầu, Java là một trong những ngôn ngữ lập trình thống trị. Trong thời đại tiến bộ ngày này, nơi mà nhiều ngôn ngữ mạnh mẽ có mặt đã chết từ lâu, Java vẫn phù hợp và phát triển nhanh chóng theo

0 0 56

- vừa được xem lúc

Custom Self-Hosted Maven Repository

Giới thiệu. Đối với một số ứng dụng sử dụng nhiều Micro Service bên trong, những Class, Function,.

0 0 33