3 var env = require('gitter-web-env');
4 var logger = env.logger;
5 var _ = require('lodash');
6 var unreadItemService = require('gitter-web-unread-items');
7 var getVersion = require('gitter-web-serialization/lib/get-model-version');
8 var UserIdStrategy = require('./user-id-strategy');
9 const TroupeIdStrategy = require('./troupe-id-strategy');
10 var mongoUtils = require('gitter-web-persistence-utils/lib/mongo-utils');
11 var Promise = require('bluebird');
13 getMockIdFromVirtualUser,
14 transformVirtualUserIntoMockedFromUser
15 } = require('gitter-web-users/lib/virtual-user-service');
16 const generateProxyUrl = require('gitter-web-text-processor/lib/generate-proxy-url');
18 function formatDate(d) {
19 return d ? d.toISOString() : null;
22 function UnreadItemStrategy(options) {
25 this.preload = function() {
26 return unreadItemService.getUnreadItems(options.userId, options.roomId).then(function(ids) {
29 _.each(ids, function(id) {
33 unreadItemsHash = hash;
37 this.map = function(id) {
38 return !!unreadItemsHash[id];
42 UnreadItemStrategy.prototype = {
43 name: 'UnreadItemStrategy'
47 * Serializes chat into JSON
48 * - if there is no `currentUserId`, all the messages are going to have default {unread: false}
50 function ChatStrategy({
55 serializeFromUserId = true,
56 serializeToTroupeId = false,
60 // useLookups will be set to true if there are any lookups that this strategy
61 // understands. Currently it only knows about user lookups.
62 var useLookups = false;
64 if (lookups && lookups.indexOf('user') !== -1) {
71 // we're breaking users out, but then not returning their displayNames
72 // which kinda defeats the purpose
73 logger.warn('ChatStrategy was called with lookups, but also with lean', { lookups, lean });
77 var userStrategy, troupeStrategy, unreadItemStrategy;
79 this.preload = function(items) {
80 if (items.isEmpty()) return;
84 // If the user is fixed in options, we don't need to look them up using a strategy...
85 if (!user && serializeFromUserId) {
86 userStrategy = new UserIdStrategy({ lean });
88 var users = items.map(function(i) {
91 strategies.push(userStrategy.preload(users));
94 if (serializeToTroupeId) {
95 troupeStrategy = new TroupeIdStrategy({ lean });
97 const troupeIds = items.map(function(i) {
100 strategies.push(troupeStrategy.preload(troupeIds));
104 unreadItemStrategy = new UnreadItemStrategy({
105 userId: currentUserId,
108 strategies.push(unreadItemStrategy.preload());
111 return Promise.all(strategies);
114 function safeArray(array) {
115 if (!array) return [];
119 function undefinedForEmptyArray(array) {
120 if (!array) return undefined;
121 if (!array.length) return undefined;
125 function mapUser(userId) {
126 if (!serializeFromUserId) {
128 } else if (userLookups) {
129 if (!userLookups[userId]) {
130 userLookups[userId] = userStrategy.map(userId);
135 return userStrategy.map(userId);
139 function mapVirtualUser(virtualUser) {
140 if (!serializeFromUserId) {
142 } else if (userLookups) {
143 const mockUserId = getMockIdFromVirtualUser(virtualUser);
144 if (!userLookups[mockUserId]) {
145 userLookups[mockUserId] = transformVirtualUserIntoMockedFromUser(virtualUser);
150 return transformVirtualUserIntoMockedFromUser(virtualUser);
154 this.map = function(item) {
155 // If there is no unread strategy(meaning currentUserId was undefined), don't define how it's unread/read.
157 // We don't want to default to true/false because even when someone is signed in,
158 // some places of code don't define `currentUserId`. We don't want to accidentally
159 // override the actual value. See `live-collection-chats.js` as an example
160 var unread = unreadItemStrategy ? unreadItemStrategy.map(item._id) : undefined;
162 var castArray = lean ? undefinedForEmptyArray : safeArray;
166 initial = mongoUtils.objectIDsEqual(item._id, initialId);
169 const serializedData = {
174 sent: formatDate(item.sent),
175 editedAt: item.editedAt ? formatDate(item.editedAt) : undefined,
176 parentId: item.parentId,
177 threadMessageCount: item.threadMessageCount,
179 readBy: item.readBy ? item.readBy.length : undefined,
180 urls: castArray(item.urls),
181 initial: initial || undefined,
184 _.map(item.mentions, function(m) {
186 screenName: m.screenName,
188 userIds: m.userIds, // For groups
189 group: m.group || undefined,
190 announcement: m.announcement || undefined
194 issues: castArray(item.issues),
195 meta: castArray(item.meta),
196 highlights: item.highlights,
200 if (troupeStrategy) {
201 serializedData.toTroupe = troupeStrategy.map(item.toTroupeId);
204 if (item.virtualUser) {
205 serializedData.virtualUser = {
206 type: item.virtualUser.type,
207 externalId: item.virtualUser.externalId,
208 displayName: item.virtualUser.displayName,
209 avatarUrl: item.virtualUser.avatarUrl
210 ? generateProxyUrl(item.virtualUser.avatarUrl)
214 // Mock the fromUser at a basic level for legacy apps (Android and iOS)
215 serializedData.fromUser = mapVirtualUser(item.virtualUser);
217 serializedData.fromUser = user ? user : mapUser(item.fromUserId);
220 return serializedData;
223 this.postProcess = function(serialized) {
226 items: serialized.toArray(),
232 return serialized.toArray();
237 ChatStrategy.prototype = {
241 module.exports = ChatStrategy;