用户收件箱组件优化:新消息实时提醒

目前 用户收件箱 组件的消息通知是通过定时器来触发的

image

现在改为事件订阅模式,只要发送了消息就实时更新未读消息数。

创建消息发布订阅事件

package de.diedavids.cuba.userinbox.web.message;

import com.haulmont.cuba.gui.events.UiEvent;
import org.springframework.context.ApplicationEvent;

/**
 * @author Rubin
 * @version v1 2021/4/8 11:08
 */
public class SendMessageEvent extends ApplicationEvent  implements UiEvent {
    /**
     * Create a new {@code ApplicationEvent}.
     *
     * @param source the object on which the event initially occurred or with
     *               which the event is associated (never {@code null})
     */
    public SendMessageEvent(Object source) {
        super(source);
    }
}

修改SendMessage类,添加事件发布。

package de.diedavids.cuba.userinbox.web.message;

import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.CommitContext;
import com.haulmont.cuba.core.global.Events;
import com.haulmont.cuba.gui.screen.*;
import de.diedavids.cuba.userinbox.entity.SendMessageEntity;
import de.diedavids.cuba.userinbox.service.MessageService;
import org.springframework.context.ApplicationEvent;

import javax.inject.Inject;
import java.util.Collections;
import java.util.Set;

@UiController("ddcui$send-message")
@UiDescriptor("send-message.xml")
@EditedEntityContainer("messageDc")
@LoadDataBeforeShow
public class SendMessage extends StandardEditor<SendMessageEntity> {

    @Inject
    protected Events events;

    @Inject
    protected MessageService messageService;

    @Install(target = Target.DATA_CONTEXT)
    protected Set<Entity> commitDelegate(CommitContext commitContext) {
        messageService.sendMessage(getEditedEntity());
        events.publish(new SendMessageEvent(""));
        return Collections.singleton(getEditedEntity());
    }

}

修改UserInboxMessageMenuBadge类,去掉定时器。

package de.diedavids.cuba.userinbox.web.screens;

import com.haulmont.cuba.core.global.Messages;
import com.haulmont.cuba.gui.ScreenBuilders;
import com.haulmont.cuba.gui.components.Timer;
import com.haulmont.cuba.gui.components.mainwindow.SideMenu;
import com.haulmont.cuba.gui.screen.FrameOwner;
import com.haulmont.cuba.gui.screen.OpenMode;
import com.haulmont.cuba.web.WebConfig;
import de.diedavids.cuba.userinbox.service.MessageService;
import de.diedavids.cuba.userinbox.web.message.UserInbox;
import org.springframework.stereotype.Component;

import javax.inject.Inject;

@Component(UserInboxMessageMenuBadge.NAME)
public class UserInboxMessageMenuBadge {

    static final String NAME = "ddcui_UserInboxMessageMenuBadge";

    @Inject
    protected Messages messages;

    @Inject
    protected ScreenBuilders screenBuilders;

    @Inject
    protected MessageService messageService;

    public void initMessagesMenuItem(SideMenu sideMenu, FrameOwner frameOwner) {


        SideMenu.MenuItem messagesMenuItem = sideMenu.createMenuItem("messages");
        messagesMenuItem.setCaption(messages.getMainMessage("menu-config.ddcui$user-inbox"));
        messagesMenuItem.setIcon("font-icon:ENVELOPE");
        messagesMenuItem.setCommand(menuItem ->
                screenBuilders.screen(frameOwner)
                        .withLaunchMode(OpenMode.NEW_TAB)
                        .withScreenClass(UserInbox.class)
                        .show()
        );

        sideMenu.addMenuItem(messagesMenuItem, 0);

    }



    public void updateMessageCounter(SideMenu sideMenu) {
        sideMenu.getMenuItemNN("messages")
                .setBadgeText(
                        messages.formatMainMessage("menu-config.ddcui$user-inbox.badge", getMessageCounter())
                );
    }

    private long getMessageCounter() {
        return messageService.countUnreadMessagesForCurrentUser();
    }

}

订阅消息发布事件

这里是我自己实现的主界面,如果想用默认的可以修改UserInboxSideMenuMainScreen这个类。

package com.enruipu.rfid.web.mainscreen;

import com.haulmont.cuba.gui.components.mainwindow.SideMenu;
import com.haulmont.cuba.gui.screen.Subscribe;
import com.haulmont.cuba.gui.screen.UiController;
import com.haulmont.cuba.gui.screen.UiDescriptor;
import com.haulmont.cuba.web.app.main.MainScreen;
import de.diedavids.cuba.userinbox.service.MessageService;
import de.diedavids.cuba.userinbox.web.message.SendMessageEvent;
import de.diedavids.cuba.userinbox.web.screens.UserInboxMessageMenuBadge;
import org.springframework.context.event.EventListener;

import javax.inject.Inject;

/**
 * @author Rubin
 * @version v1 2021/3/6 16:41
 */
@UiController("extMainScreen")
@UiDescriptor("ext-main-screen.xml")
public class ExtMainScreen extends MainScreen {

    @Inject
    protected MessageService messageService;

    @Inject
    protected SideMenu sideMenu;

    @Inject
    protected UserInboxMessageMenuBadge userInboxMessageMenuBadge;

    @Subscribe
    protected void onInit(InitEvent event) {
        userInboxMessageMenuBadge.initMessagesMenuItem(sideMenu, this);
    }

    @EventListener
    public void onSendMessage(SendMessageEvent event) {
        if (messageService.countUnreadMessagesForCurrentUser() > 0) {
            userInboxMessageMenuBadge.updateMessageCounter(sideMenu);
        }
    }

    @Override
    @Subscribe
    protected void onAfterShow(AfterShowEvent event) {
        if (messageService.countUnreadMessagesForCurrentUser() > 0) {
            userInboxMessageMenuBadge.updateMessageCounter(sideMenu);
        }
    }

}

搞定~ :ok_hand:

2赞

优化:只有自己的消息才触发更新。

发布

@Install(target = Target.DATA_CONTEXT)
protected Set<Entity> commitDelegate(CommitContext commitContext) {
    SendMessageEntity editedEntity = getEditedEntity();
    messageService.sendMessage(editedEntity);
    events.publish(new SendMessageEvent(editedEntity.getReceivers()));
    return Collections.singleton(editedEntity);
}

订阅

@EventListener
public void onSendMessage(SendMessageEvent event) {
    List<User> receivers = (List<User>) event.getSource();

    // 只匹配自己的消息
    String userName = securityContext.getUser();
    Optional<User> currentUser = receivers
            .stream()
            .filter(user -> user.getLogin().equals(userName))
            .findFirst();

    if (currentUser.isPresent() && userInboxMessageMenuBadge.getMessageCounter() > 0) {
        userInboxMessageMenuBadge.updateMessageCounter(sideMenu);
    }
}

修一bug:已读、手动标记为已读未读,消息数的增减

MessageDetails

点击查看时,未读数 -1。

final SecurityContext securityContext = AppContext.getSecurityContext();

@Subscribe
protected void onAfterShow(AfterShowEvent event) {
    Message editedEntity = getEditedEntity();
    Boolean read = editedEntity.getRead();

    if (!read) {
        String user = securityContext.getUser();
        Map<String ,Boolean> status = new HashMap<>();
        status.put(user, true);
        events.publish(new SendMessageEvent(status));
    }

    editedEntity.setRead(true);
    dataContext.commit();
}

UserInbox

手动标记为已读未读时,±1。

@Subscribe("messagesTable.toggleRead")
protected void onMessagesTableToggleRead(Action.ActionPerformedEvent event) {
    Message singleSelected = messagesTable.getSingleSelected();
    Boolean read = singleSelected.getRead();
    singleSelected.toggleRead();
    // read为true则-1,false,+1
    String user = securityContext.getUser();

    Map<String ,Boolean> status = new HashMap<>();
    status.put(user, !read);

    events.publish(new SendMessageEvent(status));
    dataContext.commit();
}

UserInboxMessageMenuBadge

新增readUpdateMessageCounter方法

public void readUpdateMessageCounter(String user, SideMenu sideMenu, Map<String, Boolean> status) {

    // 只处理自己操作的'标记为已读未读'
    if (!status.containsKey(user)) {
        return;
    }

    long messageCounter = getMessageCounter();

    sideMenu.getMenuItemNN("messages")
            .setBadgeText(
                    messages.formatMainMessage("menu-config.ddcui$user-inbox.badge", status.get(user) ? messageCounter - 1 : messageCounter + 1)
            );
}

ExtMainScreen

package com.enruipu.rfid.web.mainscreen;

import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.core.sys.SecurityContext;
import com.haulmont.cuba.gui.components.mainwindow.SideMenu;
import com.haulmont.cuba.gui.screen.Subscribe;
import com.haulmont.cuba.gui.screen.UiController;
import com.haulmont.cuba.gui.screen.UiDescriptor;
import com.haulmont.cuba.security.entity.User;
import com.haulmont.cuba.web.app.main.MainScreen;
import de.diedavids.cuba.userinbox.web.message.SendMessageEvent;
import de.diedavids.cuba.userinbox.web.screens.UserInboxMessageMenuBadge;
import org.springframework.context.event.EventListener;

import javax.inject.Inject;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * @author Rubin
 * @version v1 2021/3/6 16:41
 */
@UiController("extMainScreen")
@UiDescriptor("ext-main-screen.xml")
public class ExtMainScreen extends MainScreen {

    @Inject
    protected SideMenu sideMenu;

    @Inject
    protected UserInboxMessageMenuBadge userInboxMessageMenuBadge;

    final SecurityContext securityContext = AppContext.getSecurityContext();

    @Subscribe
    protected void onInit(InitEvent event) {
        userInboxMessageMenuBadge.initMessagesMenuItem(sideMenu, this);
    }

    @EventListener
    public void onSendMessage(SendMessageEvent event) {
        Object source = event.getSource();
        String userName = securityContext.getUser();

        // 已/未读,更新未读数量
        if (source instanceof Map) {
            userInboxMessageMenuBadge.readUpdateMessageCounter(userName, sideMenu, (Map<String, Boolean>) source);
            return;
        }

        List<User> receivers = (List<User>) source;

        // 只匹配自己的消息
        Optional<User> currentUser = receivers
                .stream()
                .filter(user -> user.getLogin().equals(userName))
                .findFirst();

        if (currentUser.isPresent()) {
            onAfterShow(null);
        }
    }

    @Override
    @Subscribe
    protected void onAfterShow(AfterShowEvent event) {
        userInboxMessageMenuBadge.updateMessageCounter(sideMenu);
    }

}