* Lots of improvements (WIP) to file browsing
This commit is contained in:
parent
67e2ff987f
commit
806e6539f4
|
@ -266,7 +266,7 @@ module.exports = class ArchiveUtil {
|
||||||
while((m = entryMatchRe.exec(output))) {
|
while((m = entryMatchRe.exec(output))) {
|
||||||
// :TODO: allow alternate ordering!!!
|
// :TODO: allow alternate ordering!!!
|
||||||
entries.push({
|
entries.push({
|
||||||
size : m[1],
|
byteSize : parseInt(m[1]),
|
||||||
fileName : m[2],
|
fileName : m[2],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -239,24 +239,28 @@ function getDefaultConfig() {
|
||||||
offset : 0,
|
offset : 0,
|
||||||
exts : [ 'zip' ],
|
exts : [ 'zip' ],
|
||||||
handler : '7Zip',
|
handler : '7Zip',
|
||||||
|
desc : 'ZIP Archive',
|
||||||
},
|
},
|
||||||
'7z' : {
|
'7z' : {
|
||||||
sig : '377abcaf271c',
|
sig : '377abcaf271c',
|
||||||
offset : 0,
|
offset : 0,
|
||||||
exts : [ '7z' ],
|
exts : [ '7z' ],
|
||||||
handler : '7Zip',
|
handler : '7Zip',
|
||||||
|
desc : '7-Zip Archive',
|
||||||
},
|
},
|
||||||
arj : {
|
arj : {
|
||||||
sig : '60ea',
|
sig : '60ea',
|
||||||
offset : 0,
|
offset : 0,
|
||||||
exts : [ 'arj' ],
|
exts : [ 'arj' ],
|
||||||
handler : '7Zip',
|
handler : '7Zip',
|
||||||
|
desc : 'ARJ Archive',
|
||||||
},
|
},
|
||||||
rar : {
|
rar : {
|
||||||
sig : '526172211a0700',
|
sig : '526172211a0700',
|
||||||
offset : 0,
|
offset : 0,
|
||||||
exts : [ 'rar' ],
|
exts : [ 'rar' ],
|
||||||
handler : '7Zip',
|
handler : '7Zip',
|
||||||
|
desc : 'RAR Archive',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -339,7 +343,11 @@ function getDefaultConfig() {
|
||||||
areaStoragePrefix : paths.join(__dirname, './../file_base/'),
|
areaStoragePrefix : paths.join(__dirname, './../file_base/'),
|
||||||
|
|
||||||
fileNamePatterns: {
|
fileNamePatterns: {
|
||||||
shortDesc : [ '^FILE_ID\.DIZ$', '^DESC\.SDI$' ],
|
// These are NOT case sensitive
|
||||||
|
shortDesc : [
|
||||||
|
'^FILE_ID\.DIZ$', '^DESC\.SDI$', '^DESCRIPT\.ION$', '^FILE\.DES$', '$FILE\.SDI$', '^DISK\.ID$'
|
||||||
|
],
|
||||||
|
|
||||||
longDesc : [ '^.*\.NFO$', '^README\.1ST$', '^README\.TXT$' ],
|
longDesc : [ '^.*\.NFO$', '^README\.1ST$', '^README\.TXT$' ],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -349,7 +357,7 @@ function getDefaultConfig() {
|
||||||
// The year may be YY or YYYY
|
// The year may be YY or YYYY
|
||||||
//
|
//
|
||||||
'[0-3]?[0-9][\\-\\/\\.][0-3]?[0-9][\\-\\/\\.]((?:[0-9]{2})?[0-9]{2})', // m/d/yyyy, mm-dd-yyyy, etc.
|
'[0-3]?[0-9][\\-\\/\\.][0-3]?[0-9][\\-\\/\\.]((?:[0-9]{2})?[0-9]{2})', // m/d/yyyy, mm-dd-yyyy, etc.
|
||||||
"\\B('[1789][0-9])\\b",
|
"\\B('[1789][0-9])\\b", // eslint-disable-line quotes
|
||||||
// :TODO: DD/MMM/YY, DD/MMMM/YY, DD/MMM/YYYY, etc.
|
// :TODO: DD/MMM/YY, DD/MMMM/YY, DD/MMM/YYYY, etc.
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ const iconv = require('iconv-lite');
|
||||||
|
|
||||||
exports.getAvailableFileAreas = getAvailableFileAreas;
|
exports.getAvailableFileAreas = getAvailableFileAreas;
|
||||||
exports.getSortedAvailableFileAreas = getSortedAvailableFileAreas;
|
exports.getSortedAvailableFileAreas = getSortedAvailableFileAreas;
|
||||||
exports.getDefaultFileArea = getDefaultFileArea;
|
exports.getDefaultFileAreaTag = getDefaultFileAreaTag;
|
||||||
exports.getFileAreaByTag = getFileAreaByTag;
|
exports.getFileAreaByTag = getFileAreaByTag;
|
||||||
exports.changeFileAreaWithOptions = changeFileAreaWithOptions;
|
exports.changeFileAreaWithOptions = changeFileAreaWithOptions;
|
||||||
//exports.addOrUpdateFileEntry = addOrUpdateFileEntry;
|
//exports.addOrUpdateFileEntry = addOrUpdateFileEntry;
|
||||||
|
@ -45,18 +45,20 @@ function getAvailableFileAreas(client, options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSortedAvailableFileAreas(client, options) {
|
function getSortedAvailableFileAreas(client, options) {
|
||||||
const areas = _.map(getAvailableFileAreas(client, options), (v, k) => {
|
const areas = _.map(getAvailableFileAreas(client, options), (v, k) => {
|
||||||
return {
|
const areaInfo = {
|
||||||
areaTag : k,
|
areaTag : k,
|
||||||
area : v
|
area : v
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return areaInfo;
|
||||||
});
|
});
|
||||||
|
|
||||||
sortAreasOrConfs(areas, 'area');
|
sortAreasOrConfs(areas, 'area');
|
||||||
return areas;
|
return areas;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDefaultFileArea(client, disableAcsCheck) {
|
function getDefaultFileAreaTag(client, disableAcsCheck) {
|
||||||
let defaultArea = _.findKey(Config.fileAreas, o => o.default);
|
let defaultArea = _.findKey(Config.fileAreas, o => o.default);
|
||||||
if(defaultArea) {
|
if(defaultArea) {
|
||||||
const area = Config.fileAreas.areas[defaultArea];
|
const area = Config.fileAreas.areas[defaultArea];
|
||||||
|
@ -76,7 +78,8 @@ function getDefaultFileArea(client, disableAcsCheck) {
|
||||||
function getFileAreaByTag(areaTag) {
|
function getFileAreaByTag(areaTag) {
|
||||||
const areaInfo = Config.fileAreas.areas[areaTag];
|
const areaInfo = Config.fileAreas.areas[areaTag];
|
||||||
if(areaInfo) {
|
if(areaInfo) {
|
||||||
areaInfo.areaTag = areaTag; // convienence!
|
areaInfo.areaTag = areaTag; // convienence!
|
||||||
|
areaInfo.storageDirectory = getAreaStorageDirectory(areaInfo);
|
||||||
return areaInfo;
|
return areaInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,7 +180,20 @@ function attemptSetEstimatedReleaseDate(fileEntry) {
|
||||||
//
|
//
|
||||||
const match = getMatch(fileEntry.desc) || getMatch(fileEntry.descLong);
|
const match = getMatch(fileEntry.desc) || getMatch(fileEntry.descLong);
|
||||||
if(match && match[1]) {
|
if(match && match[1]) {
|
||||||
const year = (2 === match[1].length) ? parseInt('19' + match[1]) : parseInt(match[1]);
|
let year;
|
||||||
|
if(2 === match[1].length) {
|
||||||
|
year = parseInt(match[1]);
|
||||||
|
if(year) {
|
||||||
|
if(year > 70) {
|
||||||
|
year += 1900;
|
||||||
|
} else {
|
||||||
|
year += 2000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
year = parseInt(match[1]);
|
||||||
|
}
|
||||||
|
|
||||||
if(year) {
|
if(year) {
|
||||||
fileEntry.meta.est_release_year = year;
|
fileEntry.meta.est_release_year = year;
|
||||||
}
|
}
|
||||||
|
@ -290,14 +306,18 @@ function addNewFileEntry(fileEntry, filePath, cb) {
|
||||||
function populateInfo(callback) {
|
function populateInfo(callback) {
|
||||||
archiveUtil.detectType(filePath, (err, archiveType) => {
|
archiveUtil.detectType(filePath, (err, archiveType) => {
|
||||||
if(archiveType) {
|
if(archiveType) {
|
||||||
|
// save this off
|
||||||
|
fileEntry.meta.archive_type = archiveType;
|
||||||
|
|
||||||
populateFileEntryWithArchive(fileEntry, filePath, archiveType, err => {
|
populateFileEntryWithArchive(fileEntry, filePath, archiveType, err => {
|
||||||
if(err) {
|
if(err) {
|
||||||
populateFileEntry(fileEntry, filePath, err => {
|
populateFileEntry(fileEntry, filePath, err => {
|
||||||
// :TODO: log err
|
// :TODO: log err
|
||||||
return callback(null); // ignore err
|
return callback(null); // ignore err
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
return callback(null);
|
||||||
}
|
}
|
||||||
return callback(null);
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
populateFileEntry(fileEntry, filePath, err => {
|
populateFileEntry(fileEntry, filePath, err => {
|
||||||
|
@ -310,7 +330,10 @@ function addNewFileEntry(fileEntry, filePath, cb) {
|
||||||
function addNewDbRecord(callback) {
|
function addNewDbRecord(callback) {
|
||||||
return fileEntry.persist(callback);
|
return fileEntry.persist(callback);
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
err => {
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,7 +394,7 @@ function addOrUpdateFileEntry(areaInfo, fileName, options, cb) {
|
||||||
return callback(err, existingEntries);
|
return callback(err, existingEntries);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function addOrUpdate(callback, existingEntries) {
|
function addOrUpdate(existingEntries, callback) {
|
||||||
if(existingEntries.length > 0) {
|
if(existingEntries.length > 0) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -396,7 +419,7 @@ function scanFileAreaForChanges(areaInfo, cb) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
async.each(files, (fileName, next) => {
|
async.eachSeries(files, (fileName, next) => {
|
||||||
const fullPath = paths.join(areaPhysDir, fileName);
|
const fullPath = paths.join(areaPhysDir, fileName);
|
||||||
|
|
||||||
fs.stat(fullPath, (err, stats) => {
|
fs.stat(fullPath, (err, stats) => {
|
||||||
|
@ -409,8 +432,8 @@ function scanFileAreaForChanges(areaInfo, cb) {
|
||||||
return next(null);
|
return next(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
addOrUpdateFileEntry(areaInfo, fileName, err => {
|
addOrUpdateFileEntry(areaInfo, fileName, { areaTag : areaInfo.areaTag }, err => {
|
||||||
|
return next(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, err => {
|
}, err => {
|
||||||
|
|
|
@ -25,6 +25,7 @@ const FILE_WELL_KNOWN_META = {
|
||||||
dl_count : (d) => parseInt(d) || 0,
|
dl_count : (d) => parseInt(d) || 0,
|
||||||
byte_size : (b) => parseInt(b) || 0,
|
byte_size : (b) => parseInt(b) || 0,
|
||||||
user_rating : (r) => Math.min(parseInt(r) || 0, 5),
|
user_rating : (r) => Math.min(parseInt(r) || 0, 5),
|
||||||
|
archive_type : null,
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = class FileEntry {
|
module.exports = class FileEntry {
|
||||||
|
@ -33,8 +34,13 @@ module.exports = class FileEntry {
|
||||||
|
|
||||||
this.fileId = options.fileId || 0;
|
this.fileId = options.fileId || 0;
|
||||||
this.areaTag = options.areaTag || '';
|
this.areaTag = options.areaTag || '';
|
||||||
this.meta = {};
|
this.meta = options.meta || {
|
||||||
this.hashTags = new Set();
|
// values we always want
|
||||||
|
user_rating : 0,
|
||||||
|
dl_count : 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.hashTags = options.hashTags || new Set();
|
||||||
this.fileName = options.fileName;
|
this.fileName = options.fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,30 +113,39 @@ HorizontalMenuView.prototype.setItems = function(items) {
|
||||||
this.positionCacheExpired = true;
|
this.positionCacheExpired = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
HorizontalMenuView.prototype.focusNext = function() {
|
||||||
|
if(this.items.length - 1 === this.focusedItemIndex) {
|
||||||
|
this.focusedItemIndex = 0;
|
||||||
|
} else {
|
||||||
|
this.focusedItemIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// :TODO: Optimize this in cases where we only need to redraw two items. Always the case now, somtimes
|
||||||
|
this.redraw();
|
||||||
|
|
||||||
|
HorizontalMenuView.super_.prototype.focusNext.call(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
HorizontalMenuView.prototype.focusPrevious = function() {
|
||||||
|
|
||||||
|
if(0 === this.focusedItemIndex) {
|
||||||
|
this.focusedItemIndex = this.items.length - 1;
|
||||||
|
} else {
|
||||||
|
this.focusedItemIndex--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// :TODO: Optimize this in cases where we only need to redraw two items. Always the case now, somtimes
|
||||||
|
this.redraw();
|
||||||
|
|
||||||
|
HorizontalMenuView.super_.prototype.focusPrevious.call(this);
|
||||||
|
};
|
||||||
|
|
||||||
HorizontalMenuView.prototype.onKeyPress = function(ch, key) {
|
HorizontalMenuView.prototype.onKeyPress = function(ch, key) {
|
||||||
if(key) {
|
if(key) {
|
||||||
var prevFocusedItemIndex = this.focusedItemIndex;
|
|
||||||
|
|
||||||
if(this.isKeyMapped('left', key.name)) {
|
if(this.isKeyMapped('left', key.name)) {
|
||||||
if(0 === this.focusedItemIndex) {
|
this.focusPrevious();
|
||||||
this.focusedItemIndex = this.items.length - 1;
|
|
||||||
} else {
|
|
||||||
this.focusedItemIndex--;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if(this.isKeyMapped('right', key.name)) {
|
} else if(this.isKeyMapped('right', key.name)) {
|
||||||
if(this.items.length - 1 === this.focusedItemIndex) {
|
this.focusNext();
|
||||||
this.focusedItemIndex = 0;
|
|
||||||
} else {
|
|
||||||
this.focusedItemIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(prevFocusedItemIndex !== this.focusedItemIndex) {
|
|
||||||
// :TODO: Optimize this in cases where we only need to redraw two items. Always the case now, somtimes
|
|
||||||
// if this is changed to allow scrolling
|
|
||||||
this.redraw();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -143,8 +143,10 @@ function ViewController(options) {
|
||||||
var mci = mciMap[name];
|
var mci = mciMap[name];
|
||||||
var view = self.mciViewFactory.createFromMCI(mci);
|
var view = self.mciViewFactory.createFromMCI(mci);
|
||||||
|
|
||||||
if(view && false === self.noInput) {
|
if(view) {
|
||||||
view.on('action', self.viewActionListener);
|
if(false === self.noInput) {
|
||||||
|
view.on('action', self.viewActionListener);
|
||||||
|
}
|
||||||
|
|
||||||
self.addView(view);
|
self.addView(view);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,15 @@ const theme = require('../core/theme.js');
|
||||||
const FileEntry = require('../core/file_entry.js');
|
const FileEntry = require('../core/file_entry.js');
|
||||||
const stringFormat = require('../core/string_format.js');
|
const stringFormat = require('../core/string_format.js');
|
||||||
const FileArea = require('../core/file_area.js');
|
const FileArea = require('../core/file_area.js');
|
||||||
|
const Errors = require('../core/enig_error.js').Errors;
|
||||||
|
const ArchiveUtil = require('../core/archive_util.js');
|
||||||
|
const Config = require('../core/config.js').config;
|
||||||
|
|
||||||
// deps
|
// deps
|
||||||
const async = require('async');
|
const async = require('async');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
const paths = require('path');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Misc TODO
|
Misc TODO
|
||||||
|
@ -41,15 +45,35 @@ exports.moduleInfo = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const FormIds = {
|
const FormIds = {
|
||||||
browse : 0,
|
browse : 0,
|
||||||
details : 1,
|
details : 1,
|
||||||
|
detailsGeneral : 2,
|
||||||
|
detailsNfo : 3,
|
||||||
|
detailsFileList : 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MciViewIds = {
|
const MciViewIds = {
|
||||||
browse : {
|
browse : {
|
||||||
desc : 1,
|
desc : 1,
|
||||||
navMenu : 2,
|
navMenu : 2,
|
||||||
// 10+: customs
|
// 10+ = customs
|
||||||
|
},
|
||||||
|
details : {
|
||||||
|
navMenu : 1,
|
||||||
|
infoXyTop : 2, // %XY starting position for info area
|
||||||
|
infoXyBottom : 3,
|
||||||
|
// 10+ = customs
|
||||||
|
},
|
||||||
|
detailsGeneral : {
|
||||||
|
// 10+ = customs
|
||||||
|
},
|
||||||
|
detailsNfo : {
|
||||||
|
nfo : 1,
|
||||||
|
// 10+ = customs
|
||||||
|
},
|
||||||
|
detailsFileList : {
|
||||||
|
fileList : 1,
|
||||||
|
// 10+ = customs
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -69,6 +93,39 @@ exports.getModule = class FileAreaList extends MenuModule {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.currentFileEntry = new FileEntry();
|
this.currentFileEntry = new FileEntry();
|
||||||
|
|
||||||
|
this.menuMethods = {
|
||||||
|
nextFile : (formData, extraArgs, cb) => {
|
||||||
|
if(this.fileListPosition + 1 < this.fileList.length) {
|
||||||
|
this.fileListPosition += 1;
|
||||||
|
|
||||||
|
delete this.currentFileEntry.archiveEntries;
|
||||||
|
|
||||||
|
return this.displayBrowsePage(true, cb); // true=clerarScreen
|
||||||
|
}
|
||||||
|
|
||||||
|
return cb(null);
|
||||||
|
},
|
||||||
|
prevFile : (formData, extraArgs, cb) => {
|
||||||
|
if(this.fileListPosition > 0) {
|
||||||
|
--this.fileListPosition;
|
||||||
|
|
||||||
|
delete this.currentFileEntry.archiveEntries;
|
||||||
|
|
||||||
|
return this.displayBrowsePage(true, cb); // true=clearScreen
|
||||||
|
}
|
||||||
|
|
||||||
|
return cb(null);
|
||||||
|
},
|
||||||
|
viewDetails : (formData, extraArgs, cb) => {
|
||||||
|
this.viewControllers.browse.setFocus(false);
|
||||||
|
return this.displayDetailsPage(cb);
|
||||||
|
},
|
||||||
|
detailsQuit : (formData, extraArgs, cb) => {
|
||||||
|
this.viewControllers.details.setFocus(false);
|
||||||
|
return this.displayBrowsePage(true, cb); // true=clearScreen
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
enter() {
|
enter() {
|
||||||
|
@ -97,19 +154,87 @@ exports.getModule = class FileAreaList extends MenuModule {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
displayBrowsePage(clearScreen, cb) {
|
populateCurrentEntryInfo() {
|
||||||
|
const config = this.menuConfig.config;
|
||||||
|
const currEntry = this.currentFileEntry;
|
||||||
|
|
||||||
|
const uploadTimestampFormat = config.browseUploadTimestampFormat || config.uploadTimestampFormat || 'YYYY-MMM-DD';
|
||||||
|
const area = FileArea.getFileAreaByTag(currEntry.areaTag);
|
||||||
|
const hashTagsSep = config.hashTagsSep || ', ';
|
||||||
|
|
||||||
|
const entryInfo = this.currentFileEntry.entryInfo = {
|
||||||
|
fileId : currEntry.fileId,
|
||||||
|
areaTag : currEntry.areaTag,
|
||||||
|
areaName : area.name || 'N/A',
|
||||||
|
areaDesc : area.desc || 'N/A',
|
||||||
|
fileSha1 : currEntry.fileSha1,
|
||||||
|
fileName : currEntry.fileName,
|
||||||
|
desc : currEntry.desc || '',
|
||||||
|
descLong : currEntry.descLong || '',
|
||||||
|
uploadTimestamp : moment(currEntry.uploadTimestamp).format(uploadTimestampFormat),
|
||||||
|
hashTags : Array.from(currEntry.hashTags).join(hashTagsSep),
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// We need the entry object to contain meta keys even if they are empty as
|
||||||
|
// consumers may very likely attempt to use them
|
||||||
|
//
|
||||||
|
const metaValues = FileEntry.getWellKnownMetaValues();
|
||||||
|
metaValues.forEach(name => {
|
||||||
|
const value = !_.isUndefined(currEntry.meta[name]) ? currEntry.meta[name] : '';
|
||||||
|
entryInfo[_.camelCase(name)] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(entryInfo.archiveType) {
|
||||||
|
entryInfo.archiveTypeDesc = _.has(Config, [ 'archives', 'formats', entryInfo.archiveType, 'desc' ]) ?
|
||||||
|
Config.archives.formats[entryInfo.archiveType].desc :
|
||||||
|
entryInfo.archiveType;
|
||||||
|
} else {
|
||||||
|
entryInfo.archiveTypeDesc = 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
|
entryInfo.uploadByUsername = entryInfo.uploadByUsername || 'N/A'; // may be imported
|
||||||
|
|
||||||
|
// create a rating string, e.g. "**---"
|
||||||
|
const userRatingTicked = config.userRatingTicked || '*';
|
||||||
|
const userRatingUnticked = config.userRatingUnticked || '';
|
||||||
|
entryInfo.userRating = entryInfo.userRating || 0; // be safe!
|
||||||
|
entryInfo.userRatingString = new Array(entryInfo.userRating + 1).join(userRatingTicked);
|
||||||
|
if(entryInfo.userRating < 5) {
|
||||||
|
entryInfo.userRatingString += new Array( (5 - entryInfo.userRating) + 1).join(userRatingUnticked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
populateCustomLabels(category, startId) {
|
||||||
|
let textView;
|
||||||
|
let customMciId = startId;
|
||||||
|
const config = this.menuConfig.config;
|
||||||
|
|
||||||
|
while( (textView = this.viewControllers[category].getView(customMciId)) ) {
|
||||||
|
const key = `${category}InfoFormat${customMciId}`;
|
||||||
|
const format = config[key];
|
||||||
|
|
||||||
|
if(format) {
|
||||||
|
textView.setText(stringFormat(format, this.currentFileEntry.entryInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
++customMciId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
displayArtAndPrepViewController(name, options, cb) {
|
||||||
const self = this;
|
const self = this;
|
||||||
const config = this.menuConfig.config;
|
const config = this.menuConfig.config;
|
||||||
|
|
||||||
async.waterfall(
|
async.waterfall(
|
||||||
[
|
[
|
||||||
function clearAndDisplayArt(callback) {
|
function readyAndDisplayArt(callback) {
|
||||||
if (clearScreen) {
|
if(options.clearScreen) {
|
||||||
self.client.term.rawWrite(ansi.resetScreen());
|
self.client.term.rawWrite(ansi.clearScreen());
|
||||||
}
|
}
|
||||||
|
|
||||||
theme.displayThemedAsset(
|
theme.displayThemedAsset(
|
||||||
config.art.browse,
|
config.art[name],
|
||||||
self.client,
|
self.client,
|
||||||
{ font : self.menuConfig.font, trailingLF : false },
|
{ font : self.menuConfig.font, trailingLF : false },
|
||||||
(err, artData) => {
|
(err, artData) => {
|
||||||
|
@ -118,31 +243,66 @@ exports.getModule = class FileAreaList extends MenuModule {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
function prepeareViewController(artData, callback) {
|
function prepeareViewController(artData, callback) {
|
||||||
if(_.isUndefined(self.viewControllers.browse)) {
|
if(_.isUndefined(self.viewControllers[name])) {
|
||||||
const vc = self.addViewController(
|
const vcOpts = {
|
||||||
'browse',
|
client : self.client,
|
||||||
new ViewController( { client : self.client, formId : FormIds.browse } )
|
formId : FormIds[name],
|
||||||
);
|
};
|
||||||
|
|
||||||
|
if(!_.isUndefined(options.noInput)) {
|
||||||
|
vcOpts.noInput = options.noInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
const vc = self.addViewController(name, new ViewController(vcOpts));
|
||||||
|
|
||||||
|
if('details' === name) {
|
||||||
|
try {
|
||||||
|
self.detailsInfoArea = {
|
||||||
|
top : artData.mciMap.XY2.position,
|
||||||
|
bottom : artData.mciMap.XY3.position,
|
||||||
|
};
|
||||||
|
} catch(e) {
|
||||||
|
return callback(Errors.DoesNotExist('Missing XY2 and XY3 position indicators!'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const loadOpts = {
|
const loadOpts = {
|
||||||
callingMenu : self,
|
callingMenu : self,
|
||||||
mciMap : artData.mciMap,
|
mciMap : artData.mciMap,
|
||||||
formId : FormIds.browse,
|
formId : FormIds[name],
|
||||||
};
|
};
|
||||||
|
|
||||||
return vc.loadFromMenuConfig(loadOpts, callback);
|
return vc.loadFromMenuConfig(loadOpts, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.viewControllers[name].setFocus(true);
|
||||||
|
return callback(null);
|
||||||
|
|
||||||
|
},
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
self.viewControllers.view.setFocus(true);
|
displayBrowsePage(clearScreen, cb) {
|
||||||
self.viewControllers.view.getView(MciViewIds.view.BBSList).redraw();
|
const self = this;
|
||||||
|
|
||||||
return callback(null);
|
async.series(
|
||||||
|
[
|
||||||
|
function prepArtAndViewController(callback) {
|
||||||
|
return self.displayArtAndPrepViewController('browse', { clearScreen : clearScreen }, callback);
|
||||||
},
|
},
|
||||||
function fetchEntryData(callback) {
|
function fetchEntryData(callback) {
|
||||||
|
if(self.fileList) {
|
||||||
|
return callback(null);
|
||||||
|
}
|
||||||
return self.loadFileIds(callback);
|
return self.loadFileIds(callback);
|
||||||
},
|
},
|
||||||
function loadCurrentFileInfo(callback) {
|
function loadCurrentFileInfo(callback) {
|
||||||
self.currentFileEntry.load( self.fileList[ self.fileListPosition ], err => {
|
self.currentFileEntry.load( self.fileList[ self.fileListPosition ], err => {
|
||||||
|
self.populateCurrentEntryInfo();
|
||||||
return callback(err);
|
return callback(err);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -153,58 +313,164 @@ exports.getModule = class FileAreaList extends MenuModule {
|
||||||
descView.setText(self.currentFileEntry.desc);
|
descView.setText(self.currentFileEntry.desc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const currEntry = self.currentFileEntry;
|
|
||||||
const uploadTimestampFormat = config.browseUploadTimestampFormat || config.uploadTimestampFormat || 'YYYY-MMM-DD';
|
|
||||||
const area = FileArea.getFileAreaByTag(currEntry.areaTag);
|
|
||||||
const hashTagsSep = config.hashTagsSep || ', ';
|
|
||||||
|
|
||||||
const entryInfo = {
|
|
||||||
fileId : currEntry.fileId,
|
|
||||||
areaTag : currEntry.areaTag,
|
|
||||||
areaName : area.name || 'N/A',
|
|
||||||
areaDesc : area.desc || 'N/A',
|
|
||||||
fileSha1 : currEntry.fileSha1,
|
|
||||||
fileName : currEntry.fileName,
|
|
||||||
desc : currEntry.desc,
|
|
||||||
descLong : currEntry.descLong,
|
|
||||||
uploadByUsername : currEntry.uploadByUsername,
|
|
||||||
uploadTimestamp : moment(currEntry.uploadTimestamp).format(uploadTimestampFormat),
|
|
||||||
hashTags : Array.from(currEntry.hashTags).join(hashTagsSep),
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
self.populateCustomLabels('browse', 10);
|
||||||
// We need the entry object to contain meta keys even if they are empty as
|
|
||||||
// consumers may very likely attempt to use them
|
return callback(null);
|
||||||
//
|
}
|
||||||
const metaValues = FileEntry.getWellKnownMetaValues();
|
],
|
||||||
metaValues.forEach(name => {
|
err => {
|
||||||
const value = currEntry.meta[name] || '';
|
if(cb) {
|
||||||
entryInfo[_.camelCase(name)] = value;
|
return cb(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
displayDetailsPage(cb) {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
async.series(
|
||||||
|
[
|
||||||
|
function prepArtAndViewController(callback) {
|
||||||
|
return self.displayArtAndPrepViewController('details', { clearScreen : true }, callback);
|
||||||
|
},
|
||||||
|
function populateViews(callback) {
|
||||||
|
self.populateCustomLabels('details', 10);
|
||||||
|
return callback(null);
|
||||||
|
},
|
||||||
|
function prepSection(callback) {
|
||||||
|
return self.displayDetailsSection('general', false, callback);
|
||||||
|
},
|
||||||
|
function listenNavChanges(callback) {
|
||||||
|
const navMenu = self.viewControllers.details.getView(MciViewIds.details.navMenu);
|
||||||
|
navMenu.setFocusItemIndex(0);
|
||||||
|
|
||||||
|
navMenu.on('index update', index => {
|
||||||
|
const sectionName = {
|
||||||
|
0 : 'general',
|
||||||
|
1 : 'nfo',
|
||||||
|
2 : 'fileList',
|
||||||
|
}[index];
|
||||||
|
|
||||||
|
if(sectionName) {
|
||||||
|
self.displayDetailsSection(sectionName, true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const userRatingChar = config.userRatingChar ? config.userRatingChar[0] : '*';
|
return callback(null);
|
||||||
if(_.isNumber(entryInfo.userRating)) {
|
}
|
||||||
entryInfo.userRatingString = new Array(entryInfo.userRating).join(userRatingChar);
|
],
|
||||||
} else {
|
err => {
|
||||||
entryInfo.userRatingString = '';
|
return cb(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheArchiveEntries(cb) {
|
||||||
|
// check cache
|
||||||
|
if(this.currentFileEntry.archiveEntries) {
|
||||||
|
return cb(null, 'cache');
|
||||||
|
}
|
||||||
|
|
||||||
|
const areaInfo = FileArea.getFileAreaByTag(this.currentFileEntry.areaTag);
|
||||||
|
if(!areaInfo) {
|
||||||
|
return cb(Errors.Invalid('Invalid area tag'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = paths.join(areaInfo.storageDirectory, this.currentFileEntry.fileName);
|
||||||
|
const archiveUtil = ArchiveUtil.getInstance();
|
||||||
|
|
||||||
|
archiveUtil.listEntries(filePath, this.currentFileEntry.entryInfo.archiveType, (err, entries) => {
|
||||||
|
if(err) {
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentFileEntry.archiveEntries = entries;
|
||||||
|
return cb(null, 're-cached');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
populateFileListing() {
|
||||||
|
const fileListView = this.viewControllers.detailsFileList.getView(MciViewIds.detailsFileList.fileList);
|
||||||
|
|
||||||
|
if(this.currentFileEntry.entryInfo.archiveType) {
|
||||||
|
this.cacheArchiveEntries( (err, cacheStatus) => {
|
||||||
|
if(err) {
|
||||||
|
// :TODO: Handle me!!!
|
||||||
|
fileListView.setItems( [ 'Failed getting file listing' ] ); // :TODO: make this not suck
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if('re-cached' === cacheStatus) {
|
||||||
|
const fileListEntryFormat = this.menuConfig.config.fileListEntryFormat || '{fileName} {fileSize}';
|
||||||
|
const focusFileListEntryFormat = this.menuConfig.config.focusFileListEntryFormat || fileListEntryFormat;
|
||||||
|
|
||||||
|
fileListView.setItems( this.currentFileEntry.archiveEntries.map( entry => stringFormat(fileListEntryFormat, entry) ) );
|
||||||
|
fileListView.setFocusItems( this.currentFileEntry.archiveEntries.map( entry => stringFormat(focusFileListEntryFormat, entry) ) );
|
||||||
|
|
||||||
|
fileListView.redraw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fileListView.setItems( [ stringFormat(this.menuConfig.config.notAnArchiveFormat || 'Not an archive', { fileName : this.currentFileEntry.fileName } ) ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
displayDetailsSection(sectionName, clearArea, cb) {
|
||||||
|
const self = this;
|
||||||
|
const name = `details${_.capitalize(sectionName)}`;
|
||||||
|
|
||||||
|
async.series(
|
||||||
|
[
|
||||||
|
function detachPrevious(callback) {
|
||||||
|
if(self.lastDetailsViewController) {
|
||||||
|
self.lastDetailsViewController.detachClientEvents();
|
||||||
|
}
|
||||||
|
return callback(null);
|
||||||
|
},
|
||||||
|
function prepArtAndViewController(callback) {
|
||||||
|
|
||||||
|
function gotoTopPos() {
|
||||||
|
self.client.term.rawWrite(ansi.goto(self.detailsInfoArea.top[0], 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10+ are custom textviews
|
gotoTopPos();
|
||||||
let textView;
|
|
||||||
let customMciId = 10;
|
|
||||||
|
|
||||||
while( (textView = self.viewControllers.browse.getView(customMciId)) ) {
|
if(clearArea) {
|
||||||
const key = `browseInfoFormat${customMciId}`;
|
self.client.term.rawWrite(ansi.reset());
|
||||||
const format = config[key];
|
|
||||||
|
|
||||||
if(format) {
|
let pos = self.detailsInfoArea.top[0];
|
||||||
textView.setText(stringFormat(format, entryInfo));
|
const bottom = self.detailsInfoArea.bottom[0];
|
||||||
|
|
||||||
|
while(pos++ <= bottom) {
|
||||||
|
self.client.term.rawWrite(ansi.eraseLine() + ansi.down());
|
||||||
}
|
}
|
||||||
|
|
||||||
++customMciId;
|
gotoTopPos();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return self.displayArtAndPrepViewController(name, { clearScreen : false, noInput : true }, callback);
|
||||||
|
},
|
||||||
|
function populateViews(callback) {
|
||||||
|
self.lastDetailsViewController = self.viewControllers[name];
|
||||||
|
|
||||||
|
switch(sectionName) {
|
||||||
|
case 'nfo' :
|
||||||
|
{
|
||||||
|
const nfoView = self.viewControllers.detailsNfo.getView(MciViewIds.detailsNfo.nfo);
|
||||||
|
if(nfoView) {
|
||||||
|
nfoView.setText(self.currentFileEntry.entryInfo.descLong);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'fileList' :
|
||||||
|
self.populateFileListing();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.populateCustomLabels(name, 10);
|
||||||
return callback(null);
|
return callback(null);
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in New Issue