From 51de982fbb812d94fa52aef4c49b21d88a24ad9e Mon Sep 17 00:00:00 2001 From: Deon George Date: Tue, 3 May 2022 21:10:09 +1000 Subject: [PATCH] Echoarea front page navigation --- load/frame-ansi.js | 8 +-- load/frame-page.js | 21 +++++- load/funcs.js | 160 +++++++++++++++++++++++++++++++++++++++++++-- load/msgbases.js | 56 +++++++++++++++- main.js | 70 +++++++++++++++----- text/198a.tex | 1 + 6 files changed, 284 insertions(+), 32 deletions(-) create mode 100644 text/198a.tex diff --git a/load/frame-ansi.js b/load/frame-ansi.js index ae55f81..f39433f 100644 --- a/load/frame-ansi.js +++ b/load/frame-ansi.js @@ -29,21 +29,21 @@ function FrameAnsi() { this.settings.ERR_ROUTE = '\1n\1h\1WMISTAKE? \1GTRY AGAIN OR TELL US ON *08'; this.settings.ERR_METHOD_NOT_EXIST = '\1n\1h\1WMISTAKE? \1GTRY AGAIN OR TELL US ON *08'; this.settings.ACCESS_DENIED = '\1n\1h\1RACCESS DENIED. MISTAKE? TRY AGAIN OR TELL US *08'; - this.settings.ALREADY_MEMBER = '\1n\1h\1RALREADY MEMBER OF CUG' - this.settings.INACTIVITY = '\1n\1h\1RINACTIVITY ALERT, DISCONNECT PENDING...'; + this.settings.ALREADY_MEMBER = '\1n\1h\1RALREADY MEMBER OF CUG' + this.settings.INACTIVITY = '\1n\1h\1RINACTIVITY ALERT, DISCONNECT PENDING...'; this.settings.INACTIVE = '\1n\1h\1RINACTIVITY DISCONNECT'; this.settings.NOACTION = '\1n\1h\1RNO ACTION PERFORMED'; this.settings.BASESTAR = '\1N\1G\1H*'; this.settings.INVALID_CODE = '\1n\1h\1RINVAID CODE, PLEASE TRY AGAIN *00'; this.settings.TOKEN_EMAIL = '\1n\1h\1RTOKEN EMAILED TO YOU...'; - this.settings.TOKEN_SENT = '\1n\1h\1RTOKEN SENT, PLEASE ENTER TOKEN'; + this.settings.TOKEN_SENT = '\1n\1h\1RTOKEN SENT, PLEASE ENTER TOKEN'; this.settings.INVALID_EMAIL = '\1n\1h\1RINVAID EMAIL, PLEASE TRY AGAIN *00'; this.settings.INVALID_UID = '\1n\1h\1RINVAID USER ID, PLEASE TRY AGAIN *00'; this.settings.CANNOT_SEND_TOKEN = '\1n\1h\1RCANNOT SEND VALIDATION CODE, PLEASE TRY AGAIN *00'; this.settings.USER_EXISTS = '\1n\1h\1RERROR USER EXISTS, PLEASE TRY AGAIN *00'; this.settings.USER_CREATE_ERROR = '\1n\1h\1RERROR CREATING USER, PLEASE TRY AGAIN *00'; this.settings.LOGIN_ERROR = '\1n\1h\1RERROR LOGGING IN, PLEASE TRY AGAIN *00'; - this.settings.CANCEL_MSG = '\1n\1h\1GPRESS 2 TO CANCEL'; + this.settings.CANCEL_MSG = '\1n\1h\1GPRESS 2 TO CANCEL'; /** * Set the attribute at the current position diff --git a/load/frame-page.js b/load/frame-page.js index 21a51b2..49a257a 100644 --- a/load/frame-page.js +++ b/load/frame-page.js @@ -182,7 +182,7 @@ PageFrame.prototype.load = function(filename) { * @param page */ PageFrame.prototype.loadMessage = function(page) { - this.frame = '1'+page; + this.frame = ''+page; this.index = 'a'; this.owner = 1; this.isPublic = true; @@ -193,6 +193,7 @@ PageFrame.prototype.loadMessage = function(page) { // Load our message var ma = new MsgAreas() + var area = ma.getArea(this.frame); var msg = ma.getMessage(this.frame); var msg_header; @@ -231,7 +232,23 @@ PageFrame.prototype.loadMessage = function(page) { //log(LOG_DEBUG,'Loaded message: '+msg_header+msg.content); this.content = base64_encode(msg_header+msg.content); - log(LOG_DEBUG,'Loaded frame: ['+this.frame+']['+this.index+'] ('+this.page+')'); + // Update the user's pointers + var stats = ma.getUserStats(this.frame); + + // if this message is to the user, and the msg number > scan_ptr and it is the next message on the user's new mail list + var newmsgs = area.newMsgs(); + if (newmsgs.length) { + var next = newmsgs.shift(); + + if (next.tag === msg.tag) + stats.scan_ptr = next.number; + } + + // if this message is the next message, update last_read + if (msg.number === stats.last_read+1) + stats.last_read = msg.number; + + log(LOG_DEBUG,'Built frame: ['+this.frame+']['+this.index+'] ('+this.page+')'); } /** diff --git a/load/funcs.js b/load/funcs.js index aeada19..040ff5e 100644 --- a/load/funcs.js +++ b/load/funcs.js @@ -109,13 +109,12 @@ function ans2bin(ansi,frame) { function atcode(field,length,pad,context) { 'use strict'; - length = length ? length : field.length; pad = pad ? pad : ' '; var result = ''; switch(field) { // Get the ECHOAREA FTN AREA_TAG - case 'areatag': + case 'msg_area_areatag': if (typeof context !== 'object') { log(LOG_ERROR,'Unable to render ['+field+'], no context provided'); break; @@ -125,7 +124,7 @@ function atcode(field,length,pad,context) { break; // Get the ECHOAREA Description - case 'areadesc': + case 'msg_area_desc': if (typeof context !== 'object') { log(LOG_ERROR,'Unable to render ['+field+'], no context provided'); break; @@ -134,18 +133,165 @@ function atcode(field,length,pad,context) { result = context.msgbase.cfg.description; break; + // Oldest message in msgarea + case 'msg_area_msgoldest_date': + if (typeof context !== 'object') { + log(LOG_ERROR,'Unable to render ['+field+'], no context provided'); + break; + } + + var x = context.msgbase.first_msg || 1; + + result = x ? context.getMessagePtr(x).date : ''; + break; + + case 'msg_area_msgoldest_page': + if (typeof context !== 'object') { + log(LOG_ERROR,'Unable to render ['+field+'], no context provided'); + break; + } + + var x = context.msgbase.first_msg || 1; + + result = x ? context.getMessagePage(x) : null; + break; + + // Newest message in msgarea + case 'msg_area_msgnewest_date': + if (typeof context !== 'object') { + log(LOG_ERROR,'Unable to render ['+field+'], no context provided'); + break; + } + + result = context.msgbase.last_msg ? context.getMessagePtr(context.msgbase.last_msg).date : ''; + break; + + case 'msg_area_msgnewest_page': + if (typeof context !== 'object') { + log(LOG_ERROR,'Unable to render ['+field+'], no context provided'); + break; + } + + result = context.msgbase.last_msg ? context.getMessagePage(context.msgbase.last_msg) : null; + break; + + // First unread message + case 'msg_area_msgunread_date': + if (typeof context !== 'object') { + log(LOG_ERROR,'Unable to render ['+field+'], no context provided'); + break; + } + + var x = context.getUserStats().last_read+1; + result = (x <= context.msgbase.last_msg) ? context.getMessagePtr(x).date : ''; + break; + + case 'msg_area_msgunread_page': + if (typeof context !== 'object') { + log(LOG_ERROR,'Unable to render ['+field+'], no context provided'); + break; + } + + var x = context.getUserStats().last_read+1; + result = (x <= context.msgbase.last_msg) ? context.getMessagePage(x) : null; + break; + + // First unread message to me + case 'msg_area_msgotome_date': + if (typeof context !== 'object') { + log(LOG_ERROR,'Unable to render ['+field+'], no context provided'); + break; + } + + var x = context.newMsgs(); + + result = x.length ? x.shift().date : ''; + break; + + case 'msg_area_msgtome_page': + if (typeof context !== 'object') { + log(LOG_ERROR,'Unable to render ['+field+'], no context provided'); + break; + } + + var x = context.newMsgs(); + result = x.length ? context.getMessagePage(x.shift().number) : null; + break; + + // Count of unread messages + case 'msg_area_new': + if (typeof context !== 'object') { + log(LOG_ERROR,'Unable to render ['+field+'], no context provided'); + break; + } + + result = ''+(context.msgbase.last_msg-context.getUserStats().last_read); + break; + + // Count of unread messages to me + case 'msg_area_newtome': + if (typeof context !== 'object') { + log(LOG_ERROR,'Unable to render ['+field+'], no context provided'); + break; + } + + result = ''+context.newMsgs().length; + break; + + // Is this message area in my new scan list + case 'msg_area_newscan': + if (typeof context !== 'object') { + log(LOG_ERROR,'Unable to render ['+field+'], no context provided'); + break; + } + + result = (context.getUserStats().scan_ptr & SCAN_CFG_TOYOU) ? 'YES' : 'NO'; + break; + + // Is this message area in my new scan list + case 'msg_area_pending': + if (typeof context !== 'object') { + log(LOG_ERROR,'Unable to render ['+field+'], no context provided'); + break; + } + + result = ''+context.list_untagged.length; + break; + + // Get the ECHOAREA Total Number of Messages + case 'msg_area_total': + if (typeof context !== 'object') { + log(LOG_ERROR,'Unable to render ['+field+'], no context provided'); + break; + } + + result = ''+context.msgbase.total_msgs; + break; + + // Get the ECHOAREA Group Name + case 'msg_grp_name': + if (typeof context !== 'object') { + log(LOG_ERROR,'Unable to render ['+field+'], no context provided'); + break; + } + + result = context.zone_name; + break; + case 'nodeid': result = getNodeID(); break; default: result = bbs.atcode(field); - - if (result === null) - result = ''; } - log(LOG_DEBUG,' - result length ['+result.length+'] desired ('+length+')'); + if ((result === null) || (typeof result === 'undefined')) + result = ''; + + length = length ? length : result.length; + + //log(LOG_DEBUG,' - result length ['+result.length+'] desired ('+length+')'); if (result.length < Math.abs(length)) result = (length < 0) ? padright(result,Math.abs(length),pad) : padleft(result,length,pad); else if (result.length > Math.abs(length)) diff --git a/load/msgbases.js b/load/msgbases.js index d0ccee6..3712d23 100644 --- a/load/msgbases.js +++ b/load/msgbases.js @@ -63,7 +63,7 @@ function MsgArea() { exit(2); } - this.headers = this.msgbase.get_all_msg_headers(false,false); + this.headers = this.msgbase.get_all_msg_headers(false,false) || []; this.msgbase.close(); } }); @@ -159,6 +159,28 @@ function MsgArea() { }); } +/** + * New Messages for the logged in user + */ +MsgArea.prototype.newMsgs = function() { + var msgs = []; + var stats = this.getUserStats(); + //log(LOG_DEBUG,'Users scan pointer: '+JSON.stringify(stats.scan_ptr)); + + for(var x in this.headers) { + // Advance past our last scan_ptr + if (x <= stats.scan_ptr) + continue; + + if ((this.headers[x].to === user.name) || (this.headers[x].to === user.alias)) + msgs.push(this.headers[x]); + + write(); // @todo This is needed for this to work? + } + + return msgs; +} + /** * Get a specific message with a tag */ @@ -190,6 +212,38 @@ MsgArea.prototype.getMessage = function(page) { return msg; } +/** + * Get a message by pointer + * + * @param ptr + */ +MsgArea.prototype.getMessagePtr = function(ptr) { + ptr = ptr || 1; + + return this.headers[ptr]; +} + +/** + * Get a message page by pointer + * + * @param ptr + * @returns {string} + */ +MsgArea.prototype.getMessagePage = function(ptr) { + log(LOG_DEBUG,'Get Message Page with ptr ['+JSON.stringify(ptr)+']'); + + ptr = ptr || 1; + + if (! this.headers[ptr] || this.headers[ptr].tags === undefined) + return null; + + return '1'+this.zone_id+this.area_id+this.headers[ptr].tags; +} + +MsgArea.prototype.getUserStats = function() { + return msg_area.grp_list[this.msgbase.cfg.grp_number].sub_list[msg_area.sub[this.msgbase.cfg.code].index]; +} + /** * Tag messages with a frame number * diff --git a/main.js b/main.js index a279e1a..9921682 100644 --- a/main.js +++ b/main.js @@ -223,7 +223,8 @@ while(bbs.online) { // After reading from the terminal, see if we need to pass that input to a control module, // except if input is on the bottom line - if (mode !== MODE_BL && control.length) { + log(LOG_DEBUG,'CONTROL: mode ['+mode+'] ('+control.length+')'); + if ((mode !== MODE_BL) && control.length) { log(LOG_DEBUG,'CONTROL DEBUG: ['+control.length+'] ('+JSON.stringify(control)+')'); cc = control[control.length-1]; log(LOG_DEBUG,'CONTROL IS: ['+typeof cc+']'); @@ -828,7 +829,7 @@ while(bbs.online) { case ACTION_EDIT: log(LOG_DEBUG,'- ACTION_EDIT: ['+JSON.stringify(next_page)+']'); - if (! pageEditor(next_page ? next_page.frame : fo.frame)) { + if ((fo.type === FRAME_TYPE_MESSAGE) || (! pageEditor(next_page ? next_page.frame : fo.frame))) { fo.cursorOff(); fo.sendBaseline('ACCESS_DENIED',false); action = false; @@ -899,6 +900,9 @@ while(bbs.online) { // Goto specific page case ACTION_GOTO: + // Clear any controls + control = []; + log(LOG_DEBUG,'- ACTION_GOTO: ['+(next_page ? pageStr(next_page) : '')+']'); var current = null; @@ -914,15 +918,18 @@ while(bbs.online) { // @todo look for a template in the area or group first to.load(MAIL_TEMPLATE_AREA_SUMMARY); + var ma = new MsgAreas(); + var area = ma.getArea(next_page.frame); + // If the template page doesnt exist - if (! to.content) { + if ((! to.content) || (! area)) { fo.sendBaseline('ERR_ROUTE',false); mode = action = false; break; } current = fo; - var ma = new MsgAreas(); + fo = viewdata ? new FrameViewdata() : new FrameAnsi(); fo.frame = next_page.frame; fo.index = next_page.index; @@ -931,26 +938,57 @@ while(bbs.online) { fo.isAccessible = to.isAccessible; fo.owner = to.owner; fo.type = to.type; + // Parent + fo.key[0] = (''+next_page.frame).substr(0,7); + + // First Unread + fo.key[1] = atcode('msg_area_msgunread_page',null,null,area); + + // First to me + fo.key[2] = atcode('msg_area_msgtome_page',null,null,area); + + // Oldest + fo.key[3] = atcode('msg_area_msgoldest_page',null,null,area); + // Newest + fo.key[4] = atcode('msg_area_msgnewest_page',null,null,area); - log(LOG_DEBUG,'USER:'+JSON.stringify(ma.getUserStats((''+next_page.frame).substr(1,6)),null,"\r")); next_page = null; // 1zzzzEEnnnn - get a message } else if (/^1[0-9]{10}/.test(next_page.frame)) { log(LOG_DEBUG,'- ACTION_GOTO - load message: ['+next_page.frame+']'); - require('ansitex/load/control-echomail.js','CONTROL_ECHOMAIL'); - control.push(new echomail((''+next_page.frame).substr(1))); - action = false; - log(LOG_DEBUG,'- ACTION_GOTO - control message: ['+JSON.stringify(control[control.length-1])+'] ('+control.length+')'); + if (next_page.index === 'a') { + require('ansitex/load/control-echomail.js','CONTROL_ECHOMAIL'); + control.push(new echomail(next_page.frame)); + action = false; + next_page = null; + log(LOG_DEBUG,'- ACTION_GOTO - control message: ['+JSON.stringify(control[control.length-1])+'] ('+control.length+')'); - if (! control[control.length-1].ready()) { - control.pop(); - fo.sendBaseline('ERR_ROUTE',false); + if (! control[control.length-1].ready()) { + log(LOG_DEBUG,'- ACTION_GOTO - control not ready aborting...'); + control.pop(); + mode = false; + + fo.sendBaseline('ERR_ROUTE',false); + break; + } + + // @todo - Show message stats + } else if (next_page.index === 'b') { + action = mode = false; + next_page = null; + + fo.sendBaseline('ERR_NOT_IMPLEMENTED',false); + break; + + } else { mode = action = false; - } + next_page = null; - next_page = null; + fo.sendBaseline('ERR_ROUTE',false); + break; + } } } @@ -1010,8 +1048,6 @@ while(bbs.online) { console.line_counter = 0; // @todo fix to suppress a pause that is occurring before clear() fo.cursorOff(); - // Clear any controls - control = []; bbs.replace_text(NodeActionMain,'\1h%s \1nViewing \1h*'+fo.frame+'#\1n ['+fo.index+']'); bbs.log_str(fo.page+'|'); bbs.node_action=NODE_MAIN; @@ -1020,11 +1056,9 @@ while(bbs.online) { // Terminate frame case FRAME_TYPE_MAIL_TEMPLATE: log(LOG_DEBUG,'- MAIL_TEMPLATE: ['+fo.frame+']'); - //log(LOG_DEBUG,' - '+JSON.stringify(ma.getArea(fo.frame))); fo.render(ma.getArea(fo.frame)); mode = false; action = false; - ma = undefined; break; diff --git a/text/198a.tex b/text/198a.tex new file mode 100644 index 0000000..7feab41 --- /dev/null +++ b/text/198a.tex @@ -0,0 +1 @@ +{"version":1,"frame":198,"index":"a","owner":1,"cost":0,"content":"G1swbRtbNzZDG1sxbS4bWzBtDQogG1szM20bWG1zZ19ncnBfbmFtZTs0MBtcG1szMEMbWzM3bRtbMW3awr/Cv7+zG1swbQ0KIBtbMzZtG1htc2dfYXJlYV9hcmVhdGFnOzQwG1wbWzMwQxtbMzdtsyCzwrSzsw0KIBtbMTszNm0bWG1zZ19hcmVhX2Rlc2M7NDAbXBtbMzBDG1sxOzM3bVN1bW1hcnkbWzBtDQobWzE7MzBtxMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMSzxMTExMTExMTExMTExMTExMTExMTExMTExMTExBtbMG0NCiAbWERBVEVUSU1FOzI0G1wbWzI1QxtbMTszMG2zG1swbSAbWzFtTmV3IE1lc3NhZ2VzIHRvIFlvdRtbMG06IBtbMTszMW0bWG1zZ19hcmVhX25ld3RvbWU7LTUbXBtbMG0NChtbNTBDG1sxOzMwbbMbWzBtICAgICAbWzFtVW5yZWFkIE1lc3NhZ2VzG1swbTogG1sxOzMxbRtYbXNnX2FyZWFfbmV3Oy01G1wbWzBtDQogG1sxOzMybTEbWzM3bSBGaXJzdCB1bnJlYWQgICAbWzM0bRtYbXNnX2FyZWFfbXNndW5yZWFkX2RhdGU7MzEbXBtbMG0gG1sxOzMwbbMbWzBtICAgICAgG1sxbVRvdGFsIE1lc3NhZ2VzG1swbTogG1sxOzMxbRtYbXNnX2FyZWFfdG90YWw7LTUbXBtbMG0NCiAbWzE7MzJtMhtbMG0gG1sxbUZpcnN0IHRvIHlvdRtbMG0gICAbWzE7MzRtG1htc2dfYXJlYV9tc2dvdG9tZV9kYXRlOzMxG1wbWzBtIBtbMTszMG2zG1swbQ0KG1s1MEMbWzE7MzBtsxtbMG0gICAgUGVuZGluZyBNZXNzYWdlczogG1szMW0bWG1zZ19hcmVhX3BlbmRpbmc7LTUbXBtbMzdtDQogG1szMm0zG1sxOzM3bRtbMG0gT2xkZXN0IE1lc3NhZ2UgG1szNG0bWG1zZ19hcmVhX21zZ29sZGVzdF9kYXRlOzMxG1wbWzM3bSAbWzE7MzBtsxtbMG0NCiAbWzMybTQbWzFtG1swbSBMYXRlc3QgTWVzc2FnZSAbWzM0bRtYbXNnX2FyZWFfbXNnbmV3ZXN0X2RhdGU7MzEbXBtbMzdtIBtbMTszMG2zG1swbSAgICAgICAgICAbWzE7MzJtORtbMG0gTmV3IFNjYW46ICAgG1sxOzMybRtYbXNnX2FyZWFfbmV3c2NhbjstMxtcG1swbQ0KG1s1MEMbWzE7MzBtwRtbMG0NCiAbWzMybTUbWzFtIBtbMG1Xcml0ZSBOZXcgTWVzc2FnZSAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7Mzc7NDRt1c3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3NuBtbMG0NCiAbWzMybTYbWzFtIBtbMG1TZWFyY2ggZm9yIE1lc3NhZ2UgICAgICAgICAgICAgICAgICAgICAgICAbWzE7Mzc7NDRtsxtbMzNtIK8gTWVzc2FnZSBSZWFkaW5nIE5hdmlnYXRpb24grhtbMDszNzs0NG0gG1sxbbMbWzBtDQobWzQ1QxtbMTszNzs0NG2zIBtbMzJtNBtbMzZtIFByZXZpb3VzICAbWzA7NDRtICAbWzE7MzdtsxtbMDs0NG0gG1sxOzMybTUbWzA7NDRtIBtbMTszNm1Xcml0ZSBOZXcgG1swOzQ0bSAgG1sxOzM3bSCzG1swbQ0KIBtbMzJtOBtbMzdtIEFyZWEgU3VtbWFyeSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszNzs0NG2zG1swOzQ0bSAbWzE7MzJtNhtbMzZtIE5leHQgG1swOzQ0bSAgICAgICAbWzE7MzdtsxtbMDs0NG0gG1sxOzMybTgbWzA7NDRtIBtbMTszNm1SZXBseRtbMDs0NG0gICAgICAgG1sxOzM3bSCzG1swbQ0KIBtbMzJtMCAbWzM3bUJhY2sgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszNzs0NG2zG1swOzQ0bSAbWzE7MzJtIxtbMDs0NG0gG1sxOzM2bU1zZyBBdHRycxtbMDszNjs0NG0gIBtbMzdtIBtbMTszN22zG1swOzQ0bSAbWzE7MzJtMBtbMDs0NG0gG1sxOzM2bVJldHVybiBoZXJlG1swOzQ0bSAbWzE7MzdtILMbWzBtDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7Mzc7NDRt1M3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3NvhtbMG0NCg==","isPublic":0,"isAccessible":1,"type":"m","key":[null,null,null,null,null,null,null,null,null,null],"date":"2022-04-29T00:00:00.000Z"}