multichatManager.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. /** @const */
  2. var POLL_TIMEOUT = 55000;
  3. /** @interface */
  4. function ChatSystem() {
  5. };
  6. /** */
  7. ChatSystem.prototype.onRequest = function() {};
  8. /**
  9. * @return {string|null}
  10. **/
  11. ChatSystem.prototype.getId = function() {};
  12. /**
  13. * @return {ChatContext}
  14. **/
  15. ChatSystem.prototype.getChatContext = function() {};
  16. /**
  17. * Async fetch history
  18. * @param {Room} chan
  19. * @param {string} text
  20. **/
  21. ChatSystem.prototype.sendMeMsg = function(chan, text) {};
  22. /**
  23. * Async fetch history
  24. * @param {Room} chan
  25. * @param {string} text
  26. * @param {Object=} attachments
  27. **/
  28. ChatSystem.prototype.sendMsg = function(chan, text, attachments) {};
  29. /**
  30. * Async star msg
  31. * @param {Room} chan
  32. * @param {string} msgId
  33. **/
  34. ChatSystem.prototype.starMsg = function(chan, msgId) {};
  35. /**
  36. * Async remove star from msg
  37. * @param {Room} chan
  38. * @param {string} msgId
  39. **/
  40. ChatSystem.prototype.unstarMsg = function(chan, msgId) {};
  41. /**
  42. * Async fetch history
  43. * @param {Room} chan
  44. * @param {number} ts
  45. **/
  46. ChatSystem.prototype.removeMsg = function(chan, ts) {};
  47. /**
  48. * Edit msg
  49. * @param {Room} chan
  50. * @param {number} ts
  51. * @param {string} newText
  52. **/
  53. ChatSystem.prototype.editMsg = function(chan, ts, newText) {};
  54. /**
  55. * Add reaction
  56. * @param {Room} chan
  57. * @param {number} ts
  58. * @param {string} reaction
  59. **/
  60. ChatSystem.prototype.addReaction = function(chan, ts, reaction) {};
  61. /**
  62. * Add reaction
  63. * @param {Room} chan
  64. * @param {number} ts
  65. * @param {string} reaction
  66. **/
  67. ChatSystem.prototype.removeReaction = function(chan, ts, reaction) {};
  68. /**
  69. * Add reaction
  70. * @param {Room} chan
  71. * @param {string} contentType
  72. * @param {function((string|null))} callback
  73. **/
  74. ChatSystem.prototype.openUploadFileStream = function(chan, contentType, callback) {};
  75. /**
  76. * Async fetch history
  77. * @param {Room} chan
  78. **/
  79. ChatSystem.prototype.fetchHistory = function(chan, cb) {};
  80. /**
  81. * @param {Room} chan
  82. **/
  83. ChatSystem.prototype.sendTyping = function(chan) {};
  84. /**
  85. * @param {Room} chan
  86. * @param {number} ts
  87. **/
  88. ChatSystem.prototype.markRead = function(chan, ts) {};
  89. /**
  90. * @param {Room} chan
  91. * @param {string} cmd
  92. * @param {string} args
  93. **/
  94. ChatSystem.prototype.sendCommand = function(chan, cmd, args) {};
  95. /**
  96. * @param {number} knownVersion
  97. * @param {number} nowTs
  98. * @param {boolean} withTyping
  99. * @return {Object|null}
  100. **/
  101. ChatSystem.prototype.poll = function(knownVersion, nowTs, withTyping) {};
  102. /**
  103. * @param {string} path
  104. * @return {({url: string, headers: Object})|null} path to get
  105. **/
  106. ChatSystem.prototype.getImage = function(path) {};
  107. /**
  108. * @constructor
  109. **/
  110. function MultiChatManager() {
  111. /** @type {Array<ChatSystem>} */
  112. this.contexts = [];
  113. }
  114. /**
  115. * @param {ChatSystem} chatSystem
  116. **/
  117. MultiChatManager.prototype.push = function(chatSystem) {
  118. this.contexts.push(chatSystem);
  119. };
  120. /**
  121. * @param {string} id
  122. * @return {ChatSystem|null}
  123. **/
  124. MultiChatManager.prototype.getById = function(id) {
  125. for (var i = 0, nbContexts = this.contexts.length; i < nbContexts; i++)
  126. if (id === this.contexts[i].getId())
  127. return this.contexts[i];
  128. return null;
  129. };
  130. /**
  131. * You may want to return true to break the loop
  132. **/
  133. MultiChatManager.prototype.foreachChannels = function(cb) {
  134. for (var i =0, nbCtx =this.contexts.length; i < nbCtx; i++) {
  135. var ctx = this.contexts[i].getChatContext();
  136. for (var chanId in ctx.channels)
  137. if (cb(ctx.channels[chanId], chanId) === true)
  138. return;
  139. }
  140. };
  141. /**
  142. * You may want to return true to break the loop
  143. **/
  144. MultiChatManager.prototype.foreachContext = function(cb) {
  145. for (var i =0, nbCtx =this.contexts.length; i < nbCtx; i++)
  146. if (cb(this.contexts[i].getChatContext()) === true)
  147. return;
  148. };
  149. /**
  150. * @param {string} chanId
  151. * @return {ChatSystem|null}
  152. **/
  153. MultiChatManager.prototype.getChannelContext = function(chanId) {
  154. for (var i =0, nbCtx = this.contexts.length; i < nbCtx; i++) {
  155. var ctx = this.contexts[i].getChatContext();
  156. if (ctx.channels[chanId])
  157. return this.contexts[i];
  158. }
  159. return null;
  160. };
  161. /**
  162. * @param {string} userId
  163. * @return {ChatSystem|null}
  164. **/
  165. MultiChatManager.prototype.getUserContext = function(userId) {
  166. for (var i =0, nbCtx = this.contexts.length; i < nbCtx; i++) {
  167. var ctx = this.contexts[i].getChatContext();
  168. if (ctx.users[userId])
  169. return this.contexts[i];
  170. }
  171. return null;
  172. };
  173. /**
  174. * @param {string} chanId
  175. * @return {Room|null}
  176. **/
  177. MultiChatManager.prototype.getChannel = function(chanId) {
  178. for (var i =0, nbCtx = this.contexts.length; i < nbCtx; i++) {
  179. var chan = this.contexts[i].getChatContext().channels[chanId];
  180. if (chan)
  181. return chan;
  182. }
  183. return null;
  184. };
  185. /**
  186. * @param {function(Room, ChatSystem, string):boolean=} filter (room, roomId)
  187. * @return {Array<string>}
  188. **/
  189. MultiChatManager.prototype.getChannelIds = function(filter) {
  190. var ids = [];
  191. for (var i =0, nbCtx = this.contexts.length; i < nbCtx; i++) {
  192. var channels = this.contexts[i].getChatContext().channels;
  193. for (var chanId in channels) {
  194. if (!filter || filter(channels[chanId], this.contexts[i], chanId)) {
  195. ids.push(chanId);
  196. }
  197. }
  198. }
  199. return ids;
  200. };
  201. MultiChatManager.prototype.getChatSystem =function(teamId) {
  202. for (var i =0, nbCtx = this.contexts.length; i < nbCtx; i++) {
  203. var sys = this.contexts[i]
  204. ,ctx = sys.getChatContext();
  205. if (ctx && ctx.team && ctx.team.id === teamId)
  206. return sys;
  207. }
  208. };
  209. /**
  210. * @param {string} userId
  211. * @return {Chatter|null}
  212. **/
  213. MultiChatManager.prototype.getUser = function(userId) {
  214. for (var i =0, nbCtx = this.contexts.length; i < nbCtx; i++) {
  215. var user = this.contexts[i].getChatContext().users[userId];
  216. if (user)
  217. return user;
  218. }
  219. return null;
  220. };
  221. /**
  222. * @param {string} userId
  223. * @return {boolean}
  224. **/
  225. MultiChatManager.prototype.isMe = function(userId) {
  226. for (var i =0, nbCtx = this.contexts.length; i < nbCtx; i++) {
  227. var ctx = this.contexts[i].getChatContext();
  228. if (ctx.self && ctx.self.id === userId)
  229. return true;
  230. }
  231. return false;
  232. };
  233. MultiChatManager.prototype.pollPeriodical = function(knownVersion, withTyping) {
  234. var liveFeed
  235. ,staticFeed
  236. ,allTyping
  237. ,v = 0
  238. ,now = Date.now()
  239. ,updated = false;
  240. this.contexts.forEach(function(ctx) {
  241. var id = ctx.getId();
  242. if (id) {
  243. var res = ctx.poll(knownVersion, now, withTyping === undefined ? true : withTyping);
  244. if (res) {
  245. if (res["static"]) {
  246. if (!staticFeed)
  247. staticFeed = {};
  248. staticFeed[id] = res["static"];
  249. }
  250. if (res["live"])
  251. if (!liveFeed)
  252. liveFeed = {};
  253. for (var i in res["live"]) {
  254. liveFeed[i] = res["live"][i];
  255. }
  256. if (res["typing"]) {
  257. if (!allTyping)
  258. allTyping = {};
  259. for (var i in res["typing"])
  260. allTyping[i] = res["typing"][i];
  261. }
  262. v = Math.max(v, (res["v"] || 0));
  263. updated = true;
  264. }
  265. }
  266. });
  267. if (updated)
  268. return ({
  269. "live": liveFeed,
  270. "static": staticFeed,
  271. "typing": allTyping,
  272. "v": Math.max(v, knownVersion)
  273. });
  274. };
  275. MultiChatManager.polling = [];
  276. /**
  277. * @param {number} knownVersion
  278. * @param {(function(number, function((Array<{expose:Function, modified: number}>|null))))=} checkConfigUpdate
  279. * @param {boolean=} withTyping default true
  280. **/
  281. MultiChatManager.prototype.poll = function(knownVersion, reqT, checkConfigUpdate, withTyping) {
  282. var _this = this;
  283. _this.contexts.forEach(function(ctx) {
  284. ctx.onRequest();
  285. });
  286. var p = new Promise(function(succ, err) {
  287. function launchPolling() {
  288. var currentState = _this.pollPeriodical(knownVersion, withTyping);
  289. if (currentState) {
  290. succ(currentState);
  291. } else {
  292. var timeo = setTimeout(function() {
  293. for (var i =0, nbCtx = MultiChatManager.polling.length; i < nbCtx; i++) {
  294. if (MultiChatManager.polling[i].reqT === reqT &&
  295. MultiChatManager.polling[i].ctx === _this &&
  296. MultiChatManager.polling[i].knownVersion === knownVersion) {
  297. MultiChatManager.polling.splice(i, 1);
  298. break;
  299. }
  300. }
  301. err();
  302. }, 55000);
  303. MultiChatManager.polling.push({ ctx: _this, knownVersion: knownVersion, reqT: reqT, checkConfigUpdate: checkConfigUpdate, withTyping: withTyping, promise: succ, timeo: timeo });
  304. }
  305. };
  306. if (checkConfigUpdate) {
  307. checkConfigUpdate(knownVersion, function(config) {
  308. if (config) {
  309. var exposedConfig = [],
  310. v = 0;
  311. config.forEach(function(conf) {
  312. exposedConfig.push(conf.expose());
  313. v = Math.max(v, conf.modified);
  314. });
  315. var res = _this.pollPeriodical(knownVersion, withTyping);
  316. if (res) {
  317. res["config"] = exposedConfig;
  318. succ(res);
  319. } else {
  320. succ({
  321. "config": exposedConfig,
  322. "v": v
  323. });
  324. }
  325. } else {
  326. launchPolling();
  327. }
  328. });
  329. } else {
  330. launchPolling();
  331. }
  332. });
  333. return p;
  334. };
  335. MultiChatManager.prototype.containsCtx = function(ctx) {
  336. for (var i =0, nbCtx = this.contexts.length; i < nbCtx; i++)
  337. if (ctx === this.contexts[i])
  338. return true;
  339. return false;
  340. };
  341. /** @param {ChatContext} ctx */
  342. MultiChatManager.onCtxUpdated = function(ctx) {
  343. var i =0;
  344. while (MultiChatManager.polling[i]) {
  345. var pollingObj = MultiChatManager.polling[i];
  346. if (pollingObj.ctx.containsCtx(ctx)) {
  347. var res = pollingObj.ctx.pollPeriodical(pollingObj.knownVersion, pollingObj.withTyping);
  348. if (res) {
  349. clearTimeout(pollingObj.timeo);
  350. pollingObj.promise(res);
  351. MultiChatManager.polling.splice(i, 1);
  352. } else {
  353. i++;
  354. }
  355. }
  356. }
  357. };
  358. /** @suppress {undefinedVars,checkTypes} */
  359. (function() {
  360. if (typeof module !== "undefined") {
  361. module.exports.MultiChatManager = MultiChatManager;
  362. }
  363. })();