Completed
Push — master ( e88c19...17669b )
by Rain
03:00
created

dev/View/User/MailBox/MessageList.js   F

Complexity

Total Complexity 215
Complexity/F 1.85

Size

Lines of Code 1063
Function Count 116

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 0
c 2
b 0
f 0
nc 1602224128
dl 0
loc 1063
rs 2.3256
wmc 215
mnd 4
bc 183
fnc 116
bpm 1.5774
cpm 1.8534
noi 0

How to fix   Complexity   

Complexity

Complex classes like dev/View/User/MailBox/MessageList.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
2
import window from 'window';
3
import _ from '_';
4
import $ from '$';
5
import ko from 'ko';
6
import key from 'key';
7
import Jua from 'Jua';
8
import ifvisible from 'ifvisible';
9
10
import {
11
	Capa, Layout, Focused, ComposeType,
12
	FolderType, Magics, MessageSetAction, KeyState,
13
	StorageResultType
14
} from 'Common/Enums';
15
16
import {
17
	UNUSED_OPTION_VALUE
18
} from 'Common/Consts';
19
20
import {
21
	bMobileDevice,
22
	popupVisibility,
23
	leftPanelDisabled
24
} from 'Common/Globals';
25
26
import {
27
	noop,
28
	noopFalse,
29
	createCommand,
30
	computedPagenatorHelper,
31
	draggablePlace,
32
	friendlySize,
33
	inArray, isUnd
34
} from 'Common/Utils';
35
36
import {mailBox, append} from 'Common/Links';
37
import {Selector} from 'Common/Selector';
38
import * as Events from 'Common/Events';
39
40
import {i18n, initOnStartOrLangChange} from 'Common/Translator';
41
42
import {
43
	getFolderFromCacheList, clearMessageFlagsFromCacheByFolder,
44
	hasRequestedMessage, addRequestedMessage
45
} from 'Common/Cache';
46
47
import AppStore from 'Stores/User/App';
48
import QuotaStore from 'Stores/User/Quota';
49
import SettingsStore from 'Stores/User/Settings';
50
import FolderStore from 'Stores/User/Folder';
51
import MessageStore from 'Stores/User/Message';
52
53
import * as Settings from 'Storage/Settings';
54
import Remote from 'Remote/User/Ajax';
55
56
import {getApp} from 'Helper/Apps/User';
57
58
import {view, ViewType, showScreenPopup, setHash} from 'Knoin/Knoin';
59
import {AbstractViewNext} from 'Knoin/AbstractViewNext';
60
61
@view({
62
	name: 'View/User/MailBox/MessageList',
63
	type: ViewType.Right,
64
	templateID: 'MailMessageList'
65
})
66
class MessageListMailBoxUserView extends AbstractViewNext
67
{
68
	constructor() {
69
		super();
70
71
		this.sLastUid = null;
72
		this.bPrefetch = false;
73
		this.emptySubjectValue = '';
74
75
		this.iGoToUpUpOrDownDownTimeout = 0;
76
77
		this.mobile = !!Settings.appSettingsGet('mobile');
78
79
		this.allowReload = !!Settings.capa(Capa.Reload);
80
		this.allowSearch = !!Settings.capa(Capa.Search);
81
		this.allowSearchAdv = !!Settings.capa(Capa.SearchAdv);
82
		this.allowComposer = !!Settings.capa(Capa.Composer);
83
		this.allowMessageListActions = !!Settings.capa(Capa.MessageListActions);
84
		this.allowDangerousActions = !!Settings.capa(Capa.DangerousActions);
85
		this.allowFolders = !!Settings.capa(Capa.Folders);
86
87
		this.popupVisibility = popupVisibility;
88
89
		this.message = MessageStore.message;
90
		this.messageList = MessageStore.messageList;
91
		this.messageListDisableAutoSelect = MessageStore.messageListDisableAutoSelect;
92
93
		this.folderList = FolderStore.folderList;
94
95
		this.composeInEdit = AppStore.composeInEdit;
96
		this.leftPanelDisabled = leftPanelDisabled;
97
98
		this.selectorMessageSelected = MessageStore.selectorMessageSelected;
99
		this.selectorMessageFocused = MessageStore.selectorMessageFocused;
100
		this.isMessageSelected = MessageStore.isMessageSelected;
101
		this.messageListSearch = MessageStore.messageListSearch;
102
		this.messageListThreadUid = MessageStore.messageListThreadUid;
103
		this.messageListError = MessageStore.messageListError;
104
		this.folderMenuForMove = FolderStore.folderMenuForMove;
105
106
		this.useCheckboxesInList = SettingsStore.useCheckboxesInList;
107
108
		this.mainMessageListSearch = MessageStore.mainMessageListSearch;
109
		this.messageListEndFolder = MessageStore.messageListEndFolder;
110
		this.messageListEndThreadUid = MessageStore.messageListEndThreadUid;
111
112
		this.messageListChecked = MessageStore.messageListChecked;
113
		this.messageListCheckedOrSelected = MessageStore.messageListCheckedOrSelected;
114
		this.messageListCheckedOrSelectedUidsWithSubMails = MessageStore.messageListCheckedOrSelectedUidsWithSubMails;
115
		this.messageListCompleteLoadingThrottle = MessageStore.messageListCompleteLoadingThrottle;
116
		this.messageListCompleteLoadingThrottleForAnimation = MessageStore.messageListCompleteLoadingThrottleForAnimation;
117
118
		initOnStartOrLangChange(() => {
119
			this.emptySubjectValue = i18n('MESSAGE_LIST/EMPTY_SUBJECT_TEXT');
120
		});
121
122
		this.userQuota = QuotaStore.quota;
123
		this.userUsageSize = QuotaStore.usage;
124
		this.userUsageProc = QuotaStore.percentage;
125
126
		this.moveDropdownTrigger = ko.observable(false);
127
		this.moreDropdownTrigger = ko.observable(false);
128
129
		// append drag and drop
130
		this.dragOver = ko.observable(false).extend({'throttle': 1});
131
		this.dragOverEnter = ko.observable(false).extend({'throttle': 1});
132
		this.dragOverArea = ko.observable(null);
133
		this.dragOverBodyArea = ko.observable(null);
134
135
		this.messageListItemTemplate = ko.computed(
136
			() => (this.mobile || Layout.SidePreview === SettingsStore.layout() ?
137
				'MailMessageListItem' : 'MailMessageListItemNoPreviewPane')
138
		);
139
140
		this.messageListSearchDesc = ko.computed(() => {
141
			const value = MessageStore.messageListEndSearch();
142
			return '' === value ? '' : i18n('MESSAGE_LIST/SEARCH_RESULT_FOR', {'SEARCH': value});
143
		});
144
145
		this.messageListPagenator = ko.computed(
146
			computedPagenatorHelper(MessageStore.messageListPage, MessageStore.messageListPageCount));
147
148
		this.checkAll = ko.computed({
149
			read: () => 0 < MessageStore.messageListChecked().length,
150
			write: (value) => {
151
				value = !!value;
152
				_.each(MessageStore.messageList(), (message) => {
153
					message.checked(value);
154
				});
155
			}
156
		});
157
158
		this.inputMessageListSearchFocus = ko.observable(false);
159
160
		this.sLastSearchValue = '';
161
		this.inputProxyMessageListSearch = ko.computed({
162
			read: this.mainMessageListSearch,
163
			write: (value) => {
164
				this.sLastSearchValue = value;
165
			}
166
		});
167
168
		this.isIncompleteChecked = ko.computed(() => {
169
			const
170
				m = MessageStore.messageList().length,
171
				c = MessageStore.messageListChecked().length;
172
			return 0 < m && 0 < c && m > c;
173
		});
174
175
		this.hasMessages = ko.computed(() => 0 < this.messageList().length);
176
177
		this.hasCheckedOrSelectedLines = ko.computed(() => 0 < this.messageListCheckedOrSelected().length);
178
179
		this.isSpamFolder = ko.computed(
180
			() => FolderStore.spamFolder() === this.messageListEndFolder() && '' !== FolderStore.spamFolder()
181
		);
182
183
		this.isSpamDisabled = ko.computed(() => UNUSED_OPTION_VALUE === FolderStore.spamFolder());
184
185
		this.isTrashFolder = ko.computed(
186
			() => FolderStore.trashFolder() === this.messageListEndFolder() && '' !== FolderStore.trashFolder()
187
		);
188
189
		this.isDraftFolder = ko.computed(
190
			() => FolderStore.draftFolder() === this.messageListEndFolder() && '' !== FolderStore.draftFolder()
191
		);
192
193
		this.isSentFolder = ko.computed(
194
			() => FolderStore.sentFolder() === this.messageListEndFolder() && '' !== FolderStore.sentFolder()
195
		);
196
197
		this.isArchiveFolder = ko.computed(
198
			() => FolderStore.archiveFolder() === this.messageListEndFolder() && '' !== FolderStore.archiveFolder()
199
		);
200
201
		this.isArchiveDisabled = ko.computed(() => UNUSED_OPTION_VALUE === FolderStore.archiveFolder());
202
203
		this.isArchiveVisible = ko.computed(
204
			() => !this.isArchiveFolder() && !this.isArchiveDisabled() && !this.isDraftFolder()
205
		);
206
207
		this.isSpamVisible = ko.computed(
208
			() => !this.isSpamFolder() && !this.isSpamDisabled() && !this.isDraftFolder() && !this.isSentFolder()
209
		);
210
211
		this.isUnSpamVisible = ko.computed(
212
			() => this.isSpamFolder() && !this.isSpamDisabled() && !this.isDraftFolder() && !this.isSentFolder()
213
		);
214
215
		this.mobileCheckedStateShow = ko.computed(() => {
216
			const checked = 0 < this.messageListChecked().length;
217
			return this.mobile ? checked : true;
218
		});
219
220
		this.mobileCheckedStateHide = ko.computed(() => {
221
			const checked = 0 < this.messageListChecked().length;
222
			return this.mobile ? !checked : true;
223
		});
224
225
		this.messageListFocused = ko.computed(() => Focused.MessageList === AppStore.focusedState());
226
227
		this.canBeMoved = this.hasCheckedOrSelectedLines;
228
229
		this.clearCommand = createCommand(() => {
230
			if (Settings.capa(Capa.DangerousActions))
231
			{
232
				showScreenPopup(require('View/Popup/FolderClear'), [FolderStore.currentFolder()]);
233
			}
234
		});
235
236
		this.multyForwardCommand = createCommand(() => {
237
			if (Settings.capa(Capa.Composer))
238
			{
239
				showScreenPopup(require('View/Popup/Compose'), [
240
					ComposeType.ForwardAsAttachment, MessageStore.messageListCheckedOrSelected()]);
241
			}
242
		}, this.canBeMoved);
243
244
		this.deleteWithoutMoveCommand = createCommand(() => {
245
			if (Settings.capa(Capa.DangerousActions))
246
			{
247
				getApp().deleteMessagesFromFolder(FolderType.Trash,
248
					FolderStore.currentFolderFullNameRaw(),
249
					MessageStore.messageListCheckedOrSelectedUidsWithSubMails(), false);
250
			}
251
		}, this.canBeMoved);
252
253
		this.deleteCommand = createCommand(() => {
254
			getApp().deleteMessagesFromFolder(FolderType.Trash,
255
				FolderStore.currentFolderFullNameRaw(),
256
				MessageStore.messageListCheckedOrSelectedUidsWithSubMails(), true);
257
		}, this.canBeMoved);
258
259
		this.archiveCommand = createCommand(() => {
260
			getApp().deleteMessagesFromFolder(FolderType.Archive,
261
				FolderStore.currentFolderFullNameRaw(),
262
				MessageStore.messageListCheckedOrSelectedUidsWithSubMails(), true);
263
		}, this.canBeMoved);
264
265
		this.spamCommand = createCommand(() => {
266
			getApp().deleteMessagesFromFolder(FolderType.Spam,
267
				FolderStore.currentFolderFullNameRaw(),
268
				MessageStore.messageListCheckedOrSelectedUidsWithSubMails(), true);
269
		}, this.canBeMoved);
270
271
		this.notSpamCommand = createCommand(() => {
272
			getApp().deleteMessagesFromFolder(FolderType.NotSpam,
273
				FolderStore.currentFolderFullNameRaw(),
274
				MessageStore.messageListCheckedOrSelectedUidsWithSubMails(), true);
275
		}, this.canBeMoved);
276
277
		this.moveCommand = createCommand(noop, this.canBeMoved);
278
279
		this.reloadCommand = createCommand(() => {
280
			if (!MessageStore.messageListCompleteLoadingThrottleForAnimation() && this.allowReload)
281
			{
282
				getApp().reloadMessageList(false, true);
283
			}
284
		});
285
286
		this.quotaTooltip = _.bind(this.quotaTooltip, this);
287
288
		this.selector = new Selector(
289
			this.messageList, this.selectorMessageSelected, this.selectorMessageFocused,
290
			'.messageListItem .actionHandle', '.messageListItem.selected',
291
			'.messageListItem .checkboxMessage', '.messageListItem.focused');
292
293
		this.selector.on('onItemSelect', (message) => {
294
			MessageStore.selectMessage(message);
295
		});
296
297
		this.selector.on('onItemGetUid', (message) => (message ? message.generateUid() : ''));
298
299
		this.selector.on('onAutoSelect', () => this.useAutoSelect());
300
301
		this.selector.on('onUpUpOrDownDown', (v) => {
302
			this.goToUpUpOrDownDown(v);
303
		});
304
305
		Events.sub('mailbox.message-list.selector.go-down', (select) => {
306
			this.selector.goDown(select);
307
		});
308
309
		Events.sub('mailbox.message-list.selector.go-up', (select) => {
310
			this.selector.goUp(select);
311
		});
312
313
		Events.sub('mailbox.message.show', (sFolder, sUid) => {
314
315
			const message = _.find(this.messageList(), (item) => item && sFolder === item.folderFullNameRaw && sUid === item.uid);
316
317
			if ('INBOX' === sFolder)
318
			{
319
				setHash(mailBox(sFolder, 1));
320
			}
321
322
			if (message)
323
			{
324
				this.selector.selectMessageItem(message);
325
			}
326
			else
327
			{
328
				if ('INBOX' !== sFolder)
329
				{
330
					setHash(mailBox(sFolder, 1));
331
				}
332
333
				MessageStore.selectMessageByFolderAndUid(sFolder, sUid);
334
			}
335
336
		});
337
338
		MessageStore.messageListEndHash.subscribe(() => {
339
			this.selector.scrollToTop();
340
		});
341
	}
342
343
	hideLeft(oItem, oEvent) {
344
		oEvent.preventDefault();
345
		oEvent.stopPropagation();
346
347
		leftPanelDisabled(true);
348
	}
349
350
	showLeft(oItem, oEvent) {
351
		oEvent.preventDefault();
352
		oEvent.stopPropagation();
353
354
		leftPanelDisabled(false);
355
	}
356
357
	composeClick() {
358
		if (Settings.capa(Capa.Composer))
359
		{
360
			showScreenPopup(require('View/Popup/Compose'));
361
		}
362
	}
363
364
	goToUpUpOrDownDown(up) {
365
366
		if (0 < this.messageListChecked().length)
367
		{
368
			return false;
369
		}
370
371
		window.clearTimeout(this.iGoToUpUpOrDownDownTimeout);
372
		this.iGoToUpUpOrDownDownTimeout = window.setTimeout(() => {
373
374
			let
375
				prev = null,
376
				next = null,
377
				temp = null,
378
				current = null;
379
380
			_.find(this.messageListPagenator(), (item) => {
381
382
				if (item)
383
				{
384
					if (current)
385
					{
386
						next = item;
387
					}
388
389
					if (item.current)
390
					{
391
						current = item;
392
						prev = temp;
393
					}
394
395
					if (next)
396
					{
397
						return true;
398
					}
399
400
					temp = item;
401
				}
402
403
				return false;
404
			});
405
406
			if (Layout.NoPreview === SettingsStore.layout() && !this.message())
407
			{
408
				this.selector.iFocusedNextHelper = up ? -1 : 1;
409
			}
410
			else
411
			{
412
				this.selector.iSelectNextHelper = up ? -1 : 1;
413
			}
414
415
			if (up ? prev : next)
416
			{
417
				this.selector.unselect();
418
				this.gotoPage(up ? prev : next);
419
			}
420
421
		}, Magics.Time350ms);
422
423
		return true;
424
	}
425
426
	useAutoSelect() {
427
		if (this.messageListDisableAutoSelect())
428
		{
429
			return false;
430
		}
431
432
		if (/is:unseen/.test(this.mainMessageListSearch()))
433
		{
434
			return false;
435
		}
436
437
		return Layout.NoPreview !== SettingsStore.layout();
438
	}
439
440
	searchEnterAction() {
441
		this.mainMessageListSearch(this.sLastSearchValue);
442
		this.inputMessageListSearchFocus(false);
443
	}
444
445
	/**
446
	 * @returns {string}
447
	 */
448
	printableMessageCountForDeletion() {
449
		const cnt = this.messageListCheckedOrSelectedUidsWithSubMails().length;
450
		return 1 < cnt ? ' (' + (100 > cnt ? cnt : '99+') + ')' : ''; // eslint-disable-line no-magic-numbers
451
	}
452
453
	cancelSearch() {
454
		this.mainMessageListSearch('');
455
		this.inputMessageListSearchFocus(false);
456
	}
457
458
	cancelThreadUid() {
459
		setHash(mailBox(
460
			FolderStore.currentFolderFullNameHash(),
461
			MessageStore.messageListPageBeforeThread(),
462
			MessageStore.messageListSearch()
463
		));
464
	}
465
466
	/**
467
	 * @param {string} sToFolderFullNameRaw
468
	 * @param {boolean} bCopy
469
	 * @returns {boolean}
470
	 */
471
	moveSelectedMessagesToFolder(sToFolderFullNameRaw, bCopy) {
472
		if (this.canBeMoved())
473
		{
474
			getApp().moveMessagesToFolder(
475
				FolderStore.currentFolderFullNameRaw(),
476
				MessageStore.messageListCheckedOrSelectedUidsWithSubMails(), sToFolderFullNameRaw, bCopy);
477
		}
478
479
		return false;
480
	}
481
482
	dragAndDronHelper(oMessageListItem) {
483
		if (oMessageListItem)
484
		{
485
			oMessageListItem.checked(true);
486
		}
487
488
		const
489
			el = draggablePlace(),
490
			updateUidsInfo = () => {
491
				const uids = MessageStore.messageListCheckedOrSelectedUidsWithSubMails();
492
				el.data('rl-uids', uids);
493
				el.find('.text').text('' + uids.length);
494
			};
495
496
		el.data('rl-folder', FolderStore.currentFolderFullNameRaw());
497
498
		updateUidsInfo();
499
		_.defer(updateUidsInfo);
500
501
		return el;
502
	}
503
504
	/**
505
	 * @param {string} sFolderFullNameRaw
506
	 * @param {string|bool} mUid
507
	 * @param {number} iSetAction
508
	 * @param {Array=} aMessages = null
509
	 * @returns {void}
510
	 */
511
	setAction(sFolderFullNameRaw, mUid, iSetAction, aMessages) {
512
		getApp().messageListAction(sFolderFullNameRaw, mUid, iSetAction, aMessages);
513
	}
514
515
	/**
516
	 * @param {string} sFolderFullNameRaw
517
	 * @param {number} iSetAction
518
	 * @returns {void}
519
	 */
520
	setActionForAll(sFolderFullNameRaw, iSetAction) {
521
		if ('' !== sFolderFullNameRaw)
522
		{
523
			let folder = getFolderFromCacheList(sFolderFullNameRaw);
524
			if (folder)
525
			{
526
				switch (iSetAction)
527
				{
528
					case MessageSetAction.SetSeen:
529
						folder = getFolderFromCacheList(sFolderFullNameRaw);
530
						if (folder)
531
						{
532
							_.each(MessageStore.messageList(), (message) => {
533
								message.unseen(false);
534
							});
535
536
							folder.messageCountUnread(0);
537
							clearMessageFlagsFromCacheByFolder(sFolderFullNameRaw);
538
						}
539
540
						Remote.messageSetSeenToAll(noop, sFolderFullNameRaw, true);
541
						break;
542
					case MessageSetAction.UnsetSeen:
543
						folder = getFolderFromCacheList(sFolderFullNameRaw);
544
						if (folder)
545
						{
546
							_.each(MessageStore.messageList(), (message) => {
547
								message.unseen(true);
548
							});
549
550
							folder.messageCountUnread(folder.messageCountAll());
551
							clearMessageFlagsFromCacheByFolder(sFolderFullNameRaw);
552
						}
553
						Remote.messageSetSeenToAll(noop, sFolderFullNameRaw, false);
554
						break;
555
					// no default
556
				}
557
558
				getApp().reloadFlagsCurrentMessageListAndMessageFromCache();
559
			}
560
		}
561
	}
562
563
	listSetSeen() {
564
		this.setAction(FolderStore.currentFolderFullNameRaw(), true,
565
			MessageSetAction.SetSeen, MessageStore.messageListCheckedOrSelected());
566
	}
567
568
	listSetAllSeen() {
569
		this.setActionForAll(FolderStore.currentFolderFullNameRaw(), MessageSetAction.SetSeen);
570
	}
571
572
	listUnsetSeen() {
573
		this.setAction(FolderStore.currentFolderFullNameRaw(), true,
574
			MessageSetAction.UnsetSeen, MessageStore.messageListCheckedOrSelected());
575
	}
576
577
	listSetFlags() {
578
		this.setAction(FolderStore.currentFolderFullNameRaw(), true,
579
			MessageSetAction.SetFlag, MessageStore.messageListCheckedOrSelected());
580
	}
581
582
	listUnsetFlags() {
583
		this.setAction(FolderStore.currentFolderFullNameRaw(), true,
584
			MessageSetAction.UnsetFlag, MessageStore.messageListCheckedOrSelected());
585
	}
586
587
	flagMessages(currentMessage) {
588
		const checked = this.messageListCheckedOrSelected();
589
		if (currentMessage)
590
		{
591
			const checkedUids = _.map(checked, (message) => message.uid);
592
			if (0 < checkedUids.length && -1 < inArray(currentMessage.uid, checkedUids))
593
			{
594
				this.setAction(currentMessage.folderFullNameRaw, true, currentMessage.flagged() ?
595
					MessageSetAction.UnsetFlag : MessageSetAction.SetFlag, checked);
596
			}
597
			else
598
			{
599
				this.setAction(currentMessage.folderFullNameRaw, true, currentMessage.flagged() ?
600
					MessageSetAction.UnsetFlag : MessageSetAction.SetFlag, [currentMessage]);
601
			}
602
		}
603
	}
604
605
	flagMessagesFast(bFlag) {
606
		const checked = this.messageListCheckedOrSelected();
607
		if (0 < checked.length)
608
		{
609
			if (isUnd(bFlag))
610
			{
611
				const flagged = _.filter(checked, (message) => message.flagged());
612
				this.setAction(checked[0].folderFullNameRaw, true,
613
					checked.length === flagged.length ? MessageSetAction.UnsetFlag : MessageSetAction.SetFlag, checked);
614
			}
615
			else
616
			{
617
				this.setAction(checked[0].folderFullNameRaw, true,
618
					!bFlag ? MessageSetAction.UnsetFlag : MessageSetAction.SetFlag, checked);
619
			}
620
		}
621
	}
622
623
	seenMessagesFast(seen) {
624
		const checked = this.messageListCheckedOrSelected();
625
		if (0 < checked.length)
626
		{
627
			if (isUnd(seen))
628
			{
629
				const unseen = _.filter(checked, (message) => message.unseen());
630
				this.setAction(checked[0].folderFullNameRaw, true,
631
					0 < unseen.length ? MessageSetAction.SetSeen : MessageSetAction.UnsetSeen, checked);
632
			}
633
			else
634
			{
635
				this.setAction(checked[0].folderFullNameRaw, true,
636
					seen ? MessageSetAction.SetSeen : MessageSetAction.UnsetSeen, checked);
637
			}
638
		}
639
	}
640
641
	gotoPage(page) {
642
		if (page)
643
		{
644
			setHash(mailBox(
645
				FolderStore.currentFolderFullNameHash(),
646
				page.value,
647
				MessageStore.messageListSearch(),
648
				MessageStore.messageListThreadUid()
649
			));
650
		}
651
	}
652
653
	gotoThread(message) {
654
		if (message && 0 < message.threadsLen())
655
		{
656
			MessageStore.messageListPageBeforeThread(MessageStore.messageListPage());
657
658
			setHash(mailBox(
659
				FolderStore.currentFolderFullNameHash(),
660
				1,
661
				MessageStore.messageListSearch(),
662
				message.uid
663
			));
664
		}
665
	}
666
667
	clearListIsVisible() {
668
		return '' === this.messageListSearchDesc() && '' === this.messageListError() &&
669
			'' === MessageStore.messageListEndThreadUid() &&
670
			0 < this.messageList().length && (this.isSpamFolder() || this.isTrashFolder());
671
	}
672
673
	onBuild(dom) {
674
675
		const self = this;
676
677
		this.oContentVisible = $('.b-content', dom);
678
		this.oContentScrollable = $('.content', this.oContentVisible);
679
680
		this.selector.init(this.oContentVisible, this.oContentScrollable, KeyState.MessageList);
681
682
		if (this.mobile)
683
		{
684
			dom
685
				.on('click', () => {
686
					leftPanelDisabled(true);
687
				});
688
		}
689
690
		dom
691
			.on('click', '.messageList .b-message-list-wrapper', () => {
692
				if (Focused.MessageView === AppStore.focusedState())
693
				{
694
					AppStore.focusedState(Focused.MessageList);
695
				}
696
			})
697
			.on('click', '.e-pagenator .e-page', function() { // eslint-disable-line prefer-arrow-callback
698
				self.gotoPage(ko.dataFor(this)); // eslint-disable-line no-invalid-this
699
			})
700
			.on('click', '.messageList .checkboxCkeckAll', () => {
701
				this.checkAll(!this.checkAll());
702
			})
703
			.on('click', '.messageList .messageListItem .flagParent', function() { // eslint-disable-line prefer-arrow-callback
704
				self.flagMessages(ko.dataFor(this)); // eslint-disable-line no-invalid-this
705
			})
706
			.on('click', '.messageList .messageListItem .threads-len', function() { // eslint-disable-line prefer-arrow-callback
707
				self.gotoThread(ko.dataFor(this)); // eslint-disable-line no-invalid-this
708
			})
709
			.on('dblclick', '.messageList .messageListItem .actionHandle', function() { // eslint-disable-line prefer-arrow-callback
710
				self.gotoThread(ko.dataFor(this)); // eslint-disable-line no-invalid-this
711
			});
712
713
		this.initUploaderForAppend();
714
		this.initShortcuts();
715
716
		if (!bMobileDevice && ifvisible && Settings.capa(Capa.Prefetch))
717
		{
718
			ifvisible.setIdleDuration(Magics.ifvisibleIdle10s);
719
720
			ifvisible.idle(() => {
721
				this.prefetchNextTick();
722
			});
723
		}
724
	}
725
726
	initShortcuts() {
727
728
		key('enter', KeyState.MessageList, () => {
729
			if (this.message() && this.useAutoSelect())
730
			{
731
				Events.pub('mailbox.message-view.toggle-full-screen');
732
				return false;
733
			}
734
735
			return true;
736
		});
737
738
		if (Settings.capa(Capa.MessageListActions))
739
		{
740
			// archive (zip)
741
			key('z', [KeyState.MessageList, KeyState.MessageView], () => {
742
				this.archiveCommand();
743
				return false;
744
			});
745
746
			// delete
747
			key('delete, shift+delete, shift+3', KeyState.MessageList, (event, handler) => {
748
				if (event)
749
				{
750
					if (0 < MessageStore.messageListCheckedOrSelected().length)
751
					{
752
						if (handler && 'shift+delete' === handler.shortcut)
753
						{
754
							this.deleteWithoutMoveCommand();
755
						}
756
						else
757
						{
758
							this.deleteCommand();
759
						}
760
					}
761
762
					return false;
763
				}
764
765
				return true;
766
			});
767
		}
768
769
		if (Settings.capa(Capa.Reload))
770
		{
771
			// check mail
772
			key('ctrl+r, command+r', [KeyState.FolderList, KeyState.MessageList, KeyState.MessageView], () => {
773
				this.reloadCommand();
774
				return false;
775
			});
776
		}
777
778
		// check all
779
		key('ctrl+a, command+a', KeyState.MessageList, () => {
780
			this.checkAll(!(this.checkAll() && !this.isIncompleteChecked()));
781
			return false;
782
		});
783
784
		if (Settings.capa(Capa.Composer))
785
		{
786
			// write/compose (open compose popup)
787
			key('w,c', [KeyState.MessageList, KeyState.MessageView], () => {
788
				showScreenPopup(require('View/Popup/Compose'));
789
				return false;
790
			});
791
		}
792
793
		if (Settings.capa(Capa.MessageListActions))
794
		{
795
			// important - star/flag messages
796
			key('i', [KeyState.MessageList, KeyState.MessageView], () => {
797
				this.flagMessagesFast();
798
				return false;
799
			});
800
		}
801
802
		key('t', [KeyState.MessageList], () => {
803
804
			let message = this.selectorMessageSelected();
805
			if (!message)
806
			{
807
				message = this.selectorMessageFocused();
808
			}
809
810
			if (message && 0 < message.threadsLen())
811
			{
812
				this.gotoThread(message);
813
			}
814
815
			return false;
816
		});
817
818
		if (Settings.capa(Capa.MessageListActions))
819
		{
820
			// move
821
			key('m', KeyState.MessageList, () => {
822
				this.moveDropdownTrigger(true);
823
				return false;
824
			});
825
		}
826
827
		if (Settings.capa(Capa.MessageListActions))
828
		{
829
			// read
830
			key('q', [KeyState.MessageList, KeyState.MessageView], () => {
831
				this.seenMessagesFast(true);
832
				return false;
833
			});
834
835
			// unread
836
			key('u', [KeyState.MessageList, KeyState.MessageView], () => {
837
				this.seenMessagesFast(false);
838
				return false;
839
			});
840
		}
841
842
		if (Settings.capa(Capa.Composer))
843
		{
844
			key('shift+f', [KeyState.MessageList, KeyState.MessageView], () => {
845
				this.multyForwardCommand();
846
				return false;
847
			});
848
		}
849
850
		if (Settings.capa(Capa.Search))
851
		{
852
			// search input focus
853
			key('/', [KeyState.MessageList, KeyState.MessageView], () => {
854
				this.inputMessageListSearchFocus(true);
855
				return false;
856
			});
857
		}
858
859
		// cancel search
860
		key('esc', KeyState.MessageList, () => {
861
			if ('' !== this.messageListSearchDesc())
862
			{
863
				this.cancelSearch();
864
				return false;
865
			}
866
			else if ('' !== this.messageListEndThreadUid())
867
			{
868
				this.cancelThreadUid();
869
				return false;
870
			}
871
872
			return true;
873
		});
874
875
		// change focused state
876
		key('tab, shift+tab, left, right', KeyState.MessageList, (event, handler) => {
877
			if (event && handler && ('shift+tab' === handler.shortcut || 'left' === handler.shortcut))
878
			{
879
				AppStore.focusedState(Focused.FolderList);
880
			}
881
			else if (this.message())
882
			{
883
				AppStore.focusedState(Focused.MessageView);
884
			}
885
886
			return false;
887
		});
888
889
		key('ctrl+left, command+left', KeyState.MessageView, noopFalse);
890
		key('ctrl+right, command+right', KeyState.MessageView, noopFalse);
891
	}
892
893
	prefetchNextTick() {
894
		if (ifvisible && !this.bPrefetch && !ifvisible.now() && this.viewModelVisibility())
895
		{
896
			const message = _.find(this.messageList(), (item) => item && !hasRequestedMessage(item.folderFullNameRaw, item.uid));
897
			if (message)
898
			{
899
				this.bPrefetch = true;
900
901
				addRequestedMessage(message.folderFullNameRaw, message.uid);
902
903
				Remote.message((result, data) => {
904
					const next = !!(StorageResultType.Success === result && data && data.Result);
905
					_.delay(() => {
906
						this.bPrefetch = false;
907
						if (next)
908
						{
909
							this.prefetchNextTick();
910
						}
911
					}, Magics.Time1s);
912
913
				}, message.folderFullNameRaw, message.uid);
914
			}
915
		}
916
	}
917
918
	advancedSearchClick() {
919
		if (Settings.capa(Capa.SearchAdv))
920
		{
921
			showScreenPopup(require('View/Popup/AdvancedSearch'), [this.mainMessageListSearch()]);
922
		}
923
	}
924
925
	quotaTooltip() {
926
		return i18n('MESSAGE_LIST/QUOTA_SIZE', {
927
			'SIZE': friendlySize(this.userUsageSize()),
928
			'PROC': this.userUsageProc(),
929
			'LIMIT': friendlySize(this.userQuota())
930
		});
931
	}
932
933
	initUploaderForAppend() {
934
		if (!Settings.appSettingsGet('allowAppendMessage') || !this.dragOverArea())
935
		{
936
			return false;
937
		}
938
939
		const
940
			oJua = new Jua({
941
				action: append(),
942
				name: 'AppendFile',
943
				queueSize: 1,
944
				multipleSizeLimit: 1,
945
				hidden: {
946
					Folder: () => FolderStore.currentFolderFullNameRaw()
947
				},
948
				dragAndDropElement: this.dragOverArea(),
949
				dragAndDropBodyElement: this.dragOverBodyArea()
950
			});
951
952
		this.dragOver.subscribe((value) => {
953
			if (value)
954
			{
955
				this.selector.scrollToTop();
956
			}
957
		});
958
959
		oJua
960
			.on('onDragEnter', () => {
961
				this.dragOverEnter(true);
962
			})
963
			.on('onDragLeave', () => {
964
				this.dragOverEnter(false);
965
			})
966
			.on('onBodyDragEnter', () => {
967
				this.dragOver(true);
968
			})
969
			.on('onBodyDragLeave', () => {
970
				this.dragOver(false);
971
			})
972
			.on('onSelect', (sUid, oData) => {
973
974
				if (sUid && oData && 'message/rfc822' === oData.Type)
975
				{
976
					MessageStore.messageListLoading(true);
977
					return true;
978
				}
979
980
				return false;
981
982
			})
983
			.on('onComplete', () => {
984
				getApp().reloadMessageList(true, true);
985
			});
986
987
		return !!oJua;
988
	}
989
}
990
991
export {MessageListMailBoxUserView, MessageListMailBoxUserView as default};
992