import ko from 'ko'; import { koComputable, addObservablesTo, addSubscribablesTo, addComputablesTo } from 'External/ko'; import { FolderType } from 'Common/EnumsUser'; import { UNUSED_OPTION_VALUE } from 'Common/Consts'; import { forEachObjectEntry } from 'Common/Utils'; import { getFolderInboxName, getFolderFromCacheList } from 'Common/Cache'; import { Settings, SettingsCapa } from 'Common/Globals'; //import Remote from 'Remote/User/Fetch'; // Circular dependency export const ignoredKeywords = [ // rfc5788 '$forwarded', '$mdnsent', '$submitpending', '$submitted', // rfc9051 '$junk', '$notjunk', '$phishing', // Mailo 'sent', // KMail '$encrypted', '$error', '$ignored', '$invitation', '$queued', '$sent', '$signed', '$todo', '$watched', // GMail '$notphishing', 'junk', 'nonjunk', // KMail & GMail '$attachment', '$replied', // Others '$readreceipt', '$notdelivered' ], isAllowedKeyword = value => '\\' != value[0] && !ignoredKeywords.includes(value.toLowerCase()), FolderUserStore = new class { constructor() { const self = this; addObservablesTo(self, { /** * To use "checkable" option in /#/settings/folders * When true, getNextFolderNames only lists system and "checkable" folders * and affects the update of unseen count * Auto set to true when amount of folders > folderSpecLimit to prevent requests overload, * see application.ini [labs] folders_spec_limit */ displaySpecSetting: false, sortMode: '', quotaLimit: 0, quotaUsage: 0, sentFolder: '', draftsFolder: '', spamFolder: '', trashFolder: '', archiveFolder: '', optimized: false, error: '', foldersLoading: false, foldersCreating: false, foldersDeleting: false, foldersRenaming: false, foldersInboxUnreadCount: 0 }); self.namespace = ''; self.folderList = ko.observableArray(/*new FolderCollectionModel*/); self.capabilities = ko.observableArray(); self.currentFolder = ko.observable(null).extend({ toggleSubscribeProperty: [self, 'selected'] }); addComputablesTo(self, { draftsFolderNotEnabled: () => !self.draftsFolder() || UNUSED_OPTION_VALUE === self.draftsFolder(), currentFolderFullName: () => (self.currentFolder() ? self.currentFolder().fullName : ''), currentFolderFullNameHash: () => (self.currentFolder() ? self.currentFolder().fullNameHash : ''), foldersChanging: () => self.foldersLoading() | self.foldersCreating() | self.foldersDeleting() | self.foldersRenaming(), systemFoldersNames: () => { const list = [getFolderInboxName()], others = [self.sentFolder(), self.draftsFolder(), self.spamFolder(), self.trashFolder(), self.archiveFolder()]; self.folderList().length && others.forEach(name => name && UNUSED_OPTION_VALUE !== name && list.push(name)); return list; }, systemFolders: () => self.systemFoldersNames().map(name => getFolderFromCacheList(name)).filter(v => v) }); const subscribeRemoveSystemFolder = observable => { observable.subscribe(() => getFolderFromCacheList(observable())?.type(0), self, 'beforeChange'); }, fSetSystemFolderType = type => value => getFolderFromCacheList(value)?.type(type); subscribeRemoveSystemFolder(self.sentFolder); subscribeRemoveSystemFolder(self.draftsFolder); subscribeRemoveSystemFolder(self.spamFolder); subscribeRemoveSystemFolder(self.trashFolder); subscribeRemoveSystemFolder(self.archiveFolder); addSubscribablesTo(self, { sentFolder: fSetSystemFolderType(FolderType.Sent), draftsFolder: fSetSystemFolderType(FolderType.Drafts), spamFolder: fSetSystemFolderType(FolderType.Junk), trashFolder: fSetSystemFolderType(FolderType.Trash), archiveFolder: fSetSystemFolderType(FolderType.Archive) }); self.quotaPercentage = koComputable(() => { const quota = self.quotaLimit(), usage = self.quotaUsage(); return 0 < quota ? Math.ceil((usage / quota) * 100) : 0; }); } /** * If the IMAP server supports SORT, METADATA */ hasCapability(name) { return this.capabilities().includes(name); } allowKolab() { return FolderUserStore.hasCapability('METADATA') && SettingsCapa('Kolab'); } /** * @returns {Array} */ getNextFolderNames(ttl) { const result = [], limit = 10, utc = Date.now(), timeout = utc - ttl, timeouts = [], bDisplaySpecSetting = this.displaySpecSetting(), fSearchFunction = (list) => { list.forEach(folder => { if ( folder?.selectable() && folder.exists && timeout > folder.expires && (folder.isSystemFolder() || (folder.isSubscribed() && (folder.checkable() || !bDisplaySpecSetting))) ) { timeouts.push([folder.expires, folder.fullName]); } if (folder?.subFolders.length) { fSearchFunction(folder.subFolders()); } }); }; fSearchFunction(this.folderList()); timeouts.sort((a, b) => (a[0] < b[0]) ? -1 : (a[0] > b[0] ? 1 : 0)); timeouts.find(aItem => { const folder = getFolderFromCacheList(aItem[1]); if (folder) { folder.expires = utc; // result.indexOf(aItem[1]) || result.push(aItem[1]); } return limit <= result.length; }); return result; } saveSystemFolders(folders) { folders = folders || { sent: FolderUserStore.sentFolder(), drafts: FolderUserStore.draftsFolder(), junk: FolderUserStore.spamFolder(), trash: FolderUserStore.trashFolder(), archive: FolderUserStore.archiveFolder() }; forEachObjectEntry(folders, (k,v)=>Settings.set(k+'Folder',v)); rl.app.Remote.request('SystemFoldersUpdate', null, folders); } };