mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-08-30 12:39:20 +03:00
Restructure JavaScript
Split list code from MessageUserStore into MessagelistUserStore Move functions out of AppUser
This commit is contained in:
parent
60b7f61e42
commit
661cd2aaf9
26 changed files with 1144 additions and 1186 deletions
221
dev/Common/Folders.js
Normal file
221
dev/Common/Folders.js
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
import { isArray, arrayLength } from 'Common/Utils';
|
||||
import {
|
||||
MessageFlagsCache,
|
||||
setFolderHash,
|
||||
getFolderHash,
|
||||
getFolderInboxName,
|
||||
getFolderFromCacheList
|
||||
} from 'Common/Cache';
|
||||
import { SettingsUserStore } from 'Stores/User/Settings';
|
||||
import { FolderUserStore } from 'Stores/User/Folder';
|
||||
import { MessagelistUserStore } from 'Stores/User/Messagelist';
|
||||
import { getNotification } from 'Common/Translator';
|
||||
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
|
||||
export const
|
||||
|
||||
sortFolders = folders => {
|
||||
try {
|
||||
let collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
|
||||
folders.sort((a, b) =>
|
||||
a.isInbox() ? -1 : (b.isInbox() ? 1 : collator.compare(a.fullName, b.fullName))
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Array=} aDisabled
|
||||
* @param {Array=} aHeaderLines
|
||||
* @param {Function=} fDisableCallback
|
||||
* @param {Function=} fRenameCallback
|
||||
* @param {boolean=} bNoSelectSelectable Used in FolderCreatePopupView
|
||||
* @returns {Array}
|
||||
*/
|
||||
folderListOptionsBuilder = (
|
||||
aDisabled,
|
||||
aHeaderLines,
|
||||
fRenameCallback,
|
||||
fDisableCallback,
|
||||
bNoSelectSelectable,
|
||||
aList = FolderUserStore.folderList()
|
||||
) => {
|
||||
const
|
||||
aResult = [],
|
||||
sDeepPrefix = '\u00A0\u00A0\u00A0',
|
||||
// FolderSystemPopupView should always be true
|
||||
showUnsubscribed = fRenameCallback ? !SettingsUserStore.hideUnsubscribed() : true,
|
||||
|
||||
foldersWalk = folders => {
|
||||
folders.forEach(oItem => {
|
||||
if (showUnsubscribed || oItem.hasSubscriptions() || !oItem.exists) {
|
||||
aResult.push({
|
||||
id: oItem.fullName,
|
||||
name:
|
||||
sDeepPrefix.repeat(oItem.deep) +
|
||||
fRenameCallback(oItem),
|
||||
system: false,
|
||||
disabled: !bNoSelectSelectable && (
|
||||
!oItem.selectable() ||
|
||||
aDisabled.includes(oItem.fullName) ||
|
||||
fDisableCallback(oItem))
|
||||
});
|
||||
}
|
||||
|
||||
if (oItem.subFolders.length) {
|
||||
foldersWalk(oItem.subFolders());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
fDisableCallback = fDisableCallback || (() => false);
|
||||
fRenameCallback = fRenameCallback || (oItem => oItem.name());
|
||||
isArray(aDisabled) || (aDisabled = []);
|
||||
|
||||
isArray(aHeaderLines) && aHeaderLines.forEach(line =>
|
||||
aResult.push({
|
||||
id: line[0],
|
||||
name: line[1],
|
||||
system: false,
|
||||
disabled: false
|
||||
})
|
||||
);
|
||||
|
||||
foldersWalk(aList);
|
||||
|
||||
return aResult;
|
||||
},
|
||||
|
||||
// Every 5 minutes
|
||||
refreshFoldersInterval = 300000,
|
||||
|
||||
/**
|
||||
* @param {boolean=} boot = false
|
||||
*/
|
||||
folderInformationMultiply = (boot = false) => {
|
||||
const folders = FolderUserStore.getNextFolderNames(refreshFoldersInterval);
|
||||
if (arrayLength(folders)) {
|
||||
Remote.request('FolderInformationMultiply', (iError, oData) => {
|
||||
if (!iError && arrayLength(oData.Result)) {
|
||||
const utc = Date.now();
|
||||
oData.Result.forEach(item => {
|
||||
const hash = getFolderHash(item.Folder),
|
||||
folder = getFolderFromCacheList(item.Folder);
|
||||
|
||||
if (folder) {
|
||||
folder.expires = utc;
|
||||
|
||||
setFolderHash(item.Folder, item.Hash);
|
||||
|
||||
folder.messageCountAll(item.MessageCount);
|
||||
|
||||
let unreadCountChange = folder.messageCountUnread() !== item.MessageUnseenCount;
|
||||
|
||||
folder.messageCountUnread(item.MessageUnseenCount);
|
||||
|
||||
if (unreadCountChange) {
|
||||
MessageFlagsCache.clearFolder(folder.fullName);
|
||||
}
|
||||
|
||||
if (!hash || item.Hash !== hash) {
|
||||
if (folder.fullName === FolderUserStore.currentFolderFullName()) {
|
||||
MessagelistUserStore.reload();
|
||||
}
|
||||
} else if (unreadCountChange
|
||||
&& folder.fullName === FolderUserStore.currentFolderFullName()
|
||||
&& MessagelistUserStore.length) {
|
||||
rl.app.folderInformation(folder.fullName, MessagelistUserStore());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (boot) {
|
||||
setTimeout(() => folderInformationMultiply(true), 2000);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
Folders: folders
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
moveOrDeleteResponseHelper = (iError, oData) => {
|
||||
if (iError) {
|
||||
setFolderHash(FolderUserStore.currentFolderFullName(), '');
|
||||
alert(getNotification(iError));
|
||||
} else if (FolderUserStore.currentFolder()) {
|
||||
if (2 === arrayLength(oData.Result)) {
|
||||
setFolderHash(oData.Result[0], oData.Result[1]);
|
||||
} else {
|
||||
setFolderHash(FolderUserStore.currentFolderFullName(), '');
|
||||
}
|
||||
MessagelistUserStore.reload(!MessagelistUserStore.length);
|
||||
}
|
||||
},
|
||||
|
||||
messagesMoveHelper = (fromFolderFullName, toFolderFullName, uidsForMove) => {
|
||||
const
|
||||
sSpamFolder = FolderUserStore.spamFolder(),
|
||||
isSpam = sSpamFolder === toFolderFullName,
|
||||
isHam = !isSpam && sSpamFolder === fromFolderFullName && getFolderInboxName() === toFolderFullName;
|
||||
|
||||
Remote.request('MessageMove',
|
||||
moveOrDeleteResponseHelper,
|
||||
{
|
||||
FromFolder: fromFolderFullName,
|
||||
ToFolder: toFolderFullName,
|
||||
Uids: uidsForMove.join(','),
|
||||
MarkAsRead: (isSpam || FolderUserStore.trashFolder() === toFolderFullName) ? 1 : 0,
|
||||
Learning: isSpam ? 'SPAM' : isHam ? 'HAM' : ''
|
||||
},
|
||||
null,
|
||||
'',
|
||||
['MessageList']
|
||||
);
|
||||
},
|
||||
|
||||
messagesDeleteHelper = (sFromFolderFullName, aUidForRemove) => {
|
||||
Remote.request('MessageDelete',
|
||||
moveOrDeleteResponseHelper,
|
||||
{
|
||||
Folder: sFromFolderFullName,
|
||||
Uids: aUidForRemove.join(',')
|
||||
},
|
||||
null,
|
||||
'',
|
||||
['MessageList']
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} sFromFolderFullName
|
||||
* @param {Array} aUidForMove
|
||||
* @param {string} sToFolderFullName
|
||||
* @param {boolean=} bCopy = false
|
||||
*/
|
||||
moveMessagesToFolder = (sFromFolderFullName, aUidForMove, sToFolderFullName, bCopy) => {
|
||||
if (sFromFolderFullName !== sToFolderFullName && arrayLength(aUidForMove)) {
|
||||
const oFromFolder = getFolderFromCacheList(sFromFolderFullName),
|
||||
oToFolder = getFolderFromCacheList(sToFolderFullName);
|
||||
|
||||
if (oFromFolder && oToFolder) {
|
||||
if (bCopy) {
|
||||
Remote.request('MessageCopy', null, {
|
||||
FromFolder: oFromFolder.fullName,
|
||||
ToFolder: oToFolder.fullName,
|
||||
Uids: aUidForMove.join(',')
|
||||
});
|
||||
} else {
|
||||
messagesMoveHelper(oFromFolder.fullName, oToFolder.fullName, aUidForMove);
|
||||
}
|
||||
|
||||
MessagelistUserStore.removeMessagesFromList(oFromFolder.fullName, aUidForMove, oToFolder.fullName, bCopy);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
import { i18n } from 'Common/Translator';
|
||||
|
||||
export function timestampToString(timeStampInUTC, formatStr) {
|
||||
const now = Date.now(),
|
||||
time = 0 < timeStampInUTC ? Math.min(now, timeStampInUTC * 1000) : (0 === timeStampInUTC ? now : 0);
|
||||
|
||||
if (31536000000 < time) {
|
||||
const m = new Date(time);
|
||||
switch (formatStr) {
|
||||
case 'FROMNOW':
|
||||
return m.fromNow();
|
||||
case 'SHORT': {
|
||||
if (4 >= (now - time) / 3600000)
|
||||
return m.fromNow();
|
||||
const mt = m.getTime(), date = new Date,
|
||||
dt = date.setHours(0,0,0,0);
|
||||
if (mt > dt)
|
||||
return i18n('MESSAGE_LIST/TODAY_AT', {TIME: m.format('LT')});
|
||||
if (mt > dt - 86400000)
|
||||
return i18n('MESSAGE_LIST/YESTERDAY_AT', {TIME: m.format('LT')});
|
||||
if (date.getFullYear() === m.getFullYear())
|
||||
return m.format('d M');
|
||||
return m.format('LL');
|
||||
}
|
||||
case 'FULL':
|
||||
return m.format('LLL');
|
||||
default:
|
||||
return m.format(formatStr);
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
export function timeToNode(element, time) {
|
||||
try {
|
||||
if (time) {
|
||||
element.dateTime = (new Date(time * 1000)).format('Y-m-d\\TH:i:s');
|
||||
} else {
|
||||
time = Date.parse(element.dateTime) / 1000;
|
||||
}
|
||||
|
||||
let key = element.dataset.momentFormat;
|
||||
if (key) {
|
||||
element.textContent = timestampToString(time, key);
|
||||
}
|
||||
|
||||
if ((key = element.dataset.momentFormatTitle)) {
|
||||
element.title = timestampToString(time, key);
|
||||
}
|
||||
} catch (e) {
|
||||
// prevent knockout crashes
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
|
@ -82,6 +82,64 @@ export const
|
|||
element.querySelectorAll('[data-i18n]').forEach(item => i18nToNode(item))
|
||||
, 1),
|
||||
|
||||
timestampToString = (timeStampInUTC, formatStr) => {
|
||||
const now = Date.now(),
|
||||
time = 0 < timeStampInUTC ? Math.min(now, timeStampInUTC * 1000) : (0 === timeStampInUTC ? now : 0);
|
||||
|
||||
if (31536000000 < time) {
|
||||
const m = new Date(time);
|
||||
switch (formatStr) {
|
||||
case 'FROMNOW':
|
||||
return m.fromNow();
|
||||
case 'SHORT': {
|
||||
if (4 >= (now - time) / 3600000)
|
||||
return m.fromNow();
|
||||
const mt = m.getTime(), date = new Date,
|
||||
dt = date.setHours(0,0,0,0);
|
||||
if (mt > dt)
|
||||
return i18n('MESSAGE_LIST/TODAY_AT', {TIME: m.format('LT')});
|
||||
if (mt > dt - 86400000)
|
||||
return i18n('MESSAGE_LIST/YESTERDAY_AT', {TIME: m.format('LT')});
|
||||
if (date.getFullYear() === m.getFullYear())
|
||||
return m.format('d M');
|
||||
return m.format('LL');
|
||||
}
|
||||
case 'FULL':
|
||||
return m.format('LLL');
|
||||
default:
|
||||
return m.format(formatStr);
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
|
||||
timeToNode = (element, time) => {
|
||||
try {
|
||||
if (time) {
|
||||
element.dateTime = (new Date(time * 1000)).format('Y-m-d\\TH:i:s');
|
||||
} else {
|
||||
time = Date.parse(element.dateTime) / 1000;
|
||||
}
|
||||
|
||||
let key = element.dataset.momentFormat;
|
||||
if (key) {
|
||||
element.textContent = timestampToString(time, key);
|
||||
}
|
||||
|
||||
if ((key = element.dataset.momentFormatTitle)) {
|
||||
element.title = timestampToString(time, key);
|
||||
}
|
||||
} catch (e) {
|
||||
// prevent knockout crashes
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
|
||||
reloadTime = () => setTimeout(() =>
|
||||
doc.querySelectorAll('time').forEach(element => timeToNode(element))
|
||||
, 1),
|
||||
|
||||
/**
|
||||
* @param {Function} startCallback
|
||||
* @param {Function=} langCallback = null
|
||||
|
|
@ -129,7 +187,7 @@ export const
|
|||
// reload the data
|
||||
if (init()) {
|
||||
i18nToNodes(doc);
|
||||
admin || rl.app.reloadTime();
|
||||
admin || reloadTime();
|
||||
trigger(!trigger());
|
||||
}
|
||||
script.remove();
|
||||
|
|
|
|||
|
|
@ -1,26 +1,18 @@
|
|||
import { ComposeType/*, FolderType*/ } from 'Common/EnumsUser';
|
||||
import { MessageFlagsCache, addRequestedMessage } from 'Common/Cache';
|
||||
import { MessageSetAction, ComposeType/*, FolderType*/ } from 'Common/EnumsUser';
|
||||
import { doc, createElement, elementById } from 'Common/Globals';
|
||||
import { plainToHtml } from 'Common/Html';
|
||||
import { getNotification } from 'Common/Translator';
|
||||
import { EmailModel } from 'Model/Email';
|
||||
import { isArray } from 'Common/Utils';
|
||||
import { doc, createElement } from 'Common/Globals';
|
||||
import { FolderUserStore } from 'Stores/User/Folder';
|
||||
import { MessageModel } from 'Model/Message';
|
||||
import { MessageUserStore } from 'Stores/User/Message';
|
||||
import { MessagelistUserStore } from 'Stores/User/Messagelist';
|
||||
import { SettingsUserStore } from 'Stores/User/Settings';
|
||||
import * as Local from 'Storage/Client';
|
||||
import { plainToHtml } from 'Common/Html';
|
||||
import { ThemeStore } from 'Stores/Theme';
|
||||
|
||||
export const
|
||||
|
||||
sortFolders = folders => {
|
||||
try {
|
||||
let collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
|
||||
folders.sort((a, b) =>
|
||||
a.isInbox() ? -1 : (b.isInbox() ? 1 : collator.compare(a.fullName, b.fullName))
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} link
|
||||
* @returns {boolean}
|
||||
|
|
@ -39,69 +31,6 @@ download = (link, name = "") => {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Array=} aDisabled
|
||||
* @param {Array=} aHeaderLines
|
||||
* @param {Function=} fDisableCallback
|
||||
* @param {Function=} fRenameCallback
|
||||
* @param {boolean=} bNoSelectSelectable Used in FolderCreatePopupView
|
||||
* @returns {Array}
|
||||
*/
|
||||
folderListOptionsBuilder = (
|
||||
aDisabled,
|
||||
aHeaderLines,
|
||||
fRenameCallback,
|
||||
fDisableCallback,
|
||||
bNoSelectSelectable,
|
||||
aList = FolderUserStore.folderList()
|
||||
) => {
|
||||
const
|
||||
aResult = [],
|
||||
sDeepPrefix = '\u00A0\u00A0\u00A0',
|
||||
// FolderSystemPopupView should always be true
|
||||
showUnsubscribed = fRenameCallback ? !SettingsUserStore.hideUnsubscribed() : true,
|
||||
|
||||
foldersWalk = folders => {
|
||||
folders.forEach(oItem => {
|
||||
if (showUnsubscribed || oItem.hasSubscriptions() || !oItem.exists) {
|
||||
aResult.push({
|
||||
id: oItem.fullName,
|
||||
name:
|
||||
sDeepPrefix.repeat(oItem.deep) +
|
||||
fRenameCallback(oItem),
|
||||
system: false,
|
||||
disabled: !bNoSelectSelectable && (
|
||||
!oItem.selectable() ||
|
||||
aDisabled.includes(oItem.fullName) ||
|
||||
fDisableCallback(oItem))
|
||||
});
|
||||
}
|
||||
|
||||
if (oItem.subFolders.length) {
|
||||
foldersWalk(oItem.subFolders());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
fDisableCallback = fDisableCallback || (() => false);
|
||||
fRenameCallback = fRenameCallback || (oItem => oItem.name());
|
||||
isArray(aDisabled) || (aDisabled = []);
|
||||
|
||||
isArray(aHeaderLines) && aHeaderLines.forEach(line =>
|
||||
aResult.push({
|
||||
id: line[0],
|
||||
name: line[1],
|
||||
system: false,
|
||||
disabled: false
|
||||
})
|
||||
);
|
||||
|
||||
foldersWalk(aList);
|
||||
|
||||
return aResult;
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {function}
|
||||
*/
|
||||
|
|
@ -334,4 +263,123 @@ setLayoutResizer = (source, target, sClientSideKeyName, mode) =>
|
|||
} else {
|
||||
source.observer && source.observer.disconnect();
|
||||
}
|
||||
},
|
||||
|
||||
populateMessageBody = (oMessage, preload) => {
|
||||
if (oMessage) {
|
||||
preload || MessageUserStore.hideMessageBodies();
|
||||
preload || MessageUserStore.loading(true);
|
||||
rl.app.Remote.message((iError, oData/*, bCached*/) => {
|
||||
if (iError) {
|
||||
if (Notification.RequestAborted !== iError && !preload) {
|
||||
MessageUserStore.message(null);
|
||||
MessageUserStore.error(getNotification(iError));
|
||||
}
|
||||
} else {
|
||||
oMessage = preload ? oMessage : null;
|
||||
let
|
||||
isNew = false,
|
||||
json = oData && oData.Result,
|
||||
message = oMessage || MessageUserStore.message();
|
||||
|
||||
if (
|
||||
json &&
|
||||
MessageModel.validJson(json) &&
|
||||
message &&
|
||||
message.folder === json.Folder
|
||||
) {
|
||||
const threads = message.threads(),
|
||||
messagesDom = MessageUserStore.bodiesDom();
|
||||
if (!oMessage && message.uid != json.Uid && threads.includes(json.Uid)) {
|
||||
message = MessageModel.reviveFromJson(json);
|
||||
if (message) {
|
||||
message.threads(threads);
|
||||
MessageFlagsCache.initMessage(message);
|
||||
|
||||
// Set clone
|
||||
MessageUserStore.message(MessageModel.fromMessageListItem(message));
|
||||
message = MessageUserStore.message();
|
||||
|
||||
isNew = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (message && message.uid == json.Uid) {
|
||||
oMessage || MessageUserStore.error('');
|
||||
/*
|
||||
if (bCached) {
|
||||
delete json.Flags;
|
||||
}
|
||||
*/
|
||||
isNew || message.revivePropertiesFromJson(json);
|
||||
addRequestedMessage(message.folder, message.uid);
|
||||
if (messagesDom) {
|
||||
let id = 'rl-msg-' + message.hash.replace(/[^a-zA-Z0-9]/g, ''),
|
||||
body = elementById(id);
|
||||
if (body) {
|
||||
message.body = body;
|
||||
message.isHtml(body.classList.contains('html'));
|
||||
message.hasImages(body.rlHasImages);
|
||||
} else {
|
||||
body = Element.fromHTML('<div id="' + id + '" hidden="" class="b-text-part '
|
||||
+ (message.pgpSigned() ? ' openpgp-signed' : '')
|
||||
+ (message.pgpEncrypted() ? ' openpgp-encrypted' : '')
|
||||
+ '">'
|
||||
+ '</div>');
|
||||
message.body = body;
|
||||
if (!SettingsUserStore.viewHTML() || !message.viewHtml()) {
|
||||
message.viewPlain();
|
||||
}
|
||||
|
||||
MessageUserStore.purgeMessageBodyCache();
|
||||
}
|
||||
|
||||
messagesDom.append(body);
|
||||
|
||||
if (!oMessage) {
|
||||
MessageUserStore.activeDom(message.body);
|
||||
MessageUserStore.hideMessageBodies();
|
||||
message.body.hidden = false;
|
||||
}
|
||||
oMessage && message.viewPopupMessage();
|
||||
}
|
||||
|
||||
MessageFlagsCache.initMessage(message);
|
||||
if (message.isUnseen()) {
|
||||
MessageUserStore.MessageSeenTimer = setTimeout(
|
||||
() => MessagelistUserStore.setAction(message.folder, MessageSetAction.SetSeen, [message]),
|
||||
SettingsUserStore.messageReadDelay() * 1000 // seconds
|
||||
);
|
||||
}
|
||||
|
||||
if (message && isNew) {
|
||||
let selectedMessage = MessagelistUserStore.selectedMessage();
|
||||
if (
|
||||
selectedMessage &&
|
||||
(message.folder !== selectedMessage.folder || message.uid != selectedMessage.uid)
|
||||
) {
|
||||
MessagelistUserStore.selectedMessage(null);
|
||||
if (1 === MessagelistUserStore.length) {
|
||||
MessagelistUserStore.focusedMessage(null);
|
||||
}
|
||||
} else if (!selectedMessage) {
|
||||
selectedMessage = MessagelistUserStore.find(
|
||||
subMessage =>
|
||||
subMessage &&
|
||||
subMessage.folder === message.folder &&
|
||||
subMessage.uid == message.uid
|
||||
);
|
||||
|
||||
if (selectedMessage) {
|
||||
MessagelistUserStore.selectedMessage(selectedMessage);
|
||||
MessagelistUserStore.focusedMessage(selectedMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
preload || MessageUserStore.loading(false);
|
||||
}, oMessage.folder, oMessage.uid);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue