/*
* 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> {
}
}