2016-09-29 03:54:25 +00:00
|
|
|
/* jslint node: true */
|
|
|
|
'use strict';
|
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
// ENiGMA½
|
2022-06-05 20:04:25 +00:00
|
|
|
const MenuModule = require('./menu_module.js').MenuModule;
|
|
|
|
const ViewController = require('./view_controller.js').ViewController;
|
|
|
|
const ansi = require('./ansi_term.js');
|
|
|
|
const theme = require('./theme.js');
|
|
|
|
const FileEntry = require('./file_entry.js');
|
|
|
|
const stringFormat = require('./string_format.js');
|
|
|
|
const FileArea = require('./file_base_area.js');
|
|
|
|
const Errors = require('./enig_error.js').Errors;
|
|
|
|
const ErrNotEnabled = require('./enig_error.js').ErrorReasons.NotEnabled;
|
|
|
|
const ArchiveUtil = require('./archive_util.js');
|
|
|
|
const Config = require('./config.js').get;
|
|
|
|
const DownloadQueue = require('./download_queue.js');
|
|
|
|
const FileAreaWeb = require('./file_area_web.js');
|
|
|
|
const FileBaseFilters = require('./file_base_filter.js');
|
|
|
|
const resolveMimeType = require('./mime_util.js').resolveMimeType;
|
|
|
|
const isAnsi = require('./string_util.js').isAnsi;
|
|
|
|
const controlCodesToAnsi = require('./color_codes.js').controlCodesToAnsi;
|
2018-06-23 03:26:46 +00:00
|
|
|
|
|
|
|
// deps
|
2022-06-05 20:04:25 +00:00
|
|
|
const async = require('async');
|
|
|
|
const _ = require('lodash');
|
|
|
|
const moment = require('moment');
|
|
|
|
const paths = require('path');
|
2016-09-29 03:54:25 +00:00
|
|
|
|
|
|
|
exports.moduleInfo = {
|
2022-06-05 20:04:25 +00:00
|
|
|
name: 'File Area List',
|
|
|
|
desc: 'Lists contents of file an file area',
|
|
|
|
author: 'NuSkooler',
|
2016-09-29 03:54:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const FormIds = {
|
2022-06-05 20:04:25 +00:00
|
|
|
browse: 0,
|
|
|
|
details: 1,
|
|
|
|
detailsGeneral: 2,
|
|
|
|
detailsNfo: 3,
|
|
|
|
detailsFileList: 4,
|
2016-09-29 03:54:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const MciViewIds = {
|
2022-06-05 20:04:25 +00:00
|
|
|
browse: {
|
|
|
|
desc: 1,
|
|
|
|
navMenu: 2,
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
customRangeStart: 10, // 10+ = customs
|
2018-06-22 05:15:04 +00:00
|
|
|
},
|
2022-06-05 20:04:25 +00:00
|
|
|
details: {
|
|
|
|
navMenu: 1,
|
|
|
|
infoXyTop: 2, // %XY starting position for info area
|
|
|
|
infoXyBottom: 3,
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
customRangeStart: 10, // 10+ = customs
|
2018-06-22 05:15:04 +00:00
|
|
|
},
|
2022-06-05 20:04:25 +00:00
|
|
|
detailsGeneral: {
|
|
|
|
customRangeStart: 10, // 10+ = customs
|
2018-06-22 05:15:04 +00:00
|
|
|
},
|
2022-06-05 20:04:25 +00:00
|
|
|
detailsNfo: {
|
|
|
|
nfo: 1,
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
customRangeStart: 10, // 10+ = customs
|
2018-06-22 05:15:04 +00:00
|
|
|
},
|
2022-06-05 20:04:25 +00:00
|
|
|
detailsFileList: {
|
|
|
|
fileList: 1,
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
customRangeStart: 10, // 10+ = customs
|
2018-06-22 05:15:04 +00:00
|
|
|
},
|
2016-09-29 03:54:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
exports.getModule = class FileAreaList extends MenuModule {
|
2018-06-22 05:15:04 +00:00
|
|
|
constructor(options) {
|
|
|
|
super(options);
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
this.filterCriteria = _.get(options, 'extraArgs.filterCriteria');
|
|
|
|
this.fileList = _.get(options, 'extraArgs.fileList');
|
|
|
|
this.lastFileNextExit = _.get(options, 'extraArgs.lastFileNextExit', true);
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (this.fileList) {
|
2018-06-23 03:26:46 +00:00
|
|
|
// we'll need to adjust position as well!
|
2018-06-22 05:15:04 +00:00
|
|
|
this.fileListPosition = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.dlQueue = new DownloadQueue(this.client);
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (!this.filterCriteria) {
|
2018-06-22 05:15:04 +00:00
|
|
|
this.filterCriteria = FileBaseFilters.getActiveFilter(this.client);
|
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (_.isString(this.filterCriteria)) {
|
2018-06-22 05:15:04 +00:00
|
|
|
this.filterCriteria = JSON.parse(this.filterCriteria);
|
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (_.has(options, 'lastMenuResult.value')) {
|
2018-06-22 05:15:04 +00:00
|
|
|
this.lastMenuResultValue = options.lastMenuResult.value;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.menuMethods = {
|
2022-06-05 20:04:25 +00:00
|
|
|
nextFile: (formData, extraArgs, cb) => {
|
|
|
|
if (this.fileListPosition + 1 < this.fileList.length) {
|
2018-06-22 05:15:04 +00:00
|
|
|
this.fileListPosition += 1;
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
return this.displayBrowsePage(true, cb); // true=clerarScreen
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (this.lastFileNextExit) {
|
2018-06-22 05:15:04 +00:00
|
|
|
return this.prevMenu(cb);
|
|
|
|
}
|
|
|
|
|
|
|
|
return cb(null);
|
|
|
|
},
|
2022-06-05 20:04:25 +00:00
|
|
|
prevFile: (formData, extraArgs, cb) => {
|
|
|
|
if (this.fileListPosition > 0) {
|
2018-06-22 05:15:04 +00:00
|
|
|
--this.fileListPosition;
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
return this.displayBrowsePage(true, cb); // true=clearScreen
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return cb(null);
|
|
|
|
},
|
2022-06-05 20:04:25 +00:00
|
|
|
viewDetails: (formData, extraArgs, cb) => {
|
2018-06-22 05:15:04 +00:00
|
|
|
this.viewControllers.browse.setFocus(false);
|
|
|
|
return this.displayDetailsPage(cb);
|
|
|
|
},
|
2022-06-05 20:04:25 +00:00
|
|
|
detailsQuit: (formData, extraArgs, cb) => {
|
|
|
|
['detailsNfo', 'detailsFileList', 'details'].forEach(n => {
|
2018-06-22 05:15:04 +00:00
|
|
|
const vc = this.viewControllers[n];
|
2022-06-05 20:04:25 +00:00
|
|
|
if (vc) {
|
2018-06-22 05:15:04 +00:00
|
|
|
vc.detachClientEvents();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
return this.displayBrowsePage(true, cb); // true=clearScreen
|
2018-06-22 05:15:04 +00:00
|
|
|
},
|
2022-06-05 20:04:25 +00:00
|
|
|
toggleQueue: (formData, extraArgs, cb) => {
|
2018-06-22 05:15:04 +00:00
|
|
|
this.dlQueue.toggle(this.currentFileEntry);
|
|
|
|
this.updateQueueIndicator();
|
|
|
|
return cb(null);
|
|
|
|
},
|
2022-06-05 20:04:25 +00:00
|
|
|
showWebDownloadLink: (formData, extraArgs, cb) => {
|
2018-06-22 05:15:04 +00:00
|
|
|
return this.fetchAndDisplayWebDownloadLink(cb);
|
|
|
|
},
|
2022-06-05 20:04:25 +00:00
|
|
|
displayHelp: (formData, extraArgs, cb) => {
|
2018-06-22 05:15:04 +00:00
|
|
|
return this.displayHelpPage(cb);
|
2020-11-27 23:44:40 +00:00
|
|
|
},
|
2022-06-05 20:04:25 +00:00
|
|
|
movementKeyPressed: (formData, extraArgs, cb) => {
|
2020-11-27 23:44:40 +00:00
|
|
|
return this._handleMovementKeyPress(_.get(formData, 'key.name'), cb);
|
|
|
|
},
|
2018-06-22 05:15:04 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
enter() {
|
|
|
|
super.enter();
|
|
|
|
}
|
|
|
|
|
|
|
|
leave() {
|
|
|
|
super.leave();
|
|
|
|
}
|
|
|
|
|
|
|
|
getSaveState() {
|
|
|
|
return {
|
2022-06-05 20:04:25 +00:00
|
|
|
fileList: this.fileList,
|
|
|
|
fileListPosition: this.fileListPosition,
|
2018-06-22 05:15:04 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
restoreSavedState(savedState) {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (savedState) {
|
|
|
|
this.fileList = savedState.fileList;
|
|
|
|
this.fileListPosition = savedState.fileListPosition;
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
updateFileEntryWithMenuResult(cb) {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (!this.lastMenuResultValue) {
|
2018-06-22 05:15:04 +00:00
|
|
|
return cb(null);
|
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (_.isNumber(this.lastMenuResultValue.rating)) {
|
2018-06-22 05:15:04 +00:00
|
|
|
const fileId = this.fileList[this.fileListPosition];
|
2022-06-05 20:04:25 +00:00
|
|
|
FileEntry.persistUserRating(
|
|
|
|
fileId,
|
|
|
|
this.client.user.userId,
|
|
|
|
this.lastMenuResultValue.rating,
|
|
|
|
err => {
|
|
|
|
if (err) {
|
|
|
|
this.client.log.warn(
|
|
|
|
{ error: err.message, fileId: fileId },
|
|
|
|
'Failed to persist file rating'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return cb(null);
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
2022-06-05 20:04:25 +00:00
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
} else {
|
|
|
|
return cb(null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
initSequence() {
|
|
|
|
const self = this;
|
|
|
|
|
|
|
|
async.series(
|
|
|
|
[
|
|
|
|
function preInit(callback) {
|
|
|
|
return self.updateFileEntryWithMenuResult(callback);
|
|
|
|
},
|
|
|
|
function beforeArt(callback) {
|
|
|
|
return self.beforeArt(callback);
|
|
|
|
},
|
|
|
|
function display(callback) {
|
|
|
|
return self.displayBrowsePage(false, err => {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (err) {
|
|
|
|
self.gotoMenu(
|
|
|
|
self.menuConfig.config.noResultsMenu ||
|
|
|
|
'fileBaseListEntriesNoResults'
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
return callback(err);
|
|
|
|
});
|
2022-06-05 20:04:25 +00:00
|
|
|
},
|
2018-06-22 05:15:04 +00:00
|
|
|
],
|
|
|
|
() => {
|
|
|
|
self.finishedLoading();
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
populateCurrentEntryInfo(cb) {
|
2022-06-05 20:04:25 +00:00
|
|
|
const config = this.menuConfig.config;
|
|
|
|
const currEntry = this.currentFileEntry;
|
|
|
|
|
|
|
|
const uploadTimestampFormat =
|
|
|
|
config.uploadTimestampFormat ||
|
|
|
|
this.client.currentTheme.helpers.getDateFormat('short');
|
|
|
|
const area = FileArea.getFileAreaByTag(currEntry.areaTag);
|
|
|
|
const hashTagsSep = config.hashTagsSep || ', ';
|
|
|
|
const isQueuedIndicator = config.isQueuedIndicator || 'Y';
|
|
|
|
const isNotQueuedIndicator = config.isNotQueuedIndicator || 'N';
|
|
|
|
|
|
|
|
const entryInfo = (currEntry.entryInfo = {
|
|
|
|
fileId: currEntry.fileId,
|
|
|
|
areaTag: currEntry.areaTag,
|
|
|
|
areaName: _.get(area, 'name') || 'N/A',
|
|
|
|
areaDesc: _.get(area, 'desc') || 'N/A',
|
|
|
|
fileSha256: currEntry.fileSha256,
|
|
|
|
fileName: currEntry.fileName,
|
|
|
|
desc: currEntry.desc || '',
|
|
|
|
descLong: currEntry.descLong || '',
|
|
|
|
userRating: currEntry.userRating,
|
|
|
|
uploadTimestamp: moment(currEntry.uploadTimestamp).format(
|
|
|
|
uploadTimestampFormat
|
|
|
|
),
|
|
|
|
hashTags: Array.from(currEntry.hashTags).join(hashTagsSep),
|
|
|
|
isQueued: this.dlQueue.isQueued(currEntry)
|
|
|
|
? isQueuedIndicator
|
|
|
|
: isNotQueuedIndicator,
|
|
|
|
webDlLink: '', // :TODO: fetch web any existing web d/l link
|
|
|
|
webDlExpire: '', // :TODO: fetch web d/l link expire time
|
|
|
|
});
|
2018-06-22 05:15:04 +00:00
|
|
|
|
|
|
|
//
|
2018-06-23 03:26:46 +00:00
|
|
|
// We need the entry object to contain meta keys even if they are empty as
|
|
|
|
// consumers may very likely attempt to use them
|
2018-06-22 05:15:04 +00:00
|
|
|
//
|
|
|
|
const metaValues = FileEntry.WellKnownMetaValues;
|
|
|
|
metaValues.forEach(name => {
|
2022-06-05 20:04:25 +00:00
|
|
|
const value = !_.isUndefined(currEntry.meta[name])
|
|
|
|
? currEntry.meta[name]
|
|
|
|
: 'N/A';
|
2018-06-22 05:15:04 +00:00
|
|
|
entryInfo[_.camelCase(name)] = value;
|
|
|
|
});
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (entryInfo.archiveType) {
|
2018-06-22 05:15:04 +00:00
|
|
|
const mimeType = resolveMimeType(entryInfo.archiveType);
|
|
|
|
let desc;
|
2022-06-05 20:04:25 +00:00
|
|
|
if (mimeType) {
|
|
|
|
let fileType = _.get(Config(), ['fileTypes', mimeType]);
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (Array.isArray(fileType)) {
|
2018-06-23 03:26:46 +00:00
|
|
|
// further refine by extention
|
2022-06-05 20:04:25 +00:00
|
|
|
fileType = fileType.find(
|
|
|
|
ft => paths.extname(currEntry.fileName) === ft.ext
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
desc = fileType && fileType.desc;
|
|
|
|
}
|
|
|
|
entryInfo.archiveTypeDesc = desc || mimeType || entryInfo.archiveType;
|
|
|
|
} else {
|
|
|
|
entryInfo.archiveTypeDesc = 'N/A';
|
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
entryInfo.uploadByUsername = entryInfo.uploadByUserName =
|
|
|
|
entryInfo.uploadByUsername || 'N/A'; // may be imported
|
|
|
|
entryInfo.hashTags = entryInfo.hashTags || '(none)';
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
// create a rating string, e.g. "**---"
|
2022-06-05 20:04:25 +00:00
|
|
|
const userRatingTicked = config.userRatingTicked || '*';
|
|
|
|
const userRatingUnticked = config.userRatingUnticked || '';
|
|
|
|
entryInfo.userRating = ~~Math.round(entryInfo.userRating) || 0; // be safe!
|
|
|
|
entryInfo.userRatingString = userRatingTicked.repeat(entryInfo.userRating);
|
|
|
|
if (entryInfo.userRating < 5) {
|
|
|
|
entryInfo.userRatingString += userRatingUnticked.repeat(
|
|
|
|
5 - entryInfo.userRating
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
FileAreaWeb.getExistingTempDownloadServeItem(
|
|
|
|
this.client,
|
|
|
|
this.currentFileEntry,
|
|
|
|
(err, serveItem) => {
|
|
|
|
if (err) {
|
|
|
|
entryInfo.webDlExpire = '';
|
|
|
|
if (ErrNotEnabled === err.reasonCode) {
|
|
|
|
entryInfo.webDlExpire =
|
|
|
|
config.webDlLinkNoWebserver || 'Web server is not enabled';
|
|
|
|
} else {
|
|
|
|
entryInfo.webDlLink =
|
|
|
|
config.webDlLinkNeedsGenerated || 'Not yet generated';
|
|
|
|
}
|
2018-06-22 05:15:04 +00:00
|
|
|
} else {
|
2022-06-05 20:04:25 +00:00
|
|
|
const webDlExpireTimeFormat =
|
|
|
|
config.webDlExpireTimeFormat ||
|
|
|
|
this.client.currentTheme.helpers.getDateTimeFormat('short');
|
|
|
|
|
|
|
|
entryInfo.webDlLink =
|
|
|
|
ansi.vtxHyperlink(this.client, serveItem.url) + serveItem.url;
|
|
|
|
entryInfo.webDlExpire = moment(serveItem.expireTimestamp).format(
|
|
|
|
webDlExpireTimeFormat
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
return cb(null);
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
2022-06-05 20:04:25 +00:00
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
populateCustomLabels(category, startId) {
|
2022-06-05 20:04:25 +00:00
|
|
|
return this.updateCustomViewTextsWithFilter(
|
|
|
|
category,
|
|
|
|
startId,
|
|
|
|
this.currentFileEntry.entryInfo
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
displayArtAndPrepViewController(name, options, cb) {
|
2022-06-05 20:04:25 +00:00
|
|
|
const self = this;
|
|
|
|
const config = this.menuConfig.config;
|
2018-06-22 05:15:04 +00:00
|
|
|
|
|
|
|
async.waterfall(
|
|
|
|
[
|
|
|
|
function readyAndDisplayArt(callback) {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (options.clearScreen) {
|
2018-06-22 05:15:04 +00:00
|
|
|
self.client.term.rawWrite(ansi.resetScreen());
|
|
|
|
}
|
|
|
|
|
|
|
|
theme.displayThemedAsset(
|
|
|
|
config.art[name],
|
|
|
|
self.client,
|
2022-06-05 20:04:25 +00:00
|
|
|
{ font: self.menuConfig.font, trailingLF: false },
|
2018-06-22 05:15:04 +00:00
|
|
|
(err, artData) => {
|
|
|
|
return callback(err, artData);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
|
|
|
function prepeareViewController(artData, callback) {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (_.isUndefined(self.viewControllers[name])) {
|
2018-06-22 05:15:04 +00:00
|
|
|
const vcOpts = {
|
2022-06-05 20:04:25 +00:00
|
|
|
client: self.client,
|
|
|
|
formId: FormIds[name],
|
2018-06-22 05:15:04 +00:00
|
|
|
};
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (!_.isUndefined(options.noInput)) {
|
2018-06-22 05:15:04 +00:00
|
|
|
vcOpts.noInput = options.noInput;
|
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
const vc = self.addViewController(
|
|
|
|
name,
|
|
|
|
new ViewController(vcOpts)
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if ('details' === name) {
|
2018-06-22 05:15:04 +00:00
|
|
|
try {
|
|
|
|
self.detailsInfoArea = {
|
2022-06-05 20:04:25 +00:00
|
|
|
top: artData.mciMap.XY2.position,
|
|
|
|
bottom: artData.mciMap.XY3.position,
|
2018-06-22 05:15:04 +00:00
|
|
|
};
|
2022-06-05 20:04:25 +00:00
|
|
|
} catch (e) {
|
|
|
|
return callback(
|
|
|
|
Errors.DoesNotExist(
|
|
|
|
'Missing XY2 and XY3 position indicators!'
|
|
|
|
)
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const loadOpts = {
|
2022-06-05 20:04:25 +00:00
|
|
|
callingMenu: self,
|
|
|
|
mciMap: artData.mciMap,
|
|
|
|
formId: FormIds[name],
|
2018-06-22 05:15:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
return vc.loadFromMenuConfig(loadOpts, callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.viewControllers[name].setFocus(true);
|
|
|
|
return callback(null);
|
|
|
|
},
|
|
|
|
],
|
|
|
|
err => {
|
|
|
|
return cb(err);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
displayBrowsePage(clearScreen, cb) {
|
2022-06-05 20:04:25 +00:00
|
|
|
const self = this;
|
2018-06-22 05:15:04 +00:00
|
|
|
|
|
|
|
async.series(
|
|
|
|
[
|
|
|
|
function fetchEntryData(callback) {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (self.fileList) {
|
2018-06-22 05:15:04 +00:00
|
|
|
return callback(null);
|
|
|
|
}
|
2022-06-05 20:04:25 +00:00
|
|
|
return self.loadFileIds(false, callback); // false=do not force
|
2018-06-22 05:15:04 +00:00
|
|
|
},
|
|
|
|
function checkEmptyResults(callback) {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (0 === self.fileList.length) {
|
|
|
|
return callback(
|
|
|
|
Errors.General('No results for criteria', 'NORESULTS')
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
return callback(null);
|
|
|
|
},
|
|
|
|
function prepArtAndViewController(callback) {
|
2022-06-05 20:04:25 +00:00
|
|
|
return self.displayArtAndPrepViewController(
|
|
|
|
'browse',
|
|
|
|
{ clearScreen: clearScreen },
|
|
|
|
callback
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
},
|
|
|
|
function loadCurrentFileInfo(callback) {
|
|
|
|
self.currentFileEntry = new FileEntry();
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
self.currentFileEntry.load(
|
|
|
|
self.fileList[self.fileListPosition],
|
|
|
|
err => {
|
|
|
|
if (err) {
|
|
|
|
return callback(err);
|
|
|
|
}
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
return self.populateCurrentEntryInfo(callback);
|
|
|
|
}
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
},
|
|
|
|
function populateDesc(callback) {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (_.isString(self.currentFileEntry.desc)) {
|
|
|
|
const descView = self.viewControllers.browse.getView(
|
|
|
|
MciViewIds.browse.desc
|
|
|
|
);
|
|
|
|
if (descView) {
|
2018-06-22 05:15:04 +00:00
|
|
|
//
|
2018-06-23 03:26:46 +00:00
|
|
|
// For descriptions we want to support as many color code systems
|
|
|
|
// as we can for coverage of what is found in the while (e.g. Renegade
|
|
|
|
// pipes, PCB @X##, etc.)
|
2018-06-22 05:15:04 +00:00
|
|
|
//
|
2018-06-23 03:26:46 +00:00
|
|
|
// MLTEV doesn't support all of this, so convert. If we produced ANSI
|
|
|
|
// esc sequences, we'll proceed with specialization, else just treat
|
|
|
|
// it as text.
|
2018-06-22 05:15:04 +00:00
|
|
|
//
|
|
|
|
const desc = controlCodesToAnsi(self.currentFileEntry.desc);
|
2022-06-05 20:04:25 +00:00
|
|
|
if (
|
|
|
|
desc.length != self.currentFileEntry.desc.length ||
|
|
|
|
isAnsi(desc)
|
|
|
|
) {
|
2018-12-29 20:15:58 +00:00
|
|
|
const opts = {
|
2022-06-05 20:04:25 +00:00
|
|
|
prepped: false,
|
|
|
|
forceLineTerm: true,
|
2018-12-29 20:15:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// if SAUCE states a term width, honor it else we may see
|
|
|
|
// display corruption
|
|
|
|
//
|
2022-06-05 20:04:25 +00:00
|
|
|
const sauceTermWidth = _.get(
|
|
|
|
self.currentFileEntry.meta,
|
|
|
|
'desc_sauce.Character.characterWidth'
|
|
|
|
);
|
|
|
|
if (_.isNumber(sauceTermWidth)) {
|
2018-12-29 20:15:58 +00:00
|
|
|
opts.termWidth = sauceTermWidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
descView.setAnsi(desc, opts, () => {
|
|
|
|
return callback(null);
|
|
|
|
});
|
2018-06-22 05:15:04 +00:00
|
|
|
} else {
|
|
|
|
descView.setText(self.currentFileEntry.desc);
|
|
|
|
return callback(null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return callback(null);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
function populateAdditionalViews(callback) {
|
|
|
|
self.updateQueueIndicator();
|
2022-06-05 20:04:25 +00:00
|
|
|
self.populateCustomLabels(
|
|
|
|
'browse',
|
|
|
|
MciViewIds.browse.customRangeStart
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
return callback(null);
|
2022-06-05 20:04:25 +00:00
|
|
|
},
|
2018-06-22 05:15:04 +00:00
|
|
|
],
|
|
|
|
err => {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (cb) {
|
2018-06-22 05:15:04 +00:00
|
|
|
return cb(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
displayDetailsPage(cb) {
|
2022-06-05 20:04:25 +00:00
|
|
|
const self = this;
|
2018-06-22 05:15:04 +00:00
|
|
|
|
|
|
|
async.series(
|
|
|
|
[
|
|
|
|
function prepArtAndViewController(callback) {
|
2022-06-05 20:04:25 +00:00
|
|
|
return self.displayArtAndPrepViewController(
|
|
|
|
'details',
|
|
|
|
{ clearScreen: true },
|
|
|
|
callback
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
},
|
|
|
|
function populateViews(callback) {
|
2022-06-05 20:04:25 +00:00
|
|
|
self.populateCustomLabels(
|
|
|
|
'details',
|
|
|
|
MciViewIds.details.customRangeStart
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
return callback(null);
|
|
|
|
},
|
|
|
|
function prepSection(callback) {
|
|
|
|
return self.displayDetailsSection('general', false, callback);
|
|
|
|
},
|
|
|
|
function listenNavChanges(callback) {
|
2022-06-05 20:04:25 +00:00
|
|
|
const navMenu = self.viewControllers.details.getView(
|
|
|
|
MciViewIds.details.navMenu
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
navMenu.setFocusItemIndex(0);
|
|
|
|
|
|
|
|
navMenu.on('index update', index => {
|
|
|
|
const sectionName = {
|
2022-06-05 20:04:25 +00:00
|
|
|
0: 'general',
|
|
|
|
1: 'nfo',
|
|
|
|
2: 'fileList',
|
2018-06-22 05:15:04 +00:00
|
|
|
}[index];
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (sectionName) {
|
2018-06-22 05:15:04 +00:00
|
|
|
self.displayDetailsSection(sectionName, true);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return callback(null);
|
2022-06-05 20:04:25 +00:00
|
|
|
},
|
2018-06-22 05:15:04 +00:00
|
|
|
],
|
|
|
|
err => {
|
|
|
|
return cb(err);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
displayHelpPage(cb) {
|
2022-06-05 20:04:25 +00:00
|
|
|
this.displayAsset(this.menuConfig.config.art.help, { clearScreen: true }, () => {
|
|
|
|
this.client.waitForKeyPress(() => {
|
|
|
|
return this.displayBrowsePage(true, cb);
|
|
|
|
});
|
|
|
|
});
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
2020-11-27 23:44:40 +00:00
|
|
|
_handleMovementKeyPress(keyName, cb) {
|
2022-06-05 20:04:25 +00:00
|
|
|
const descView = this.viewControllers.browse.getView(MciViewIds.browse.desc);
|
2020-11-27 23:44:40 +00:00
|
|
|
if (!descView) {
|
|
|
|
return cb(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (keyName) {
|
2022-06-05 20:04:25 +00:00
|
|
|
case 'down arrow':
|
|
|
|
descView.scrollDocumentUp();
|
|
|
|
break;
|
|
|
|
case 'up arrow':
|
|
|
|
descView.scrollDocumentDown();
|
|
|
|
break;
|
|
|
|
case 'page up':
|
|
|
|
descView.keyPressPageUp();
|
|
|
|
break;
|
|
|
|
case 'page down':
|
|
|
|
descView.keyPressPageDown();
|
|
|
|
break;
|
2020-11-27 23:44:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this.viewControllers.browse.switchFocus(MciViewIds.browse.navMenu);
|
|
|
|
return cb(null);
|
|
|
|
}
|
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
fetchAndDisplayWebDownloadLink(cb) {
|
|
|
|
const self = this;
|
|
|
|
|
|
|
|
async.series(
|
|
|
|
[
|
|
|
|
function generateLinkIfNeeded(callback) {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (self.currentFileEntry.webDlExpireTime < moment()) {
|
2018-06-22 05:15:04 +00:00
|
|
|
return callback(null);
|
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
const expireTime = moment().add(
|
|
|
|
Config().fileBase.web.expireMinutes,
|
|
|
|
'minutes'
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
|
|
|
|
FileAreaWeb.createAndServeTempDownload(
|
|
|
|
self.client,
|
|
|
|
self.currentFileEntry,
|
2022-06-05 20:04:25 +00:00
|
|
|
{ expireTime: expireTime },
|
2018-06-22 05:15:04 +00:00
|
|
|
(err, url) => {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (err) {
|
2018-06-22 05:15:04 +00:00
|
|
|
return callback(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.currentFileEntry.webDlExpireTime = expireTime;
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
const webDlExpireTimeFormat =
|
|
|
|
self.menuConfig.config.webDlExpireTimeFormat ||
|
|
|
|
'YYYY-MMM-DD @ h:mm';
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
self.currentFileEntry.entryInfo.webDlLink =
|
|
|
|
ansi.vtxHyperlink(self.client, url) + url;
|
|
|
|
self.currentFileEntry.entryInfo.webDlExpire =
|
|
|
|
expireTime.format(webDlExpireTimeFormat);
|
2018-06-22 05:15:04 +00:00
|
|
|
|
|
|
|
return callback(null);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
|
|
|
function updateActiveViews(callback) {
|
|
|
|
self.updateCustomViewTextsWithFilter(
|
|
|
|
'browse',
|
2022-06-05 20:04:25 +00:00
|
|
|
MciViewIds.browse.customRangeStart,
|
|
|
|
self.currentFileEntry.entryInfo,
|
|
|
|
{ filter: ['{webDlLink}', '{webDlExpire}'] }
|
2018-06-22 05:15:04 +00:00
|
|
|
);
|
|
|
|
return callback(null);
|
2022-06-05 20:04:25 +00:00
|
|
|
},
|
2018-06-22 05:15:04 +00:00
|
|
|
],
|
|
|
|
err => {
|
|
|
|
return cb(err);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
updateQueueIndicator() {
|
2022-06-05 20:04:25 +00:00
|
|
|
const isQueuedIndicator = this.menuConfig.config.isQueuedIndicator || 'Y';
|
|
|
|
const isNotQueuedIndicator = this.menuConfig.config.isNotQueuedIndicator || 'N';
|
2018-06-22 05:15:04 +00:00
|
|
|
|
|
|
|
this.currentFileEntry.entryInfo.isQueued = stringFormat(
|
2022-06-05 20:04:25 +00:00
|
|
|
this.dlQueue.isQueued(this.currentFileEntry)
|
|
|
|
? isQueuedIndicator
|
|
|
|
: isNotQueuedIndicator
|
2018-06-22 05:15:04 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
this.updateCustomViewTextsWithFilter(
|
|
|
|
'browse',
|
|
|
|
MciViewIds.browse.customRangeStart,
|
|
|
|
this.currentFileEntry.entryInfo,
|
2022-06-05 20:04:25 +00:00
|
|
|
{ filter: ['{isQueued}'] }
|
2018-06-22 05:15:04 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
cacheArchiveEntries(cb) {
|
2018-06-23 03:26:46 +00:00
|
|
|
// check cache
|
2022-06-05 20:04:25 +00:00
|
|
|
if (this.currentFileEntry.archiveEntries) {
|
2018-06-22 05:15:04 +00:00
|
|
|
return cb(null, 'cache');
|
|
|
|
}
|
|
|
|
|
|
|
|
const areaInfo = FileArea.getFileAreaByTag(this.currentFileEntry.areaTag);
|
2022-06-05 20:04:25 +00:00
|
|
|
if (!areaInfo) {
|
2018-06-22 05:15:04 +00:00
|
|
|
return cb(Errors.Invalid('Invalid area tag'));
|
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
const filePath = this.currentFileEntry.filePath;
|
|
|
|
const archiveUtil = ArchiveUtil.getInstance();
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
archiveUtil.listEntries(
|
|
|
|
filePath,
|
|
|
|
this.currentFileEntry.entryInfo.archiveType,
|
|
|
|
(err, entries) => {
|
|
|
|
if (err) {
|
|
|
|
return cb(err);
|
|
|
|
}
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
// assign and add standard "text" member for itemFormat
|
|
|
|
this.currentFileEntry.archiveEntries = entries.map(e =>
|
|
|
|
Object.assign(e, { text: `${e.fileName} (${e.byteSize})` })
|
|
|
|
);
|
|
|
|
return cb(null, 're-cached');
|
|
|
|
}
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
2018-08-05 16:50:47 +00:00
|
|
|
setFileListNoListing(text) {
|
2022-06-05 20:04:25 +00:00
|
|
|
const fileListView = this.viewControllers.detailsFileList.getView(
|
|
|
|
MciViewIds.detailsFileList.fileList
|
|
|
|
);
|
|
|
|
if (fileListView) {
|
2018-08-05 16:50:47 +00:00
|
|
|
fileListView.complexItems = false;
|
2022-06-05 20:04:25 +00:00
|
|
|
fileListView.setItems([text]);
|
2018-08-05 16:50:47 +00:00
|
|
|
fileListView.redraw();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
populateFileListing() {
|
2022-06-05 20:04:25 +00:00
|
|
|
const fileListView = this.viewControllers.detailsFileList.getView(
|
|
|
|
MciViewIds.detailsFileList.fileList
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (this.currentFileEntry.entryInfo.archiveType) {
|
|
|
|
this.cacheArchiveEntries((err, cacheStatus) => {
|
|
|
|
if (err) {
|
2018-08-05 16:50:47 +00:00
|
|
|
return this.setFileListNoListing('Failed to get file listing');
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if ('re-cached' === cacheStatus) {
|
2018-07-28 19:49:37 +00:00
|
|
|
fileListView.setItems(this.currentFileEntry.archiveEntries);
|
2018-06-22 05:15:04 +00:00
|
|
|
fileListView.redraw();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
2022-06-05 20:04:25 +00:00
|
|
|
const notAnArchiveFileName = stringFormat(
|
|
|
|
this.menuConfig.config.notAnArchiveFormat || 'Not an archive',
|
|
|
|
{ fileName: this.currentFileEntry.fileName }
|
|
|
|
);
|
2018-08-05 16:50:47 +00:00
|
|
|
this.setFileListNoListing(notAnArchiveFileName);
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
displayDetailsSection(sectionName, clearArea, cb) {
|
2022-06-05 20:04:25 +00:00
|
|
|
const self = this;
|
|
|
|
const name = `details${_.upperFirst(sectionName)}`;
|
2018-06-22 05:15:04 +00:00
|
|
|
|
|
|
|
async.series(
|
|
|
|
[
|
|
|
|
function detachPrevious(callback) {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (self.lastDetailsViewController) {
|
2018-06-22 05:15:04 +00:00
|
|
|
self.lastDetailsViewController.detachClientEvents();
|
|
|
|
}
|
|
|
|
return callback(null);
|
|
|
|
},
|
|
|
|
function prepArtAndViewController(callback) {
|
|
|
|
function gotoTopPos() {
|
2022-06-05 20:04:25 +00:00
|
|
|
self.client.term.rawWrite(
|
|
|
|
ansi.goto(self.detailsInfoArea.top[0], 1)
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
gotoTopPos();
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (clearArea) {
|
2018-06-22 05:15:04 +00:00
|
|
|
self.client.term.rawWrite(ansi.reset());
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
let pos = self.detailsInfoArea.top[0];
|
|
|
|
const bottom = self.detailsInfoArea.bottom[0];
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
while (pos++ <= bottom) {
|
2018-06-22 05:15:04 +00:00
|
|
|
self.client.term.rawWrite(ansi.eraseLine() + ansi.down());
|
|
|
|
}
|
|
|
|
|
|
|
|
gotoTopPos();
|
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
return self.displayArtAndPrepViewController(
|
|
|
|
name,
|
|
|
|
{ clearScreen: false, noInput: true },
|
|
|
|
callback
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
},
|
|
|
|
function populateViews(callback) {
|
|
|
|
self.lastDetailsViewController = self.viewControllers[name];
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
switch (sectionName) {
|
|
|
|
case 'nfo':
|
2018-06-22 05:15:04 +00:00
|
|
|
{
|
2022-06-05 20:04:25 +00:00
|
|
|
const nfoView = self.viewControllers.detailsNfo.getView(
|
|
|
|
MciViewIds.detailsNfo.nfo
|
|
|
|
);
|
|
|
|
if (!nfoView) {
|
2018-06-22 05:15:04 +00:00
|
|
|
return callback(null);
|
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (isAnsi(self.currentFileEntry.entryInfo.descLong)) {
|
2018-06-22 05:15:04 +00:00
|
|
|
nfoView.setAnsi(
|
|
|
|
self.currentFileEntry.entryInfo.descLong,
|
|
|
|
{
|
2022-06-05 20:04:25 +00:00
|
|
|
prepped: false,
|
|
|
|
forceLineTerm: true,
|
2018-06-22 05:15:04 +00:00
|
|
|
},
|
|
|
|
() => {
|
|
|
|
return callback(null);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
} else {
|
2022-06-05 20:04:25 +00:00
|
|
|
nfoView.setText(
|
|
|
|
self.currentFileEntry.entryInfo.descLong
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
return callback(null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
case 'fileList':
|
2018-06-22 05:15:04 +00:00
|
|
|
self.populateFileListing();
|
|
|
|
return callback(null);
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
default:
|
2018-06-22 05:15:04 +00:00
|
|
|
return callback(null);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
function setLabels(callback) {
|
|
|
|
self.populateCustomLabels(name, MciViewIds[name].customRangeStart);
|
|
|
|
return callback(null);
|
2022-06-05 20:04:25 +00:00
|
|
|
},
|
2018-06-22 05:15:04 +00:00
|
|
|
],
|
|
|
|
err => {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (cb) {
|
2018-06-22 05:15:04 +00:00
|
|
|
return cb(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
loadFileIds(force, cb) {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (
|
|
|
|
force ||
|
|
|
|
_.isUndefined(this.fileList) ||
|
|
|
|
_.isUndefined(this.fileListPosition)
|
|
|
|
) {
|
|
|
|
this.fileListPosition = 0;
|
2018-06-22 05:15:04 +00:00
|
|
|
|
|
|
|
const filterCriteria = Object.assign({}, this.filterCriteria);
|
2022-06-05 20:04:25 +00:00
|
|
|
if (!filterCriteria.areaTag) {
|
2018-06-22 05:15:04 +00:00
|
|
|
filterCriteria.areaTag = FileArea.getAvailableFileAreaTags(this.client);
|
|
|
|
}
|
|
|
|
|
|
|
|
FileEntry.findFiles(filterCriteria, (err, fileIds) => {
|
2020-11-29 21:30:18 +00:00
|
|
|
this.fileList = fileIds || [];
|
2018-06-22 05:15:04 +00:00
|
|
|
return cb(err);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2016-09-29 03:54:25 +00:00
|
|
|
};
|