text: import { Mongo } from 'meteor/mongo';
  import { Tracker } from 'meteor/tracker';
  import { Template } from 'meteor/templating';
  import { ReactiveDict } from 'meteor/reactive-dict';
  import _ from 'underscore';
  
  import { call } from '../../../ui-utils';
  import { Messages, Subscriptions } from '../../../models';
  import { messageContext } from '../../../ui-utils/client/lib/messageContext';
  import { messageArgs } from '../../../ui-utils/client/lib/messageArgs';
  import { getConfig } from '../../../ui-utils/client/config';
  import { upsertMessageBulk } from '../../../ui-utils/client/lib/RoomHistoryManager';
  
  import './threads.html';
  
  const LIST_SIZE = parseInt(getConfig('threadsListSize')) || 50;
  
  const sort = { tlm: -1 };
  
  Template.threads.events({
  	'click .js-open-thread'(e, instance) {
  		const { msg, jump } = messageArgs(this);
  		instance.state.set('mid', msg._id);
  		instance.state.set('jump', jump);
  		e.preventDefault();
  		e.stopPropagation();
  		return false;
  	},
  	'scroll .js-scroll-threads': _.throttle(({ currentTarget: e }, { incLimit }) => {
  		if (e.offsetHeight + e.scrollTop >= e.scrollHeight - 50) {
  			incLimit && incLimit();
  		}
  	}, 500),
  });
  
  Template.threads.helpers({
  	jump() {
  		return Template.instance().state.get('jump');
  	},
  	subscription() {
  		return Template.currentData().subscription;
  	},
  	doDotLoadThreads() {
  		return Template.instance().state.get('close');
  	},
  	close() {
  		const { state, data } = Template.instance();
  		const { tabBar } = data;
  		return () => (state.get('close') ? tabBar.close() : state.set('mid', null));
  	},
  	msg() {
  		return Template.instance().state.get('thread');
  	},
  	isLoading() {
  		return Template.instance().state.get('loading');
  	},
  	hasNoThreads() {
  		return !Template.instance().state.get('loading') && Template.instance().Threads.find({ rid: Template.instance().state.get('rid') }, { sort }).count() === 0;
  	},
  	threads() {
  		return Template.instance().Threads.find({ rid: Template.instance().state.get('rid') }, { sort, limit: Template.instance().state.get('limit') });
  	},
  	messageContext,
  });
  
  Template.threads.onCreated(async function() {
  	this.Threads = new Mongo.Collection(null);
  	const { rid, mid, msg } = this.data;
  	this.state = new ReactiveDict({
  		rid,
  		close: !!mid,
  		loading: true,
  		mid,
  		thread: msg,
  	});
  
  	this.rid = rid;
  
  	this.incLimit = () => {
  		const { rid, limit } = Tracker.nonreactive(() => this.state.all());
  
  		const count = this.Threads.find({ rid }).count();
  
  		if (limit > count) {
  			return;
  		}
  
  		this.state.set('limit', this.state.get('limit') + LIST_SIZE);
  		this.loadMore();
  	};
  
  	this.loadMore = _.debounce(async () => {
  		const { rid, limit } = Tracker.nonreactive(() => this.state.all());
  		if (this.state.get('loading') === rid) {
  			return;
  		}
  
  
  		this.state.set('loading', rid);
  		const messages = await call('getThreadsList', { rid, limit: LIST_SIZE, skip: limit - LIST_SIZE });
  		upsertMessageBulk({ msgs: messages }, this.Threads);
  		// threads.forEach(({ _id, ...msg }) => this.Threads.upsert({ _id }, msg));
  		this.state.set('loading', false);
  	}, 500);
  
  	Tracker.afterFlush(() => {
  		this.autorun(async () => {
  			const { rid, mid, jump } = Template.currentData();
  
  			this.state.set({
  				close: !!mid,
  				mid,
  				rid,
  				jump,
  			});
  		});
  	});
  
  	this.autorun(() => {
  		if (mid) {
  			return;
  		}
  		const rid = this.state.get('rid');
  		this.rid = rid;
  		this.state.set({
  			limit: LIST_SIZE,
  		});
  		this.loadMore();
  	});
  
  	this.autorun(() => {
  		const rid = this.state.get('rid');
  		this.threadsObserve && this.threadsObserve.stop();
  		this.threadsObserve = Messages.find({ rid, tcount: { $exists: true }, _hidden: { $ne: true } }).observe({
  			added: ({ _id, ...message }) => {
  				this.Threads.upsert({ _id }, message);
  			}, // Update message to re-render DOM
  			changed: ({ _id, ...message }) => {
  				this.Threads.update({ _id }, message);
  			}, // Update message to re-render DOM
  			removed: ({ _id }) => {
  				this.Threads.remove(_id);
  
  				const mid = this.state.get('mid');
  				if (_id === mid) {
  					this.state.set('mid', null);
  				}
  			},
  		});
  
  		const alert = 'Unread';
  		this.subscriptionObserve && this.subscriptionObserve.stop();
  		this.subscriptionObserve = Subscriptions.find({ rid }, { fields: { tunread: 1 } }).observeChanges({
  			added: (_id, { tunread }) => {
  				tunread && tunread.length && this.Threads.update({ tmid: { $in: tunread } }, { $set: { alert } }, { multi: true });
  			},
  			changed: (id, { tunread = [] }) => {
  				this.Threads.update({ alert, _id: { $nin: tunread } }, { $unset: { alert: 1 } }, { multi: true });
  				tunread && tunread.length && this.Threads.update({ _id: { $in: tunread } }, { $set: { alert } }, { multi: true });
  			},
  		});
  	});
  
  	this.autorun(async () => {
  		const mid = this.state.get('mid');
  		return this.state.set('thread', mid && (Messages.findOne({ _id: mid }, { fields: { tcount: 0, tlm: 0, replies: 0, _updatedAt: 0 } }) || this.Threads.findOne({ _id: mid }, { fields: { tcount: 0, tlm: 0, replies: 0, _updatedAt: 0 } })));
  	});
  });
  
  Template.threads.onDestroyed(function() {
  	const { Threads, threadsObserve, subscriptionObserve } = this;
  	Threads.remove({});
  	threadsObserve && threadsObserve.stop();
  	subscriptionObserve && subscriptionObserve.stop();
  });
  