/* * Copyright (C) 2015 Actor LLC. <https://actor.im> */ package im.actor.core.js.modules; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArray; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import im.actor.core.entity.Avatar; import im.actor.core.entity.Contact; import im.actor.core.entity.Dialog; import im.actor.core.entity.Message; import im.actor.core.entity.Peer; import im.actor.core.entity.PeerType; import im.actor.core.entity.SearchEntity; import im.actor.core.entity.Sticker; import im.actor.core.entity.StickerPack; import im.actor.core.entity.content.DocumentContent; import im.actor.core.entity.content.FileRemoteSource; import im.actor.core.entity.content.StickerContent; import im.actor.core.js.JsMessenger; import im.actor.core.js.entity.JsBlockedUser; import im.actor.core.js.entity.JsCall; import im.actor.core.js.entity.JsContact; import im.actor.core.js.entity.JsCounter; import im.actor.core.js.entity.JsDialog; import im.actor.core.js.entity.JsDialogGroup; import im.actor.core.js.entity.JsDialogShort; import im.actor.core.js.entity.JsEventBusCallback; import im.actor.core.js.entity.JsGroup; import im.actor.core.js.entity.JsMessage; import im.actor.core.js.entity.JsOnlineGroup; import im.actor.core.js.entity.JsOnlineUser; import im.actor.core.js.entity.JsSearchEntity; import im.actor.core.js.entity.JsSticker; import im.actor.core.js.entity.JsTyping; import im.actor.core.js.entity.JsUser; import im.actor.core.modules.AbsModule; import im.actor.core.modules.Modules; import im.actor.core.viewmodel.AppStateVM; import im.actor.core.viewmodel.CallMember; import im.actor.core.viewmodel.CallState; import im.actor.core.viewmodel.CallVM; import im.actor.core.viewmodel.DialogGroup; import im.actor.core.viewmodel.DialogSmall; import im.actor.core.viewmodel.GlobalStateVM; import im.actor.core.viewmodel.GroupTypingVM; import im.actor.core.viewmodel.GroupVM; import im.actor.core.viewmodel.UserPresence; import im.actor.core.viewmodel.UserTypingVM; import im.actor.core.viewmodel.UserVM; import im.actor.core.viewmodel.generics.ArrayListMediaTrack; import im.actor.runtime.js.mvvm.JsDisplayList; import im.actor.runtime.js.mvvm.JsDisplayListBind; import im.actor.runtime.mvvm.ModelChangedListener; import im.actor.runtime.mvvm.Value; import im.actor.runtime.mvvm.ValueChangedListener; import im.actor.runtime.mvvm.ValueModel; import im.actor.runtime.webrtc.WebRTCMediaStream; public class JsBindingModule extends AbsModule implements JsFileLoadedListener { private JsMessenger messenger; private HashMap<Integer, JsBindedValue<JsUser>> users = new HashMap<>(); private HashMap<Integer, JsBindedValue<JsGroup>> groups = new HashMap<>(); private HashMap<Integer, JsBindedValue<JsOnlineUser>> usersOnlines = new HashMap<>(); private HashMap<Integer, JsBindedValue<JsBlockedUser>> usersBloked = new HashMap<>(); private HashMap<Integer, JsBindedValue<JsOnlineGroup>> groupOnlines = new HashMap<>(); private HashMap<Peer, JsBindedValue<JsTyping>> typing = new HashMap<>(); private JsBindedValue<String> onlineState; private JsDisplayList<JsDialog, Dialog> dialogsList; private JsDisplayList<JsContact, Contact> contactsList; private JsDisplayList<JsSearchEntity, SearchEntity> searchList; private HashMap<Peer, JsDisplayList<JsMessage, Message>> messageLists = new HashMap<>(); private JsBindedValue<JsCounter> globalCounter; private JsBindedValue<JsCounter> tempGlobalCounter; private JsBindedValue<JsArray<JsDialogGroup>> dialogsGroupedList; private HashMap<String, JsBindedValue<JsCall>> calls = new HashMap<>(); private ArrayList<JsEventBusCallback> eventBusCallbacks = new ArrayList<>(); private JsBindedValue<JsArray<JsSticker>> stickers; public JsBindingModule(JsMessenger messenger, JsFilesModule filesModule, Modules modules) { super(modules); this.messenger = messenger; filesModule.registerListener(this); } public void broadcastEvent(String tag, JavaScriptObject obj) { for (JsEventBusCallback c : eventBusCallbacks) { c.onEvent(tag, obj); } } public void subscribeEventBus(JsEventBusCallback callback) { if (!eventBusCallbacks.contains(callback)) { eventBusCallbacks.add(callback); } } public void unsubscribeEventBus(JsEventBusCallback callback) { eventBusCallbacks.remove(callback); } public JsBindedValue<JsArray<JsDialogGroup>> getDialogsGroupedList() { if (dialogsGroupedList == null) { ValueModel<ArrayList<DialogGroup>> dialogGroups = context().getMessagesModule().getDialogGroupsVM().getGroupsValueModel(); dialogsGroupedList = new JsBindedValue<>(); dialogGroups.subscribe(new ValueChangedListener<ArrayList<DialogGroup>>() { @Override public void onChanged(ArrayList<DialogGroup> val, Value<ArrayList<DialogGroup>> valueModel) { if (val == null) { dialogsGroupedList.changeValue(JsArray.createArray().<JsArray<JsDialogGroup>>cast()); } else { JsArray<JsDialogGroup> res = JsArray.createArray().cast(); for (DialogGroup g : val) { JsArray<JsDialogShort> resd = JsArray.createArray().cast(); for (DialogSmall ds : g.getDialogs()) { resd.push(JsDialogShort.create(messenger.buildPeerInfo(ds.getPeer()), ds.getCounter())); } res.push(JsDialogGroup.create(g.getTitle(), g.getKey(), resd)); } dialogsGroupedList.changeValue(res); } } }); } return dialogsGroupedList; } public JsBindedValue<JsArray<JsSticker>> getStickers() { if (stickers == null) { stickers = new JsBindedValue<>((JsArray<JsSticker>) JsArray.createArray()); messenger.getAvailableStickersVM().getOwnStickerPacks().subscribe(new ValueChangedListener<ArrayList<StickerPack>>() { @Override public void onChanged(ArrayList<StickerPack> val, Value<ArrayList<StickerPack>> valueModel) { JsArray<JsSticker> stickerJsArray = JsArray.createArray().cast(); stickerJsArray.setLength(0); for (StickerPack pack : val) { for (Sticker sticker : pack.getStickers()) { stickerJsArray.push(JsSticker.create(sticker)); } } stickers.changeValue(stickerJsArray); } }); } return stickers; } public JsBindedValue<String> getOnlineStatus() { if (onlineState == null) { final GlobalStateVM vm = context().getConductor().getGlobalStateVM(); onlineState = new JsBindedValue<>("online"); vm.getIsConnecting().subscribe(new ValueChangedListener<Boolean>() { @Override public void onChanged(Boolean val, Value<Boolean> valueModel) { if (val) { onlineState.changeValue("connecting"); } else { if (vm.getIsSyncing().get()) { onlineState.changeValue("updating"); } else { onlineState.changeValue("online"); } } } }); vm.getIsSyncing().subscribe(new ValueChangedListener<Boolean>() { @Override public void onChanged(Boolean val, Value<Boolean> valueModel) { if (vm.getIsConnecting().get()) { onlineState.changeValue("connecting"); } else { if (val) { onlineState.changeValue("updating"); } else { onlineState.changeValue("online"); } } } }); } return onlineState; } public JsBindedValue<JsUser> getUser(int uid) { if (!users.containsKey(uid)) { final UserVM userVM = context().getUsersModule().getUsers().get(uid); final JsBindedValue<JsUser> value = new JsBindedValue<>(JsUser.fromUserVM(userVM, messenger)); // Bind updates userVM.subscribe(new ModelChangedListener<UserVM>() { @Override public void onChanged(UserVM model) { value.changeValue(JsUser.fromUserVM(userVM, messenger)); } }, false); // Sign for contact separately userVM.isContact().subscribe(new ValueChangedListener<Boolean>() { @Override public void onChanged(Boolean val, Value<Boolean> valueModel) { value.changeValue(JsUser.fromUserVM(userVM, messenger)); } }); // Sign for blocked separately userVM.getIsBlocked().subscribe(new ValueChangedListener<Boolean>() { @Override public void onChanged(Boolean val, Value<Boolean> valueModel) { value.changeValue(JsUser.fromUserVM(userVM, messenger)); } }); users.put(uid, value); } return users.get(uid); } public JsBindedValue<JsOnlineUser> getUserOnline(int uid) { if (!usersOnlines.containsKey(uid)) { final JsBindedValue<JsOnlineUser> value = new JsBindedValue<>(); final UserVM userVM = context().getUsersModule().getUsers().get(uid); userVM.getPresence().subscribe(new ValueChangedListener<UserPresence>() { @Override public void onChanged(UserPresence val, Value<UserPresence> valueModel) { if (val.getState() == UserPresence.State.UNKNOWN) { value.changeValue(null); } else { String presenceString = messenger.getFormatter().formatPresence(val, userVM.getSex()); if (userVM.isBot()) { presenceString = "bot"; } value.changeValue(JsOnlineUser.create(presenceString, val.getState() == UserPresence.State.ONLINE)); } } }); usersOnlines.put(uid, value); } return usersOnlines.get(uid); } public JsBindedValue<JsBlockedUser> getUserBlocked(int uid) { if (!usersBloked.containsKey(uid)) { final JsBindedValue<JsBlockedUser> value = new JsBindedValue<>(); final UserVM userVM = context().getUsersModule().getUsers().get(uid); userVM.getIsBlocked().subscribe(new ValueChangedListener<Boolean>() { @Override public void onChanged(Boolean val, Value<Boolean> valueModel) { value.changeValue(JsBlockedUser.create(val)); } }); usersBloked.put(uid, value); } return usersBloked.get(uid); } public JsBindedValue<JsGroup> getGroup(int gid) { if (!groups.containsKey(gid)) { final GroupVM groupVM = context().getGroupsModule().getGroupsCollection().get(gid); final JsBindedValue<JsGroup> value = new JsBindedValue<>(JsGroup.fromGroupVM(groupVM, messenger)); // Bind updates groupVM.subscribe(new ModelChangedListener<GroupVM>() { @Override public void onChanged(GroupVM model) { value.changeValue(JsGroup.fromGroupVM(groupVM, messenger)); } }, false); groups.put(gid, value); } return groups.get(gid); } public JsBindedValue<JsOnlineGroup> getGroupOnline(int gid) { if (!groupOnlines.containsKey(gid)) { final JsBindedValue<JsOnlineGroup> value = new JsBindedValue<>(); final GroupVM groupVM = context().getGroupsModule().getGroupsCollection().get(gid); groupVM.getPresence().subscribe(new ValueChangedListener<Integer>() { @Override public void onChanged(Integer val, Value<Integer> valueModel) { if (groupVM.isMember().get()) { if (val == null) { value.changeValue(null); return; } String presence = messenger.getFormatter().formatGroupMembers(groupVM.getMembersCount().get()); if (val > 0) { presence += ", " + messenger.getFormatter().formatGroupOnline(val); } value.changeValue(JsOnlineGroup.create(groupVM.getMembersCount().get(), val, presence, false)); } else { value.changeValue(JsOnlineGroup.create(0, 0, "Not member", false)); } } }); groupOnlines.put(gid, value); } return groupOnlines.get(gid); } public JsBindedValue<JsCall> getCall(String id) { if (!calls.containsKey(id)) { final CallVM callModel = messenger.getCall(Long.parseLong(id)); final JsBindedValue<JsCall> jsCall = new JsBindedValue<>(JsCall.create(messenger, callModel)); callModel.getState().subscribe(new ValueChangedListener<CallState>() { @Override public void onChanged(CallState val, Value<CallState> valueModel) { jsCall.changeValue(JsCall.create(messenger, callModel)); } }); callModel.getMembers().subscribe(new ValueChangedListener<ArrayList<CallMember>>() { @Override public void onChanged(ArrayList<CallMember> val, Value<ArrayList<CallMember>> valueModel) { jsCall.changeValue(JsCall.create(messenger, callModel)); } }); callModel.getOwnVideoTracks().subscribe(new ValueChangedListener<ArrayListMediaTrack>() { @Override public void onChanged(ArrayListMediaTrack val, Value<ArrayListMediaTrack> valueModel) { jsCall.changeValue(JsCall.create(messenger, callModel)); } }); callModel.getTheirVideoTracks().subscribe(new ValueChangedListener<ArrayListMediaTrack>() { @Override public void onChanged(ArrayListMediaTrack val, Value<ArrayListMediaTrack> valueModel) { jsCall.changeValue(JsCall.create(messenger, callModel)); } }); calls.put(id, jsCall); } return calls.get(id); } public JsBindedValue<JsTyping> getTyping(final Peer peer) { if (!typing.containsKey(peer)) { if (peer.getPeerType() == PeerType.PRIVATE) { UserTypingVM userTypingVM = context().getTypingModule().getTyping(peer.getPeerId()); final JsBindedValue<JsTyping> value = new JsBindedValue<>(); userTypingVM.getTyping().subscribe((val, valueModel) -> { String typingValue = null; if (val) { typingValue = messenger.getFormatter().formatTyping(""); } value.changeValue(JsTyping.create(typingValue)); }); typing.put(peer, value); } else if (peer.getPeerType() == PeerType.GROUP) { GroupTypingVM groupTypingVM = context().getTypingModule().getGroupTyping(peer.getPeerId()); final JsBindedValue<JsTyping> value = new JsBindedValue<>(); groupTypingVM.getActive().subscribe((val, valueModel) -> { if (val.length > 0) { ArrayList<String> names = new ArrayList<>(); for (int i : val) { names.add(context() .getUsersModule() .getUsers() .get(i) .getName() .get()); } value.changeValue(JsTyping.create(messenger.getFormatter().formatTyping(names))); } else { value.changeValue(JsTyping.create(null)); } }); typing.put(peer, value); } else { throw new RuntimeException(); } } return typing.get(peer); } public JsDisplayList<JsSearchEntity, SearchEntity> getSharedSearchList() { if (searchList == null) { searchList = (JsDisplayList<JsSearchEntity, SearchEntity>) context().getDisplayListsModule().buildSearchList(true); } return searchList; } public JsDisplayList<JsContact, Contact> getSharedContactList() { if (contactsList == null) { contactsList = (JsDisplayList<JsContact, Contact>) context().getDisplayListsModule().getContactsSharedList(); } return contactsList; } public JsDisplayList<JsDialog, Dialog> getSharedDialogList() { if (dialogsList == null) { dialogsList = (JsDisplayList<JsDialog, Dialog>) context().getDisplayListsModule().getDialogsSharedList(); } return dialogsList; } public JsDisplayList<JsMessage, Message> getSharedMessageList(Peer peer) { if (!messageLists.containsKey(peer)) { messageLists.put(peer, (JsDisplayList<JsMessage, Message>) context().getDisplayListsModule().getMessagesSharedList(peer)); } return messageLists.get(peer); } public JsBindedValue<JsCounter> getGlobalCounter() { if (globalCounter == null) { ValueModel<Integer> counter = context().getConductor().getGlobalStateVM().getGlobalCounter(); globalCounter = new JsBindedValue<>(JsCounter.create(counter.get())); counter.subscribe(new ValueChangedListener<Integer>() { @Override public void onChanged(Integer val, Value<Integer> valueModel) { globalCounter.changeValue(JsCounter.create(val)); } }, false); } return globalCounter; } public JsBindedValue<JsCounter> getTempGlobalCounter() { if (tempGlobalCounter == null) { ValueModel<Integer> counter = context().getConductor().getGlobalStateVM().getGlobalTempCounter(); tempGlobalCounter = new JsBindedValue<>(JsCounter.create(counter.get())); counter.subscribe(new ValueChangedListener<Integer>() { @Override public void onChanged(Integer val, Value<Integer> valueModel) { tempGlobalCounter.changeValue(JsCounter.create(val)); } }, false); } return tempGlobalCounter; } @Override public void onFileLoaded(HashSet<Long> fileId) { // // Dialogs List // if (dialogsList != null) { for (JsDisplayListBind<JsDialog, Dialog> b : dialogsList.getActiveBinds()) { b.startReconverting(); for (Dialog dialog : b.getRawItems()) { if (checkAvatar(dialog.getDialogAvatar(), fileId)) { b.forceReconvert(dialog.getEngineId()); } } b.stopReconverting(); } } // // Grouped Dialogs // if (dialogsGroupedList != null) { ArrayList<DialogGroup> groups = context().getMessagesModule().getDialogGroupsVM().getGroupsValueModel().get(); if (groups != null) { outer: for (DialogGroup g : groups) { for (DialogSmall ds : g.getDialogs()) { if (checkAvatar(ds.getAvatar(), fileId)) { context().getMessagesModule().getDialogGroupsVM().getGroupsValueModel().forceNotify(); break outer; } } } } } // // Contacts List // if (contactsList != null) { for (JsDisplayListBind<JsContact, Contact> b : contactsList.getActiveBinds()) { b.startReconverting(); for (Contact contact : b.getRawItems()) { if (checkAvatar(contact.getAvatar(), fileId)) { b.forceReconvert(contact.getEngineId()); } } b.stopReconverting(); } } // // Message Contents // for (JsDisplayList<JsMessage, Message> messageList : messageLists.values()) { for (JsDisplayListBind<JsMessage, Message> b : messageList.getActiveBinds()) { b.startReconverting(); for (Message message : b.getRawItems()) { UserVM user = context().getUsersModule().getUsers().get(message.getSenderId()); if (checkAvatar(user.getAvatar().get(), fileId)) { b.forceReconvert(message.getEngineId()); continue; } if (message.getContent() instanceof DocumentContent) { DocumentContent doc = (DocumentContent) message.getContent(); if (doc.getSource() instanceof FileRemoteSource) { if (fileId.contains(((FileRemoteSource) doc.getSource()).getFileReference().getFileId())) { b.forceReconvert(message.getEngineId()); } } } if (message.getContent() instanceof StickerContent) { StickerContent content = (StickerContent) message.getContent(); if (content.getImage512() != null) { long stickerFileId = content.getImage512().getReference().getFileId(); if (fileId.contains(stickerFileId)) { b.forceReconvert(message.getEngineId()); } } else if (content.getImage256() != null) { long stickerFileId = content.getImage256().getReference().getFileId(); if (fileId.contains(stickerFileId)) { b.forceReconvert(message.getEngineId()); } } } } b.stopReconverting(); } } // // Users // for (JsBindedValue<JsUser> u : users.values()) { int uid = u.get().getUid(); UserVM userVM = context().getUsersModule().getUsers().get(uid); if (checkAvatar(userVM.getAvatar().get(), fileId)) { u.changeValue(JsUser.fromUserVM(userVM, messenger)); } } // // Groups // for (JsBindedValue<JsGroup> g : groups.values()) { int gid = g.get().getGid(); GroupVM groupVM = context().getGroupsModule().getGroupsCollection().get(gid); if (checkAvatar(groupVM.getAvatar().get(), fileId)) { g.changeValue(JsGroup.fromGroupVM(groupVM, messenger)); } } // // Stickers // if (stickers != null) { outer: for (StickerPack stickerPack : messenger.getAvailableStickersVM().getOwnStickerPacks().get()) { for (Sticker s : stickerPack.getStickers()) { if (s.getImage256() != null && fileId.contains(s.getImage256().getFileId())) { messenger.getAvailableStickersVM().getOwnStickerPacks().forceNotify(); break outer; } } } } } protected boolean checkAvatar(Avatar avatar, HashSet<Long> fileIds) { if (avatar == null) { return false; } if (avatar.getSmallImage() != null && fileIds.contains(avatar.getSmallImage().getFileReference().getFileId())) { return true; } if (avatar.getFullImage() != null && fileIds.contains(avatar.getFullImage().getFileReference().getFileId())) { return true; } if (avatar.getLargeImage() != null && fileIds.contains(avatar.getLargeImage().getFileReference().getFileId())) { return true; } return false; } }