/* * Copyright (C) 2015 Actor LLC. <https://actor.im> */ package im.actor.core.modules.messaging.history; import java.util.ArrayList; import java.util.List; import im.actor.core.api.ApiMessageContainer; import im.actor.core.api.ApiMessageReaction; import im.actor.core.api.ApiMessageState; import im.actor.core.api.rpc.RequestLoadHistory; import im.actor.core.api.rpc.ResponseLoadHistory; import im.actor.core.entity.EntityConverter; import im.actor.core.entity.Message; import im.actor.core.entity.MessageState; import im.actor.core.entity.Peer; import im.actor.core.entity.Reaction; import im.actor.core.entity.content.AbsContent; import im.actor.core.modules.api.ApiSupportConfiguration; import im.actor.core.modules.ModuleContext; import im.actor.core.modules.ModuleActor; import im.actor.runtime.Log; import im.actor.runtime.actors.ask.AskMessage; import im.actor.runtime.actors.messages.Void; import im.actor.runtime.function.Consumer; import im.actor.runtime.promise.Promise; public class ConversationHistoryActor extends ModuleActor { // j2objc workaround private static final Void DUMB = null; private static final int LIMIT = 20; private final String KEY_LOADED_DATE; private final String KEY_LOADED; private final String KEY_LOADED_INIT; private final Peer peer; private long historyMaxDate; private boolean historyLoaded; private boolean isFreezed = false; public ConversationHistoryActor(Peer peer, ModuleContext context) { super(context); this.peer = peer; this.KEY_LOADED_DATE = "conv_" + peer + "_history_date"; this.KEY_LOADED = "conv_" + peer + "_history_loaded"; this.KEY_LOADED_INIT = "conv_" + peer + "_history_inited"; } @Override public void preStart() { super.preStart(); historyMaxDate = preferences().getLong(KEY_LOADED_DATE, Long.MAX_VALUE); historyLoaded = preferences().getBool(KEY_LOADED, false); if (!preferences().getBool(KEY_LOADED_INIT, false)) { self().send(new LoadMore()); } } private void onLoadMore() { if (isFreezed || historyLoaded) { return; } isFreezed = true; api(new RequestLoadHistory(buidOutPeer(peer), historyMaxDate, null, LIMIT, ApiSupportConfiguration.OPTIMIZATIONS)) .chain(r -> updates().applyRelatedData(r.getUsers(), r.getGroups())) .chain(r -> updates().loadRequiredPeers(r.getUserPeers(), r.getGroupPeers())) .flatMap(r -> { Log.d("HistoryActor", "Apply " + historyMaxDate); return applyHistory(peer, r.getHistory()); }) .map(r -> { Log.d("HistoryActor", "Applied"); isFreezed = false; unstashAll(); return null; }); } private Promise<Void> onReset() { Log.d("HistoryActor", "Reset"); historyMaxDate = Long.MAX_VALUE; preferences().putLong(KEY_LOADED_DATE, Long.MAX_VALUE); historyLoaded = false; preferences().putBool(KEY_LOADED, false); preferences().putBool(KEY_LOADED_INIT, false); isFreezed = true; return context().getMessagesModule().getRouter().onChatReset(peer) .then(r -> { isFreezed = false; unstashAll(); onLoadMore(); }); } private Promise<Void> applyHistory(Peer peer, List<ApiMessageContainer> history) { ArrayList<Message> messages = new ArrayList<>(); long maxLoadedDate = Long.MAX_VALUE; long maxReadDate = 0; long maxReceiveDate = 0; for (ApiMessageContainer historyMessage : history) { AbsContent content = AbsContent.fromMessage(historyMessage.getMessage()); MessageState state = EntityConverter.convert(historyMessage.getState()); ArrayList<Reaction> reactions = new ArrayList<>(); for (ApiMessageReaction r : historyMessage.getReactions()) { reactions.add(new Reaction(r.getCode(), r.getUsers())); } messages.add(new Message(historyMessage.getRid(), historyMessage.getDate(), historyMessage.getDate(), historyMessage.getSenderUid(), state, content, reactions, 0)); maxLoadedDate = Math.min(historyMessage.getDate(), maxLoadedDate); if (historyMessage.getState() == ApiMessageState.RECEIVED) { maxReceiveDate = Math.max(historyMessage.getDate(), maxReceiveDate); } else if (historyMessage.getState() == ApiMessageState.READ) { maxReceiveDate = Math.max(historyMessage.getDate(), maxReceiveDate); maxReadDate = Math.max(historyMessage.getDate(), maxReadDate); } } boolean isEnded = history.size() < LIMIT; // Sending updates to conversation actor final long finalMaxLoadedDate = maxLoadedDate; return context().getMessagesModule().getRouter() .onChatHistoryLoaded(peer, messages, maxReceiveDate, maxReadDate, isEnded) .map(r -> { // Saving Internal State if (isEnded) { historyLoaded = true; } else { historyLoaded = false; historyMaxDate = finalMaxLoadedDate; } preferences().putLong(KEY_LOADED_DATE, finalMaxLoadedDate); preferences().putBool(KEY_LOADED, historyLoaded); preferences().putBool(KEY_LOADED_INIT, true); return r; }); } @Override public Promise onAsk(Object message) throws Exception { if (message instanceof Reset) { if (isFreezed) { stash(); return null; } onReset(); return Promise.success(null); } else { return super.onAsk(message); } } @Override public void onReceive(Object message) { if (message instanceof LoadMore) { onLoadMore(); } else { super.onReceive(message); } } public static class LoadMore { } public static class Reset implements AskMessage<Void> { } }