From 30756a907ab490101307a9cf0a5a69fd3a6a5a77 Mon Sep 17 00:00:00 2001 From: DSri Seah <11952933+dsriseah@users.noreply.github.com> Date: Thu, 29 May 2025 18:18:51 -0400 Subject: [PATCH 01/38] prop-settings-2: fix editor rollover offsets --- app/view/netcreate/components/react-settings-bridge.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/view/netcreate/components/react-settings-bridge.js b/app/view/netcreate/components/react-settings-bridge.js index 38ee92989..571fa0da0 100644 --- a/app/view/netcreate/components/react-settings-bridge.js +++ b/app/view/netcreate/components/react-settings-bridge.js @@ -131,7 +131,7 @@ const popupStyle = { backgroundColor: 'gray', color: 'white', padding, - zIndex: 1000, + zIndex: 10000, maxWidth: '20rem', display: 'none' }; @@ -150,8 +150,12 @@ function GetStyles() { /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function EventTargetOffsetStyle(event) { const rect = event.target.getBoundingClientRect(); + const advPanel = document.querySelector('#popover'); + const advRect = advPanel + ? advPanel.getBoundingClientRect() + : { left: '0px', top: '0px' }; return { - left: `${rect.left + window.scrollX}px`, + left: `${rect.left - advRect.left}px`, top: `${rect.top + window.scrollY + rect.height + 2}px` }; } From 3f1b2e01e9c2282d904d8040b63079673bc72d0e Mon Sep 17 00:00:00 2001 From: DSri Seah <11952933+dsriseah@users.noreply.github.com> Date: Thu, 29 May 2025 20:33:48 -0400 Subject: [PATCH 02/38] prop-settings-2: clarify naming for Dataset (not D3Data) loader and related routines --- _mur/web-client/comment/dc-comment.ts | 62 ++++++++++++-------- app/system/datastore.js | 2 +- app/unisys/server-database.js | 22 +++---- app/unisys/server.js | 4 +- app/view/netcreate/components/NCTemplate.jsx | 2 +- app/view/netcreate/nc-logic.js | 8 ++- app/view/netcreate/template-editor-mgr.js | 2 +- app/view/netcreate/template-mgr.js | 2 +- 8 files changed, 62 insertions(+), 42 deletions(-) diff --git a/_mur/web-client/comment/dc-comment.ts b/_mur/web-client/comment/dc-comment.ts index a6c4283cd..fe2143c58 100644 --- a/_mur/web-client/comment/dc-comment.ts +++ b/_mur/web-client/comment/dc-comment.ts @@ -336,13 +336,16 @@ const DEFAULT_CommentTypes: Array = [ function m_LoadUsers(dbUsers: TUserObject[]) { dbUsers.forEach(u => USERS.set(u.id, u.name)); } +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function m_LoadCommentTypes(commentTypes: TCommentType[]) { COMMENTTYPES.clear(); commentTypes.forEach(t => COMMENTTYPES.set(t.slug, t)); } +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function m_LoadComments(comments: TComment[]) { comments.forEach(c => COMMENTS.set(c.comment_id, c)); } +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function m_LoadReadBy(readby: TReadByObject[]) { readby.forEach(r => READBY.set(r.comment_id, r.commenter_ids)); } @@ -357,11 +360,14 @@ function Init() { // Load Defaults // m_LoadCommentTypes(DEFAULT_CommentTypes); } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function LoadTemplate(commentTypes: Array) { // fall back order: template.toml > DEFAULT_CommentTypes > DEFAULT_COMMENTTYPE // fall back to DEFAULT_CommentTypes if DEFAULT_CommentTypes is not in dc-comments // or a single DEFAULT_COMMENTTYPE if it's completely missing + if (commentTypes === undefined) { + console.warn(PR, 'No custom commentTypes provided in template, using defaults'); + } const types = commentTypes || (DEFAULT_CommentTypes && DEFAULT_CommentTypes.length > 0 @@ -369,7 +375,7 @@ function LoadTemplate(commentTypes: Array) { : [DEFAULT_COMMENTTYPE]); m_LoadCommentTypes(types); } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** * @param {Object} data * @param {Object} data.users @@ -404,34 +410,40 @@ function LoadDB(data: TLokiData) { // Derive Secondary Values m_DeriveValues(); } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function GetUsers(): TUserMap { return USERS; } +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function GetUser(uid): TUserName { return USERS.get(uid); } +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function GetUserName(uid): TUserName { const u = USERS.get(uid); return u !== undefined ? u : uid; // fallback to using `uid` if there's no record } +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function GetCurrentUser(): TUserName { // TODO Placeholder return 'Ben32'; } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function GetCommentTypes(): TCommentTypeMap { return COMMENTTYPES; } +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function GetCommentType(slug: CType): TCommentType { return COMMENTTYPES.get(slug); } +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function GetDefaultCommentType(): TCommentType { // returns the first comment type object if (COMMENTTYPES.size < 1) throw new Error('dc-comments: No comment types defined!'); return GetCommentType(GetDefaultCommentTypeSlug()); } +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function GetDefaultCommentTypeSlug(): CType { // returns the first comment type slug // fall back order: template.toml > DEFAULT_CommentTypes > DEFAULT_COMMENTTYPE @@ -439,14 +451,15 @@ function GetDefaultCommentTypeSlug(): CType { throw new Error('dc-comments: No comment types defined!'); return COMMENTTYPES.keys().next().value; } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function GetCOMMENTS(): TCommentMap { return COMMENTS; } +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function GetComment(cid: TCommentID): TComment { return COMMENTS.get(cid); } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function m_DeriveValues() { ROOTS.clear(); REPLY_ROOTS.clear(); @@ -465,7 +478,7 @@ function m_DeriveValues() { if (DBG) console.log('REPLY_ROOTS', REPLY_ROOTS); if (DBG) console.log('NEXT', NEXT); } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function AddComment(data): TComment { if (data.cref === undefined) throw new Error('Comments must have a collection ref!'); @@ -492,7 +505,7 @@ function AddComment(data): TComment { return comment; } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** * API Call to processes a single update * @param {Object} cobj TComment @@ -501,6 +514,7 @@ function UpdateComment(cobj: TComment) { m_UpdateComment(cobj); m_DeriveValues(); } +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** * Processes a single update * @param {Object} cobj TComment @@ -515,6 +529,7 @@ function m_UpdateComment(cobj: TComment) { // ); COMMENTS.set(cobj.comment_id, cobj); } +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** * API Call to batch updates an array of updated comments * @param {Object[]} cobjs TComment[] @@ -523,7 +538,7 @@ function HandleUpdatedComments(cobjs: TComment[]) { cobjs.forEach(cobj => m_UpdateComment(cobj)); m_DeriveValues(); } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** * Safely delete a comment and queue it for deletion * This is necessary to also return the `id` @@ -539,6 +554,7 @@ function m_safeDeleteAndQueue(cid): TCommentQueueActions { } throw new Error(`Comment ${cid} not found. This should not happen!`); } +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** * @param {Object} parms * @param {Object} parms.collection_ref @@ -727,7 +743,7 @@ function RemoveComment(parms): TCommentQueueActions[] { m_DeriveValues(); return queuedActions; } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** * @param {Object} parms * @param {Object} parms.collection_ref @@ -747,7 +763,7 @@ function RemoveAllCommentsForCref(parms): TCommentQueueActions[] { m_DeriveValues(); return queuedActions; } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** * Checks if the current root and all children are marked deleted. * Ignores the NEXT root items @@ -766,7 +782,7 @@ function m_AllAreMarkedDeleted(rootCommentId: TCommentID): boolean { }); return allAreMarkedDeleted; } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** * Batch updates an array of removed comment ids * @param {number[]} comment_ids @@ -778,7 +794,7 @@ function HandleRemovedComments(comment_ids: TCommentID[]) { }); m_DeriveValues(); } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** * `uid` can be undefined if user is not logged in */ @@ -788,22 +804,23 @@ function MarkCommentRead(cid: TCommentID, uid: TUserID) { if (!readby.includes(uid)) readby.push(uid); READBY.set(cid, readby); } +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function MarkCommentUnread(cid: TCommentID, uid: TUserID) { // Mark the comment NOT read const readby = READBY.get(cid) || []; const updatedReadby = readby.filter(readByUid => readByUid !== uid); READBY.set(cid, updatedReadby); } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function IsMarkedRead(cid: TCommentID, uid: TUserID): boolean { const readby = READBY.get(cid) || []; return readby.includes(uid); } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function IsMarkedDeleted(cid: TCommentID): boolean { return COMMENTS.get(cid).comment_isMarkedDeleted; } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** Walk down the next items starting with the current * Ignores child threads * @returns {string[]} comment_ids @@ -815,7 +832,7 @@ function m_GetNexts(cid: TCommentID): TCommentID[] { if (nextId) results.push(nextId, ...m_GetNexts(nextId)); return results; } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** Gets all the child reply comments under the root * Does not include the rootCid * @param {string} rootCid root comment id @@ -828,7 +845,7 @@ function m_GetReplies(rootCid: TCommentID): TCommentID[] { if (replyRootId) results.push(replyRootId, ...m_GetNexts(replyRootId)); return results; } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** recursively add replies and next * 1. Adds nested children reply threads first * 2. Then adds the next younger sibling @@ -853,7 +870,7 @@ function m_GetRepliesAndNext(cid: TCommentID): TCommentID[] { return results; } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** * Get all the comment ids related to a particular collection_ref * based on ROOTS. @@ -874,7 +891,7 @@ function GetThreadedCommentIds(cref: TCollectionRef): TCommentID[] { } if (DBG) console.log('GetThreadedView', GetThreadedCommentIds('1')); if (DBG) console.log('GetThreadedView', GetThreadedCommentIds('2')); - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** * [Currently not used] * Get all the comments related to a particular collection_ref @@ -886,7 +903,7 @@ function GetThreadedCommentData(cref: TCollectionRef): TComment[] { // convert ids to comment objects return threaded_comments_ids.map(cid => COMMENTS.get(cid)); } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // NOT USED? // // /** @@ -900,11 +917,10 @@ function GetThreadedCommentData(cref: TCollectionRef): TComment[] { // // convert ids to comment objects // return threaded_comments_ids.map(cid => COMMENTS.get(cid)); // } - function GetReadby(cid: TCommentID): TUserID[] { return READBY.get(cid); } - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function GetCrefs(): TCollectionRef[] { return [...ROOTS.keys()]; } diff --git a/app/system/datastore.js b/app/system/datastore.js index cb0d07623..957e8b5e1 100644 --- a/app/system/datastore.js +++ b/app/system/datastore.js @@ -302,7 +302,7 @@ DSTOR.PromiseTOMLFile = function (tomlFile) { /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** API: Load D3 Database */ -DSTOR.PromiseD3Data = function () { +DSTOR.PromiseDataset = function () { // UDATA.Call() returns a promise return UDATA.Call('SRV_DBGET', {}); // server.js }; diff --git a/app/unisys/server-database.js b/app/unisys/server-database.js index 0637d00ad..1460f974b 100644 --- a/app/unisys/server-database.js +++ b/app/unisys/server-database.js @@ -82,7 +82,7 @@ function m_DefaultTemplatePath() { } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** API: Initialize the database */ -DB.InitializeDatabase = function (options = {}) { +DB.InitializeDataset = function (options = {}) { let dataset = NC_CONFIG.dataset; db_file = m_GetValidDBFilePath(dataset); FS.ensureDirSync(PATH.dirname(db_file)); @@ -181,7 +181,7 @@ DB.InitializeDatabase = function (options = {}) { } console.log( PR, - 'dataset loaded', + 'graph data loaded', BL(db_file), `m_max_nodeID '${m_max_nodeID}', m_max_edgeID '${m_max_edgeID}'` ); @@ -204,6 +204,8 @@ DB.InitializeDatabase = function (options = {}) { m_db.saveDatabase(); + // load non-database assets from dataset.toml, creating + // it if necessary await m_LoadTemplate(); m_MigrateTemplate(); m_ValidateTemplate(); @@ -220,7 +222,7 @@ DB.InitializeDatabase = function (options = {}) { `AUTOSAVING! ${nodeCount} NODES / ${edgeCount} EDGES / ${commentCount} COMMENTS / ${readbyCount} READBY <3` ); } -}; // InitializeDatabase() +}; // InitializeDataset() /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** Loads a *.template.toml file from the server. */ function m_LoadTOMLTemplate(templateFilePath) { @@ -240,7 +242,7 @@ function m_LoadTOMLTemplate(templateFilePath) { 2. If it can't be found, tries to load the JSON template and convert it 3. If that fails, clone the default TOML template and load it Called by - * DB.InitializeDatabase + * DB.InitializeDataset * DB.WriteTemplateTOML */ async function m_LoadTemplate() { @@ -448,9 +450,9 @@ function m_ValidateTemplate() { /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** API: load database - * note: InitializeDatabase() was already called on system initialization + * note: InitializeDataset() was already called on system initialization * to populate the NODES and EDGES structures */ -DB.PKT_GetDatabase = function (pkt) { +DB.PKT_GetDataset = function (pkt) { let nodes = NODES.chain().data({ removeMeta: false }); let edges = EDGES.chain().data({ removeMeta: false }); let comments = COMMENTS.chain().data(); @@ -458,7 +460,7 @@ DB.PKT_GetDatabase = function (pkt) { if (DBG) console.log( PR, - `PKT_GetDatabase ${pkt.Info()} (loaded ${nodes.length} nodes, ${ + `PKT_GetDataset ${pkt.Info()} (loaded ${nodes.length} nodes, ${ edges.length } edges)` ); @@ -490,7 +492,7 @@ DB.PKT_SetDatabase = function (pkt) { READBY.insert(readby); console.log(PR, `PKT_SetDatabase complete. Data available on next get.`); m_db.close(); - DB.InitializeDatabase(); + DB.InitializeDataset(); LOGGER.WriteRLog(pkt.InfoObj(), `setdatabase`); return { OK: true }; }; @@ -513,7 +515,7 @@ DB.PKT_InsertDatabase = function (pkt) { READBY.insert(readby); console.log(PR, `PKT_InsertDatabase complete. Data available on next get.`); m_db.close(); - DB.InitializeDatabase(); + DB.InitializeDataset(); LOGGER.WriteRLog(pkt.InfoObj(), `setdatabase`); return { OK: true }; }; @@ -560,7 +562,7 @@ DB.PKT_MergeDatabase = function (pkt) { return new Promise((resolve, reject) => m_db.saveDatabase(err => { if (err) reject(new Error('rejected')); - DB.InitializeDatabase(); + DB.InitializeDataset(); LOGGER.WriteRLog(pkt.InfoObj(), `mergedatabase`); resolve({ OK: true }); }) diff --git a/app/unisys/server.js b/app/unisys/server.js index c220c5f2f..d9e967809 100644 --- a/app/unisys/server.js +++ b/app/unisys/server.js @@ -38,7 +38,7 @@ UNISYS.InitializeNetwork = override => { // MUR INTEROP: end // resume NetCreate server initialization - UDB.InitializeDatabase(override); + UDB.InitializeDataset(override); return UNET.InitializeNetwork(override); }; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -65,7 +65,7 @@ UNISYS.RegisterHandlers = () => { // UNET.HandleMessage('SRV_DBGET', function (pkt) { if (DBG) console.log(PR, sprint_message(pkt)); - return UDB.PKT_GetDatabase(pkt); + return UDB.PKT_GetDataset(pkt); }); // UNET.HandleMessage('SRV_DBSET', function (pkt) { diff --git a/app/view/netcreate/components/NCTemplate.jsx b/app/view/netcreate/components/NCTemplate.jsx index 938b4de1c..a66fd4589 100644 --- a/app/view/netcreate/components/NCTemplate.jsx +++ b/app/view/netcreate/components/NCTemplate.jsx @@ -24,7 +24,7 @@ ## BACKGROUND - Template data is loaded by `server-database` DB.InitializeDatabase call. + Template data is loaded by `server-database` DB.InitializeDataset call. \*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * //////////////////////////////////////*/ diff --git a/app/view/netcreate/nc-logic.js b/app/view/netcreate/nc-logic.js index a20a2bcc0..ecb5796e8 100644 --- a/app/view/netcreate/nc-logic.js +++ b/app/view/netcreate/nc-logic.js @@ -185,8 +185,8 @@ var TEMPLATE = null; // template definition for prompts /** Used by LOADASSETS and RELOAD_DB to reload NCDATA from the database. */ function m_PromiseLoadDB() { - return DATASTORE.PromiseD3Data().then(data => { - if (DBG) console.log(PR, 'DATASTORE returned data', data); + return DATASTORE.PromiseDataset().then(data => { + console.log(PR, 'DATASTORE returned data', data); m_MigrateData(data.d3data); UTILS.RecalculateAllEdgeSizes(data.d3data); UTILS.RecalculateAllNodeDegrees(data.d3data); @@ -261,8 +261,10 @@ MOD.Hook('LOADASSETS', () => { })(); }); } + + // if got this far, then we're NOT in standalone mode return Promise.all([m_PromiseLoadDB()]); -}); // loadassets +}); // end LOADASSETS HOOK /// UNISYS LIFECYCLE HOOKS //////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/view/netcreate/template-editor-mgr.js b/app/view/netcreate/template-editor-mgr.js index a453393af..a0508d4a7 100644 --- a/app/view/netcreate/template-editor-mgr.js +++ b/app/view/netcreate/template-editor-mgr.js @@ -18,7 +18,7 @@ ## BACKGROUND - Template data is loaded by `server-database` DB.InitializeDatabase call. + Template data is loaded by `server-database` DB.InitializeDataset call. With Version 1.4 of NetCreate, we introduce a new TOML template format that is easier to work with directly. diff --git a/app/view/netcreate/template-mgr.js b/app/view/netcreate/template-mgr.js index 3b8800f1c..6cffdd2e9 100644 --- a/app/view/netcreate/template-mgr.js +++ b/app/view/netcreate/template-mgr.js @@ -17,7 +17,7 @@ ## BACKGROUND - Template data is loaded by `server-database` DB.InitializeDatabase call. + Template data is loaded by `server-database` DB.InitializeDataset call. With Version 1.4 of Net.Create, we introduce a new TOML template format that is easier to work with directly. From 98b678abdf83d25a1a9ae64d60202542dedb35a0 Mon Sep 17 00:00:00 2001 From: DSri Seah <11952933+dsriseah@users.noreply.github.com> Date: Thu, 29 May 2025 21:18:23 -0400 Subject: [PATCH 03/38] prop-settings-2: rename async or promise returning methods to help catch async bugs in programming see potential bug in server-database in WriteTemplateTOML --- app/unisys/server-database.js | 18 +++++++++--------- app/view/netcreate/template-editor-mgr.js | 2 +- app/view/netcreate/template-mgr.js | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/unisys/server-database.js b/app/unisys/server-database.js index 1460f974b..d2f2900a4 100644 --- a/app/unisys/server-database.js +++ b/app/unisys/server-database.js @@ -95,7 +95,7 @@ DB.InitializeDataset = function (options = {}) { // console.log(PR, YL(`loading dataset`), `${BL(db_file)}...`); let ropt = { autoload: true, - autoloadCallback: f_DatabaseInitialize, + autoloadCallback: async_DatabaseInitialize, autosave: true, autosaveCallback: f_AutosaveStatus, autosaveInterval: 4000 // save every four seconds @@ -111,7 +111,7 @@ DB.InitializeDataset = function (options = {}) { nothing to do with the database :| /*/ - async function f_DatabaseInitialize() { + async function async_DatabaseInitialize() { // on the first load of (non-existent database), we will have no // collections so we can detect the absence of our collections and // add (and configure) them now. @@ -206,10 +206,10 @@ DB.InitializeDataset = function (options = {}) { // load non-database assets from dataset.toml, creating // it if necessary - await m_LoadTemplate(); + await async_LoadTemplate(); m_MigrateTemplate(); m_ValidateTemplate(); - } // end f_DatabaseInitialize + } // end async_DatabaseInitialize // UTILITY FUNCTION function f_AutosaveStatus() { @@ -225,7 +225,7 @@ DB.InitializeDataset = function (options = {}) { }; // InitializeDataset() /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** Loads a *.template.toml file from the server. */ -function m_LoadTOMLTemplate(templateFilePath) { +function promise_LoadTOMLTemplate(templateFilePath) { return new Promise((resolve, reject) => { const templateFile = FS.readFile(templateFilePath, 'utf8', (err, data) => { if (err) throw err; @@ -245,7 +245,7 @@ function m_LoadTOMLTemplate(templateFilePath) { * DB.InitializeDataset * DB.WriteTemplateTOML */ -async function m_LoadTemplate() { +async function async_LoadTemplate() { const TOMLPath = m_GetTemplateTOMLFilePath(); FS.ensureDirSync(PATH.dirname(TOMLPath)); /*/ SRI NOTE @@ -255,13 +255,13 @@ async function m_LoadTemplate() { // Does the TOML template exist? if (FS.existsSync(TOMLPath)) { // 1. If TOML exists, load it - await m_LoadTOMLTemplate(TOMLPath); + await promise_LoadTOMLTemplate(TOMLPath); } else { // clone _default.template.toml console.log(PR, `NO EXISTING TEMPLATE ${TOMLPath}`); FS.copySync(m_DefaultTemplatePath(), TOMLPath); // then load it - await m_LoadTOMLTemplate(TOMLPath); + await promise_LoadTOMLTemplate(TOMLPath); } } @@ -1556,7 +1556,7 @@ DB.WriteTemplateTOML = pkt => { .then(data => { console.log(PR, 'Saved template to', templateFilePath); // reload template - m_LoadTemplate(); + async_LoadTemplate(); return { OK: true, info: templateFilePath }; }) .catch(err => { diff --git a/app/view/netcreate/template-editor-mgr.js b/app/view/netcreate/template-editor-mgr.js index a0508d4a7..589a342e3 100644 --- a/app/view/netcreate/template-editor-mgr.js +++ b/app/view/netcreate/template-editor-mgr.js @@ -27,7 +27,7 @@ server-database.m_LoadJSONTemplate() and m_MigrateJSONtoTOML(). * If you try to load a TOML template that is missing some key fields (e.g. error message definitions), then the app will fall back on - fields defined in the schema. See server-database.m_LoadTOMLTemplate()). + fields defined in the schema. See server-database.promise_LoadTOMLTemplate()). \*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * //////////////////////////////////////*/ diff --git a/app/view/netcreate/template-mgr.js b/app/view/netcreate/template-mgr.js index 6cffdd2e9..f24cb660b 100644 --- a/app/view/netcreate/template-mgr.js +++ b/app/view/netcreate/template-mgr.js @@ -26,7 +26,7 @@ server-database.m_LoadJSONTemplate() and m_MigrateJSONtoTOML(). * If you try to load a TOML template that is missing some key fields (e.g. error message definitions), then the app will fall back on - fields defined in the schema. See server-database.m_LoadTOMLTemplate()). + fields defined in the schema. See server-database.promise_LoadTOMLTemplate()). With Version 2.0 of Net.Create, we want to centralize all of the template management logic in this module. From 18a43dd15d9609a22f98ce2f3fd5c340f34f34a5 Mon Sep 17 00:00:00 2001 From: DSri Seah <11952933+dsriseah@users.noreply.github.com> Date: Fri, 30 May 2025 15:32:13 -0400 Subject: [PATCH 04/38] prop-settings-2: hack so settings panel opens on launch for development --- app/init.jsx | 3 ++- app/view/netcreate/components/NCAdvancedPanel.jsx | 7 +++++-- app/view/netcreate/nc-logic.js | 1 - 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/init.jsx b/app/init.jsx index d4d014a55..6988dba32 100644 --- a/app/init.jsx +++ b/app/init.jsx @@ -32,7 +32,8 @@ const MUR = require('ursys-min'); /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /// When the DOM is loaded, initialize UNISYS document.addEventListener('DOMContentLoaded', () => { - console.groupCollapsed('init.jsx bootstrap'); + // HACK MAKE SURE TO COLLAPSE BEFORE PR SUBMISSION + console.group('init.jsx bootstrap'); console.log( '%cINIT %cDOMContentLoaded. Starting UNISYS Lifecycle!', 'color:blue', diff --git a/app/view/netcreate/components/NCAdvancedPanel.jsx b/app/view/netcreate/components/NCAdvancedPanel.jsx index d64d35824..b99fab477 100644 --- a/app/view/netcreate/components/NCAdvancedPanel.jsx +++ b/app/view/netcreate/components/NCAdvancedPanel.jsx @@ -31,8 +31,11 @@ const VIEWS = { /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /// export a class object for consumption by brunch/require function NCAdvancedPanel() { - const [isOpen, setIsOpen] = useState(false); - const [openTab, setOpenTab] = useState('importexport'); + // const [isOpen, setIsOpen] = useState(false); + // const [openTab, setOpenTab] = useState('importexport'); + // HACK MAKE SURE TO REMOVE BEFORE PR SUBMISSION + const [isOpen, setIsOpen] = useState(true); + const [openTab, setOpenTab] = useState('settings'); useEffect(() => { UDATA.OnAppStateChange('PANELSTATE', evt_ToggleAdvanced); diff --git a/app/view/netcreate/nc-logic.js b/app/view/netcreate/nc-logic.js index ecb5796e8..48d86884b 100644 --- a/app/view/netcreate/nc-logic.js +++ b/app/view/netcreate/nc-logic.js @@ -186,7 +186,6 @@ var TEMPLATE = null; // template definition for prompts */ function m_PromiseLoadDB() { return DATASTORE.PromiseDataset().then(data => { - console.log(PR, 'DATASTORE returned data', data); m_MigrateData(data.d3data); UTILS.RecalculateAllEdgeSizes(data.d3data); UTILS.RecalculateAllNodeDegrees(data.d3data); From f88163c998b3e31b2906c45b70e964ace6e996d9 Mon Sep 17 00:00:00 2001 From: DSri Seah <11952933+dsriseah@users.noreply.github.com> Date: Fri, 30 May 2025 15:32:51 -0400 Subject: [PATCH 05/38] prop-settings-2: work in progress converting TEMPLATE to PropertyGroup-compatible data --- .../netcreate/components/MURPropertyGroup.jsx | 16 ++- .../components/MURSettingsEditor.jsx | 106 +++++---------- .../netcreate/components/MURSettingsToDo.jsx | 125 ++++++++++++++++++ .../netcreate/components/MURTextInput.jsx | 17 ++- .../components/react-settings-bridge.js | 19 +++ 5 files changed, 203 insertions(+), 80 deletions(-) create mode 100644 app/view/netcreate/components/MURSettingsToDo.jsx diff --git a/app/view/netcreate/components/MURPropertyGroup.jsx b/app/view/netcreate/components/MURPropertyGroup.jsx index ce243b4af..37e18745e 100644 --- a/app/view/netcreate/components/MURPropertyGroup.jsx +++ b/app/view/netcreate/components/MURPropertyGroup.jsx @@ -40,7 +40,7 @@ function PropertyGroup(props) { if (gdata.error) return

PropertyGroup bad groupDef {gdata.error}

; const { groupName, properties } = gdata; const propList = Object.keys(properties); // list of property names - const meta = metaDef[groupName]; + const meta = metaDef[groupName] || {}; const { title, description } = meta._groupMeta || {}; const key = `pg-${groupName}`; return ( @@ -54,9 +54,17 @@ function PropertyGroup(props) { )} {propList.map(p => { if (properties[p].type) { - const key = `in-${groupName}.${p}`; - return ; - } + const dotProp = `${groupName}.${p}`; + const key = `in-${dotProp}`; + return ( + + ); + } else console.log(`PropertyGroup: no type for ${groupName}.${p}`); return null; })} diff --git a/app/view/netcreate/components/MURSettingsEditor.jsx b/app/view/netcreate/components/MURSettingsEditor.jsx index d6076d6e5..0bc66ab05 100644 --- a/app/view/netcreate/components/MURSettingsEditor.jsx +++ b/app/view/netcreate/components/MURSettingsEditor.jsx @@ -13,6 +13,7 @@ const { ConsoleStyler } = require('ursys-min'); const RSB = require('./react-settings-bridge'); const PropertyGroup = require('./MURPropertyGroup'); const { diff } = require('deep-object-diff'); +const ToDoList = require('./MURSettingsToDo'); /// RUNTIME INITIALIZATION //////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -40,83 +41,44 @@ function MURSettingsEditor() { LOG(...PR('would revert changes')); } - const propDefs = RSB.GetPropertyDefs(); - const metaDefs = RSB.GetMetaDefs(); + // new system is no longer used for netcreate; sticking with legacy for now + // const propDefs = RSB.GetPropertyDefs(); + // const metaDefs = RSB.GetMetaDefs(); + const { name, description } = RSB.GetLegacyTemplate(); + const propDefs = { + graphSettings: { + graphName: { + type: 'string', + value: name + }, + graphDescription: { + type: 'string', + value: description + } + } + }; + const metaDefs = { + _groupMeta: {}, + graphSettings: { + graphName: { + label: 'Graph Name', + tooltip: 'Name of the graph', + placeholder: 'Graph Name' + }, + graphDescription: { + label: 'Graph Description', + tooltip: 'Description of the graph', + placeholder: 'Graph Description' + } + } + }; + const { opBtnStyle, modColor } = RSB.GetStyles(); const mod = api.lastSettingsUpdate !== oldState; const backgroundColor = mod ? modColor : 'white'; const color = mod ? 'black' : 'gray'; const btnStyle = { ...opBtnStyle, backgroundColor, color }; - const toDoList = ( -
-
    -
  • graph name
  • -
  • graph description
  • -
  • secret key (for tokens)
  • -
  • admin password
  • -
  • - Node Definitions -
      -
    • - Node Type -
        -
      • 1: [label, color]
      • -
      • 2: [label, color]
      • -
      • ...7
      • -
      -
    • -
    • Notes -- label, type, hide
    • -
    • Info -- label, type, hide
    • -
    • InfoSource -- label, type, hide
    • -
    -
  • -
  • - Edge Definitions -
      -
    • - Edge Type -
        -
      • 1: [label, color]
      • -
      • 2: [label, color]
      • -
      • ...7
      • -
      -
    • -
    • Notes -- label, type, hide
    • -
    • InfoOrigin -- label, type, hide
    • -
    • Citation -- label, type, hide
    • -
    • Category -- label, type, hide
    • -
    -
  • -
  • - Comment Types -
      -
    • slug
    • -
    • label
    • -
    • - prompts -
        -
      • 1: [format, prompt, help, feedback]
      • -
      • 2: [format, prompt, help, feedback]
      • -
      -
    • -
    -
  • -
-

NOTES:

-
    -
  • - `isProvenance` will place a field in the Proveannce tab. But we do not - expect teachers to need to change that. -
  • -
  • - Ideally teachers can add and remove new Node and Edge field definitions, - rather merely re-purposing existing fields. e.g. they might add an Event - Date field. -
  • -
-
- ); return ( @@ -138,7 +100,7 @@ function MURSettingsEditor() { key={gn} /> ))} - {showToDo && toDoList} + {showToDo && ToDoList} ); } diff --git a/app/view/netcreate/components/MURSettingsToDo.jsx b/app/view/netcreate/components/MURSettingsToDo.jsx new file mode 100644 index 000000000..9295ae58d --- /dev/null +++ b/app/view/netcreate/components/MURSettingsToDo.jsx @@ -0,0 +1,125 @@ +/*//////////////////////////////// ABOUT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*\ + + MUR Settings To Do List + This is just a list of things that need to be done, imported by + MURSettingsEditor.jsx as a reminder of what needs to be done. + +\*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * //////////////////////////////////////*/ + +const React = require('react'); + +/// TO DO LIST //////////////////////////////////////////////////////////////// +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +module.exports = ( +
+

+ This list is defined in MURSettingsToDo.jsx and imported by + MURSettingsEditor.jsx. +

+
    +
  • + graphName :string +
  • +
  • + graphDescription :string +
  • +
  • + secretKey (for tokens) :string +
  • +
  • + adminPassword :string +
  • +
  • + Node Definitions +
      +
    • + Node Type +
        +
      • + 1: [label:string, color:colorChoice] +
      • +
      • ...7
      • +
      +
    • +
    • + Notes: label:string, hide + :boolean +
    • +
    • + Info: label:string, hide:boolean +
    • +
    • + InfoSource: label:string, hide + :boolean +
    • +
    +
  • +
  • + Edge Definitions +
      +
    • + Edge Type +
        +
      • + 1: [label:string, color:colorChoice] +
      • +
      • ...7
      • +
      +
    • +
    • + Notes: label:string, hide + :boolean +
    • +
    • + InfoOrigin: label:string, hide + :boolean +
    • +
    • + Citation: label:string, hide + :boolean +
    • +
    • + Category: label:string, hide + :boolean +
    • +
    +
  • +
  • + Comment Types +
      +
    • + slug:string +
    • +
    • + label:string +
    • +
    • + prompts +
        +
      • + 1: [format:string, prompt:string, help + :string, feedback:string] +
      • +
      • + 2: [format:string, prompt:string, help + :string, feedback:string] +
      • +
      +
    • +
    +
  • +
+

NOTES:

+
    +
  • + `isProvenance` will place a field in the Proveannce tab. But we do not expect + teachers to need to change that. +
  • +
  • + Ideally teachers can add and remove new Node and Edge field definitions, + rather merely re-purposing existing fields. e.g. they might add an Event Date + field. +
  • +
+
+); diff --git a/app/view/netcreate/components/MURTextInput.jsx b/app/view/netcreate/components/MURTextInput.jsx index 7cdf3da4b..f4c5c39a6 100644 --- a/app/view/netcreate/components/MURTextInput.jsx +++ b/app/view/netcreate/components/MURTextInput.jsx @@ -19,14 +19,22 @@ const SettingsContext = RSB.GetSettingsContext(); /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const { itemGrid, labelStyle, inputStyle, popupStyle, modColor } = RSB.GetStyles(); +/// HELPER METHODS //////////////////////////////////////////////////////////// +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/** quote a string or return as-is number */ +function $(strOrNum) { + return typeof strOrNum === 'string' ? `'${strOrNum}'` : strOrNum; +} + /// TEXT INPUT COMPONENT ////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** A TextInput component */ function TextInput(props) { - const { propDef, metaDef } = props; + const { propDef, metaDef, dotProp } = props; if (DBG) { if (typeof propDef !== 'object') return

TextInput bad groupDef

; if (typeof metaDef !== 'object') return

TextInput bad metaDef

; + if (typeof dotProp !== 'string') return

TextInput bad dotProp

; } const { value, default: defValue } = propDef; const { label, tooltip, help, placeholder } = metaDef; @@ -53,9 +61,10 @@ function TextInput(props) { /// UI-SETTINGS INTEROP EVENT UPDATES /// // send data to settings object, which will trigger rerender - const submitToSettings = async value => { - LOG('would check data', propDef); - LOG('would call RSB.UpdateProperty(args)'); + const submitToSettings = async event => { + const value = event.target.value; + LOG('would check value', $(value), 'against', propDef); + LOG(`would call RSB.UpdateProperty('${dotProp}', ${$(value)})`); // api.forceUpdate(); }; diff --git a/app/view/netcreate/components/react-settings-bridge.js b/app/view/netcreate/components/react-settings-bridge.js index 571fa0da0..52643ff25 100644 --- a/app/view/netcreate/components/react-settings-bridge.js +++ b/app/view/netcreate/components/react-settings-bridge.js @@ -16,6 +16,7 @@ const PR = ConsoleStyler('SettingClient', 'TagBlue'); const DBG = true; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const MOD = UNISYS.NewModule(module.id); +const UDATA = UNISYS.NewDataLink(MOD); /// SETTINGS CHANGE SUBSCRIPTION ////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -33,6 +34,20 @@ function Unsubscribe(event, changeHandler) { Settings.Unsubscribe(event, changeHandler); } +/// LEGACY SETTINGS API /////////////////////////////////////////////////////// +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/** API: Get the AppState('TEMPLATE') object, which is the legacy settings + * object used by the legacy netcreate modules. This is what we have to use + * in NetCreate for July and September 2025 */ +function GetLegacyTemplate() { + const legacyTemplate = UDATA.AppState('TEMPLATE'); + return legacyTemplate; +} +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +function UpdateLegacyTemplate(newTemplate) { + LOG(...PR('Would update legacy template with', newTemplate)); +} + /// REACT SETTINGS API //////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** API: PropertyDefs define the type and default value of a property, but not @@ -208,6 +223,10 @@ function useSettings(initialSettings = {}) { /// EXPORTS /////////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - module.exports = { + // legacy API + GetLegacyTemplate, + UpdateLegacyTemplate, + // new API GetPropertyDefs, GetMetaDefs, // From 970e0fc7e9d969bf343c4ecfcb5c2aa87ab2d75f Mon Sep 17 00:00:00 2001 From: DSri Seah <11952933+dsriseah@users.noreply.github.com> Date: Fri, 30 May 2025 16:54:57 -0400 Subject: [PATCH 06/38] prop-settings-2: WIP mods to write to Legacy Template from MURTextInput --- _mur/common/util-data-settings.ts | 2 +- _mur/web-client/mur-settings-client.ts | 1 + .../components/MURSettingsEditor.jsx | 8 ++-- .../netcreate/components/MURTextInput.jsx | 5 +-- .../components/react-settings-bridge.js | 37 ++++++++++++++++++- 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/_mur/common/util-data-settings.ts b/_mur/common/util-data-settings.ts index 5e804549d..8b46f2d93 100644 --- a/_mur/common/util-data-settings.ts +++ b/_mur/common/util-data-settings.ts @@ -72,7 +72,7 @@ function DecodeDotProp(dotProp: string): PropToken { if (extra.length > 0) return { error: `invalid dotProp: ${dotProp}` }; if (!IsCamelCase(groupID)) return { error: `invalid group name: ${groupID}` }; if (!IsCamelCase(propID)) return { error: `invalid prop name: ${propID}` }; - return { groupID, propID }; + return { groupID, propID, parts: [groupID, propID] }; } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** a prop submit object is a single key object with a dotProp key and a value, diff --git a/_mur/web-client/mur-settings-client.ts b/_mur/web-client/mur-settings-client.ts index 32a3721e9..226a27cda 100644 --- a/_mur/web-client/mur-settings-client.ts +++ b/_mur/web-client/mur-settings-client.ts @@ -138,3 +138,4 @@ export { Unsubscribe // (scope: string, evHdl: SNA_EvtHandler) => void }; export { GetSettingsContext, useSettings } from './mur-settings-context.ts'; +export { DecodeDotProp } from '../common/util-data-settings.ts'; diff --git a/app/view/netcreate/components/MURSettingsEditor.jsx b/app/view/netcreate/components/MURSettingsEditor.jsx index 0bc66ab05..968a4f96d 100644 --- a/app/view/netcreate/components/MURSettingsEditor.jsx +++ b/app/view/netcreate/components/MURSettingsEditor.jsx @@ -47,11 +47,11 @@ function MURSettingsEditor() { const { name, description } = RSB.GetLegacyTemplate(); const propDefs = { graphSettings: { - graphName: { + name: { type: 'string', value: name }, - graphDescription: { + description: { type: 'string', value: description } @@ -60,12 +60,12 @@ function MURSettingsEditor() { const metaDefs = { _groupMeta: {}, graphSettings: { - graphName: { + name: { label: 'Graph Name', tooltip: 'Name of the graph', placeholder: 'Graph Name' }, - graphDescription: { + description: { label: 'Graph Description', tooltip: 'Description of the graph', placeholder: 'Graph Description' diff --git a/app/view/netcreate/components/MURTextInput.jsx b/app/view/netcreate/components/MURTextInput.jsx index f4c5c39a6..05f6212b9 100644 --- a/app/view/netcreate/components/MURTextInput.jsx +++ b/app/view/netcreate/components/MURTextInput.jsx @@ -63,9 +63,8 @@ function TextInput(props) { // send data to settings object, which will trigger rerender const submitToSettings = async event => { const value = event.target.value; - LOG('would check value', $(value), 'against', propDef); - LOG(`would call RSB.UpdateProperty('${dotProp}', ${$(value)})`); - // api.forceUpdate(); + console.log(dotProp, 'submitToSettings', $(value)); + RSB.UpdateLegacySetting(dotProp, value); }; /// LOCAL EVENT UPDATES /// diff --git a/app/view/netcreate/components/react-settings-bridge.js b/app/view/netcreate/components/react-settings-bridge.js index 52643ff25..1306f2021 100644 --- a/app/view/netcreate/components/react-settings-bridge.js +++ b/app/view/netcreate/components/react-settings-bridge.js @@ -34,6 +34,13 @@ function Unsubscribe(event, changeHandler) { Settings.Unsubscribe(event, changeHandler); } +/// HELPER METHODS //////////////////////////////////////////////////////////// +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/** quote a string or return as-is number */ +function $(strOrNum) { + return typeof strOrNum === 'string' ? `'${strOrNum}'` : strOrNum; +} + /// LEGACY SETTINGS API /////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** API: Get the AppState('TEMPLATE') object, which is the legacy settings @@ -44,8 +51,32 @@ function GetLegacyTemplate() { return legacyTemplate; } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -function UpdateLegacyTemplate(newTemplate) { - LOG(...PR('Would update legacy template with', newTemplate)); +/** API: Update the APPSTATE('TEMPLATE') object. This does NOT save the + * template to the server, it just updates the local state. */ +function UpdateLegacyTemplate() {} +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/** API: Legacy Hack assumes that groupName is not needed; all settings are + * at the top level of the template object. The groupNames are more of a + * nicety for the UI in this implementation*/ +function UpdateLegacySetting(dotProp, value) { + const fn = 'UpdateLegacySetting:'; + const [groupName, propName] = Settings.DecodeDotProp(dotProp).parts; + LOG(...PR(`Would update legacy setting ${groupName}.${propName} with ${$(value)}`)); + const template = GetLegacyTemplate(); + if (template[propName] === undefined) { + console.log(fn, `prop ${propName} not found in legacy template`, template); + } +} +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +function UpdateLegacySettingMeta(dotProp, metaObj) { + LOG( + ...PR( + 'Would update legacy setting meta', + dotProp, + 'with', + JSON.stringify(metaObj) + ) + ); } /// REACT SETTINGS API //////////////////////////////////////////////////////// @@ -226,6 +257,8 @@ module.exports = { // legacy API GetLegacyTemplate, UpdateLegacyTemplate, + UpdateLegacySetting, + UpdateLegacySettingMeta, // new API GetPropertyDefs, GetMetaDefs, From dead0920556b9508410f53651b8753b9dafa0693 Mon Sep 17 00:00:00 2001 From: DSri Seah <11952933+dsriseah@users.noreply.github.com> Date: Sun, 1 Jun 2025 14:32:21 -0400 Subject: [PATCH 07/38] prop-settings-2: WIP to be discarded I'm going to rewrite SettingsContext to hold viewData, not a dictionary of layered properties --- .../netcreate/components/MURPropertyGroup.jsx | 5 ++ .../components/MURSettingsEditor.jsx | 84 ++++++++++++------- .../netcreate/components/MURTextInput.jsx | 18 +--- .../components/react-settings-bridge.js | 47 ++++++++--- 4 files changed, 97 insertions(+), 57 deletions(-) diff --git a/app/view/netcreate/components/MURPropertyGroup.jsx b/app/view/netcreate/components/MURPropertyGroup.jsx index 37e18745e..ea43ba865 100644 --- a/app/view/netcreate/components/MURPropertyGroup.jsx +++ b/app/view/netcreate/components/MURPropertyGroup.jsx @@ -13,6 +13,7 @@ import TextInput from './MURTextInput'; /// CONSTANTS ///////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const DBG = true; +const LOG = console.log.bind(console); /// HELPER METHODS //////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -36,6 +37,7 @@ function PropertyGroup(props) { if (typeof groupDef !== 'object') return

PropertyGroup bad groupDef

; if (typeof metaDef !== 'object') return

PropertyGroup bad metaDef

; } + const gdata = DerefGroupDef(groupDef); if (gdata.error) return

PropertyGroup bad groupDef {gdata.error}

; const { groupName, properties } = gdata; @@ -45,6 +47,7 @@ function PropertyGroup(props) { const key = `pg-${groupName}`; return (
+ {console.log('>>rendering PropertyGroup', properties)}
@@ -52,10 +55,12 @@ function PropertyGroup(props) { {description && (

{description}

)} + {console.log(`>>> RENDERING ${propList.length} PROPERTIES`)} {propList.map(p => { if (properties[p].type) { const dotProp = `${groupName}.${p}`; const key = `in-${dotProp}`; + return ( { + LOG(...PR('MURSettingsEditor mounted')); + RSB.SubscribeLegacyTemplateChanges(handleTemplateChange); + m_old_template = RSB.GetLegacyTemplate(); + setTemplate({ ...m_old_template }); + return () => { + LOG(...PR('MURSettingsEditor unmounted')); + RSB.UnsubscribeLegacyTemplateChanges(handleTemplateChange); + }; + }, []); function saveChanges() { - saveOldState(api.lastSettingsUpdate); - LOG(...PR('would save changes')); + LOG(...PR('would call RSB.SaveLegacyTemplate()')); + // expecting that a template-wide update message will be received + // elsewhere } function revertChanges() { - LOG(...PR('would revert changes')); + LOG(...PR('Reverting changes to old template'), m_old_template); + setPropDefs(m_MakePropDefs(m_old_template)); + setChanges({}); + } + + function handleTemplateChange(changeObj) { + LOG(...PR('Legacy template changed'), changeObj); + setChanges({ ...changes, ...changeObj }); } // new system is no longer used for netcreate; sticking with legacy for now // const propDefs = RSB.GetPropertyDefs(); // const metaDefs = RSB.GetMetaDefs(); - const { name, description } = RSB.GetLegacyTemplate(); - const propDefs = { - graphSettings: { - name: { - type: 'string', - value: name - }, - description: { - type: 'string', - value: description - } - } - }; const metaDefs = { _groupMeta: {}, graphSettings: { @@ -74,19 +100,21 @@ function MURSettingsEditor() { }; const { opBtnStyle, modColor } = RSB.GetStyles(); - - const mod = api.lastSettingsUpdate !== oldState; + const mod = Object.keys(changes).length > 0; const backgroundColor = mod ? modColor : 'white'; const color = mod ? 'black' : 'gray'; const btnStyle = { ...opBtnStyle, backgroundColor, color }; + /// RENDER /// + LOG(...PR('Rendering MURSettingsEditor props', propDefs, changes)); + return ( - -   -
); } diff --git a/app/view/netcreate/components/MURTextInput.jsx b/app/view/netcreate/components/MURTextInput.jsx index 05f6212b9..92753543a 100644 --- a/app/view/netcreate/components/MURTextInput.jsx +++ b/app/view/netcreate/components/MURTextInput.jsx @@ -6,14 +6,12 @@ \*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * /////////////////////////////////////*/ const React = require('react'); -const { Settings } = require('ursys-min'); // import the settings manager const RSB = require('./react-settings-bridge'); /// CONSTANTS ///////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const DBG = true; const LOG = console.log.bind(console); -const SettingsContext = RSB.GetSettingsContext(); /// STYLING OBJECTS /////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -45,19 +43,6 @@ function TextInput(props) { const [oldValue] = React.useState(value || defValue); const [inputValue, setInputValue] = React.useState(value || defValue); - /// CONTEXT /// - - const api = React.useContext(SettingsContext); - - /// TESTS /// - - function assert_is_modified() { - if (label === 'description') - console.log( - `old / input / propDef\n${oldValue} \t${inputValue} \t${propDef.value}` - ); - } - /// UI-SETTINGS INTEROP EVENT UPDATES /// // send data to settings object, which will trigger rerender @@ -122,10 +107,9 @@ function TextInput(props) { const bgColor = mod ? modColor : 'white'; const pad = mod ? '1rem' : '0'; - // assert_is_modified(); - return (
+ {console.log('>> rendering TextInput', dotProp, inputValue)}