3 Commits

Author SHA1 Message Date
Eric Tuvesson
41711c3934 Merge branch 'main' into feature/store-cloudservices-in-project-folder 2024-06-17 10:35:36 +02:00
Eric Tuvesson
5d8b2d5bba Merge branch 'main' into feature/store-cloudservices-in-project-folder 2024-06-03 10:29:29 +02:00
Eric Tuvesson
851dbc98d3 feat: Save Cloud Service in the project folder instead 2024-05-22 10:01:46 +02:00
111 changed files with 1145 additions and 1545 deletions

View File

@@ -1,4 +1,4 @@
name: Build fluxscape-editor name: Build noodl-editor
on: on:
# Allows you to run this workflow manually from the Actions tab # Allows you to run this workflow manually from the Actions tab
@@ -72,6 +72,6 @@ jobs:
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: fluxscape-editor-${{ matrix.platform }}-${{ github.head_ref }}-${{ github.sha }} name: noodl-editor-${{ matrix.platform }}-${{ github.head_ref }}-${{ github.sha }}
path: publish path: publish
retention-days: '12' retention-days: "12"

View File

@@ -1,4 +1,4 @@
name: Test fluxscape-editor name: Test noodl-editor
on: on:
# Allows you to run this workflow manually from the Actions tab # Allows you to run this workflow manually from the Actions tab

347
package-lock.json generated
View File

@@ -2849,9 +2849,9 @@
} }
}, },
"node_modules/@electron/remote": { "node_modules/@electron/remote": {
"version": "2.1.2", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.2.tgz", "resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.1.tgz",
"integrity": "sha512-EPwNx+nhdrTBxyCqXt/pftoQg/ybtWDW3DUWHafejvnB1ZGGfMpv6e15D8KeempocjXe78T7WreyGGb3mlZxdA==", "integrity": "sha512-Lfxul2yBxL+FBVaKszNAkuUqSIDbUQ1I7BC394iRXyqA2XGz7im2bAxroNIM51jhySSPKUaOLHaFLxfV6pC9VQ==",
"peerDependencies": { "peerDependencies": {
"electron": ">= 13.0.0" "electron": ">= 13.0.0"
} }
@@ -25220,13 +25220,13 @@
} }
}, },
"node_modules/electron": { "node_modules/electron": {
"version": "31.3.1", "version": "28.1.4",
"resolved": "https://registry.npmjs.org/electron/-/electron-31.3.1.tgz", "resolved": "https://registry.npmjs.org/electron/-/electron-28.1.4.tgz",
"integrity": "sha512-9fiuWlRhBfygtcT+auRd/WdBK/f8LZZcrpx0RjpXhH2DPTP/PfnkC4JB1PW55qCbGbh4wAgkYbf4ExIag8oGCA==", "integrity": "sha512-WE6go611KOhtH6efRPMnVC7FE7DCKnQ3ZyHFeI1DbaCy8OU4UjZ8/CZGcuZmZgRdxSBEHoHdgaJkWRHZzF0FOg==",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@electron/get": "^2.0.0", "@electron/get": "^2.0.0",
"@types/node": "^20.9.0", "@types/node": "^18.11.18",
"extract-zip": "^2.0.1" "extract-zip": "^2.0.1"
}, },
"bin": { "bin": {
@@ -25452,14 +25452,6 @@
"tiny-typed-emitter": "^2.1.0" "tiny-typed-emitter": "^2.1.0"
} }
}, },
"node_modules/electron/node_modules/@types/node": {
"version": "20.14.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz",
"integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/elliptic": { "node_modules/elliptic": {
"version": "6.5.4", "version": "6.5.4",
"dev": true, "dev": true,
@@ -27211,10 +27203,6 @@
"react": "^15.0.2 || ^16.0.0 || ^17.0.0" "react": "^15.0.2 || ^16.0.0 || ^17.0.0"
} }
}, },
"node_modules/fluxscape-editor": {
"resolved": "packages/noodl-editor",
"link": true
},
"node_modules/focus-lock": { "node_modules/focus-lock": {
"version": "0.8.1", "version": "0.8.1",
"dev": true, "dev": true,
@@ -35455,6 +35443,10 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/noodl-editor": {
"resolved": "packages/noodl-editor",
"link": true
},
"node_modules/nopt": { "node_modules/nopt": {
"version": "1.0.10", "version": "1.0.10",
"license": "MIT", "license": "MIT",
@@ -43199,11 +43191,6 @@
"version": "1.13.6", "version": "1.13.6",
"license": "MIT" "license": "MIT"
}, },
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"node_modules/unfetch": { "node_modules/unfetch": {
"version": "4.2.0", "version": "4.2.0",
"dev": true, "dev": true,
@@ -48980,10 +48967,9 @@
"dev": true "dev": true
}, },
"packages/noodl-editor": { "packages/noodl-editor": {
"name": "fluxscape-editor", "version": "1.0.0",
"version": "1.1.0",
"dependencies": { "dependencies": {
"@electron/remote": "^2.1.2", "@electron/remote": "^2.1.1",
"@jaames/iro": "^5.5.2", "@jaames/iro": "^5.5.2",
"@microsoft/fetch-event-source": "^2.0.1", "@microsoft/fetch-event-source": "^2.0.1",
"@noodl/git": "file:../noodl-git", "@noodl/git": "file:../noodl-git",
@@ -49039,7 +49025,7 @@
"babel-loader": "^8.2.4", "babel-loader": "^8.2.4",
"concurrently": "^7.4.0", "concurrently": "^7.4.0",
"css-loader": "^6.7.1", "css-loader": "^6.7.1",
"electron": "31.3.1", "electron": "28.1.4",
"electron-builder": "^24.9.1", "electron-builder": "^24.9.1",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"html-loader": "^3.1.0", "html-loader": "^3.1.0",
@@ -53389,9 +53375,9 @@
} }
}, },
"@electron/remote": { "@electron/remote": {
"version": "2.1.2", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.2.tgz", "resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.1.tgz",
"integrity": "sha512-EPwNx+nhdrTBxyCqXt/pftoQg/ybtWDW3DUWHafejvnB1ZGGfMpv6e15D8KeempocjXe78T7WreyGGb3mlZxdA==", "integrity": "sha512-Lfxul2yBxL+FBVaKszNAkuUqSIDbUQ1I7BC394iRXyqA2XGz7im2bAxroNIM51jhySSPKUaOLHaFLxfV6pC9VQ==",
"requires": {} "requires": {}
}, },
"@electron/universal": { "@electron/universal": {
@@ -74071,23 +74057,13 @@
} }
}, },
"electron": { "electron": {
"version": "31.3.1", "version": "28.1.4",
"resolved": "https://registry.npmjs.org/electron/-/electron-31.3.1.tgz", "resolved": "https://registry.npmjs.org/electron/-/electron-28.1.4.tgz",
"integrity": "sha512-9fiuWlRhBfygtcT+auRd/WdBK/f8LZZcrpx0RjpXhH2DPTP/PfnkC4JB1PW55qCbGbh4wAgkYbf4ExIag8oGCA==", "integrity": "sha512-WE6go611KOhtH6efRPMnVC7FE7DCKnQ3ZyHFeI1DbaCy8OU4UjZ8/CZGcuZmZgRdxSBEHoHdgaJkWRHZzF0FOg==",
"requires": { "requires": {
"@electron/get": "^2.0.0", "@electron/get": "^2.0.0",
"@types/node": "^20.9.0", "@types/node": "^18.11.18",
"extract-zip": "^2.0.1" "extract-zip": "^2.0.1"
},
"dependencies": {
"@types/node": {
"version": "20.14.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz",
"integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==",
"requires": {
"undici-types": "~5.26.4"
}
}
} }
}, },
"electron-builder": { "electron-builder": {
@@ -75432,144 +75408,6 @@
"fbjs": "^3.0.1" "fbjs": "^3.0.1"
} }
}, },
"fluxscape-editor": {
"version": "file:packages/noodl-editor",
"requires": {
"@babel/core": "^7.19.1",
"@babel/preset-react": "^7.18.6",
"@electron/remote": "^2.1.2",
"@jaames/iro": "^5.5.2",
"@microsoft/fetch-event-source": "^2.0.1",
"@noodl/git": "file:../noodl-git",
"@noodl/noodl-parse-dashboard": "file:../noodl-parse-dashboard",
"@noodl/platform": "file:../noodl-platform",
"@noodl/platform-electron": "file:../noodl-platform-electron",
"@svgr/webpack": "^6.4.0",
"@types/checksum": "^0.1.33",
"@types/jasmine": "^4.3.0",
"@types/jquery": "^3.5.14",
"@types/react": "^17.0.50",
"@types/react-dom": "^18.0.0",
"@types/remarkable": "^2.0.3",
"@types/rimraf": "^3.0.2",
"@types/split2": "^3.2.1",
"@types/string.prototype.matchall": "^4.0.1",
"@types/underscore": "^1.11.4",
"@types/webpack-env": "^1.18.0",
"about-window": "^1.15.2",
"algoliasearch": "^4.14.2",
"archiver": "^5.3.0",
"async": "^3.2.4",
"babel-loader": "^8.2.4",
"classnames": "^2.3.2",
"concurrently": "^7.4.0",
"css-loader": "^6.7.1",
"diff3": "0.0.4",
"dmg-license": "^1.0.11",
"electron": "31.3.1",
"electron-builder": "^24.9.1",
"electron-store": "^8.1.0",
"electron-updater": "^6.1.7",
"express": "^4.17.3",
"file-loader": "^6.2.0",
"highlight.js": "^11.5.1",
"html-loader": "^3.1.0",
"isbinaryfile": "^5.0.0",
"md5": "^2.3.0",
"md5-file": "^5.0.0",
"mixpanel-browser": "^2.45.0",
"mkdirp": "0.5.1",
"mkdirp-sync": "0.0.2",
"monaco-editor": "^0.34.0",
"monaco-editor-webpack-plugin": "^7.0.1",
"ncp": "^2.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.0",
"react-hot-toast": "^2.4.0",
"react-instantsearch-hooks-web": "^6.38.0",
"react-json-view": "^1.21.3",
"react-rnd": "^10.3.7",
"remarkable": "^2.0.1",
"rimraf": "^3.0.2",
"s3": "git+https://github.com/noodlapp/node-s3-client.git",
"sass": "^1.55.0",
"sass-loader": "^12.6.0",
"string.prototype.matchall": "^4.0.7",
"stringify": "^5.2.0",
"style-loader": "^3.3.1",
"ts-loader": "^9.4.1",
"ts-node": "^10.7.0",
"typescript": "^4.8.3",
"underscore": "^1.13.6",
"url-loader": "^4.1.1",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.11.1",
"webpack-merge": "^5.8.0",
"websocket-stream": "^5.5.2",
"ws": "^8.9.0"
},
"dependencies": {
"@webpack-cli/configtest": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz",
"integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==",
"dev": true,
"requires": {}
},
"@webpack-cli/info": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz",
"integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==",
"dev": true,
"requires": {
"envinfo": "^7.7.3"
}
},
"@webpack-cli/serve": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz",
"integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==",
"dev": true,
"requires": {}
},
"commander": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
"dev": true
},
"rechoir": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
"integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
"dev": true,
"requires": {
"resolve": "^1.9.0"
}
},
"webpack-cli": {
"version": "4.10.0",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz",
"integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==",
"dev": true,
"requires": {
"@discoveryjs/json-ext": "^0.5.0",
"@webpack-cli/configtest": "^1.2.0",
"@webpack-cli/info": "^1.5.0",
"@webpack-cli/serve": "^1.7.0",
"colorette": "^2.0.14",
"commander": "^7.0.0",
"cross-spawn": "^7.0.3",
"fastest-levenshtein": "^1.0.12",
"import-local": "^3.0.2",
"interpret": "^2.2.0",
"rechoir": "^0.7.0",
"webpack-merge": "^5.7.3"
}
}
}
},
"focus-lock": { "focus-lock": {
"version": "0.8.1", "version": "0.8.1",
"dev": true, "dev": true,
@@ -81145,6 +80983,144 @@
"version": "2.0.10", "version": "2.0.10",
"dev": true "dev": true
}, },
"noodl-editor": {
"version": "file:packages/noodl-editor",
"requires": {
"@babel/core": "^7.19.1",
"@babel/preset-react": "^7.18.6",
"@electron/remote": "^2.1.1",
"@jaames/iro": "^5.5.2",
"@microsoft/fetch-event-source": "^2.0.1",
"@noodl/git": "file:../noodl-git",
"@noodl/noodl-parse-dashboard": "file:../noodl-parse-dashboard",
"@noodl/platform": "file:../noodl-platform",
"@noodl/platform-electron": "file:../noodl-platform-electron",
"@svgr/webpack": "^6.4.0",
"@types/checksum": "^0.1.33",
"@types/jasmine": "^4.3.0",
"@types/jquery": "^3.5.14",
"@types/react": "^17.0.50",
"@types/react-dom": "^18.0.0",
"@types/remarkable": "^2.0.3",
"@types/rimraf": "^3.0.2",
"@types/split2": "^3.2.1",
"@types/string.prototype.matchall": "^4.0.1",
"@types/underscore": "^1.11.4",
"@types/webpack-env": "^1.18.0",
"about-window": "^1.15.2",
"algoliasearch": "^4.14.2",
"archiver": "^5.3.0",
"async": "^3.2.4",
"babel-loader": "^8.2.4",
"classnames": "^2.3.2",
"concurrently": "^7.4.0",
"css-loader": "^6.7.1",
"diff3": "0.0.4",
"dmg-license": "^1.0.11",
"electron": "28.1.4",
"electron-builder": "^24.9.1",
"electron-store": "^8.1.0",
"electron-updater": "^6.1.7",
"express": "^4.17.3",
"file-loader": "^6.2.0",
"highlight.js": "^11.5.1",
"html-loader": "^3.1.0",
"isbinaryfile": "^5.0.0",
"md5": "^2.3.0",
"md5-file": "^5.0.0",
"mixpanel-browser": "^2.45.0",
"mkdirp": "0.5.1",
"mkdirp-sync": "0.0.2",
"monaco-editor": "^0.34.0",
"monaco-editor-webpack-plugin": "^7.0.1",
"ncp": "^2.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.0",
"react-hot-toast": "^2.4.0",
"react-instantsearch-hooks-web": "^6.38.0",
"react-json-view": "^1.21.3",
"react-rnd": "^10.3.7",
"remarkable": "^2.0.1",
"rimraf": "^3.0.2",
"s3": "git+https://github.com/noodlapp/node-s3-client.git",
"sass": "^1.55.0",
"sass-loader": "^12.6.0",
"string.prototype.matchall": "^4.0.7",
"stringify": "^5.2.0",
"style-loader": "^3.3.1",
"ts-loader": "^9.4.1",
"ts-node": "^10.7.0",
"typescript": "^4.8.3",
"underscore": "^1.13.6",
"url-loader": "^4.1.1",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.11.1",
"webpack-merge": "^5.8.0",
"websocket-stream": "^5.5.2",
"ws": "^8.9.0"
},
"dependencies": {
"@webpack-cli/configtest": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz",
"integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==",
"dev": true,
"requires": {}
},
"@webpack-cli/info": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz",
"integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==",
"dev": true,
"requires": {
"envinfo": "^7.7.3"
}
},
"@webpack-cli/serve": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz",
"integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==",
"dev": true,
"requires": {}
},
"commander": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
"dev": true
},
"rechoir": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
"integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
"dev": true,
"requires": {
"resolve": "^1.9.0"
}
},
"webpack-cli": {
"version": "4.10.0",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz",
"integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==",
"dev": true,
"requires": {
"@discoveryjs/json-ext": "^0.5.0",
"@webpack-cli/configtest": "^1.2.0",
"@webpack-cli/info": "^1.5.0",
"@webpack-cli/serve": "^1.7.0",
"colorette": "^2.0.14",
"commander": "^7.0.0",
"cross-spawn": "^7.0.3",
"fastest-levenshtein": "^1.0.12",
"import-local": "^3.0.2",
"interpret": "^2.2.0",
"rechoir": "^0.7.0",
"webpack-merge": "^5.7.3"
}
}
}
},
"nopt": { "nopt": {
"version": "1.0.10", "version": "1.0.10",
"requires": { "requires": {
@@ -86427,11 +86403,6 @@
"underscore": { "underscore": {
"version": "1.13.6" "version": "1.13.6"
}, },
"undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"unfetch": { "unfetch": {
"version": "4.2.0", "version": "4.2.0",
"dev": true "dev": true

View File

@@ -12,7 +12,7 @@
"graph": "npx nx graph", "graph": "npx nx graph",
"ci:prepare:editor": "ts-node ./scripts/ci-editor-prepare.ts", "ci:prepare:editor": "ts-node ./scripts/ci-editor-prepare.ts",
"ci:build:viewer": "lerna exec --scope @noodl/noodl-viewer-react -- npm run build", "ci:build:viewer": "lerna exec --scope @noodl/noodl-viewer-react -- npm run build",
"ci:build:editor": "lerna exec --scope fluxscape-editor -- npm run ci:build", "ci:build:editor": "lerna exec --scope noodl-editor -- npm run ci:build",
"build:editor": "ts-node ./scripts/build-editor.ts", "build:editor": "ts-node ./scripts/build-editor.ts",
"build:editor:_viewer": "ts-node ./scripts/noodl-editor/build-viewer.ts", "build:editor:_viewer": "ts-node ./scripts/noodl-editor/build-viewer.ts",
"build:editor:_editor": "ts-node ./scripts/noodl-editor/build-editor.ts", "build:editor:_editor": "ts-node ./scripts/noodl-editor/build-editor.ts",
@@ -20,7 +20,7 @@
"build:cloud-runtime": "lerna run build --scope @noodl/cloud-runtime --stream && lerna run build:pack --scope @noodl/cloud-runtime --stream", "build:cloud-runtime": "lerna run build --scope @noodl/cloud-runtime --stream && lerna run build:pack --scope @noodl/cloud-runtime --stream",
"start:storybook": "lerna exec --scope @noodl/noodl-core-ui -- npm run start", "start:storybook": "lerna exec --scope @noodl/noodl-core-ui -- npm run start",
"start:viewer": "lerna run start --scope @noodl/noodl-viewer-react --stream", "start:viewer": "lerna run start --scope @noodl/noodl-viewer-react --stream",
"start:editor": "lerna run start --scope fluxscape-editor --stream", "start:editor": "lerna run start --scope noodl-editor --stream",
"dev": "ts-node ./scripts/start.ts", "dev": "ts-node ./scripts/start.ts",
"start": "ts-node ./scripts/start.ts -- --build-viewer", "start": "ts-node ./scripts/start.ts -- --build-viewer",
"test:editor": "ts-node ./scripts/test-editor.ts", "test:editor": "ts-node ./scripts/test-editor.ts",

View File

@@ -9,11 +9,6 @@
right: 0; right: 0;
bottom: 0; bottom: 0;
&.is-hidden {
visibility: hidden;
pointer-events: none;
}
&.has-backdrop { &.has-backdrop {
background-color: var(--theme-color-bg-1-transparent); background-color: var(--theme-color-bg-1-transparent);
} }

View File

@@ -42,7 +42,6 @@ export interface BaseDialogProps extends UnsafeStyleProps {
isVisible?: boolean; isVisible?: boolean;
hasBackdrop?: boolean; hasBackdrop?: boolean;
hasArrow?: boolean; hasArrow?: boolean;
alwaysMounted?: boolean;
children?: Slot; children?: Slot;
@@ -70,7 +69,6 @@ export function CoreBaseDialog({
isVisible, isVisible,
hasBackdrop, hasBackdrop,
hasArrow, hasArrow,
alwaysMounted,
children, children,
@@ -263,7 +261,7 @@ export function CoreBaseDialog({
} }
}, [background]); }, [background]);
if (!isVisible && !alwaysMounted) return null; if (!isVisible) return null;
return ( return (
<div <div
@@ -272,7 +270,6 @@ export function CoreBaseDialog({
hasBackdrop && css['has-backdrop'], hasBackdrop && css['has-backdrop'],
isLockingScroll && css['is-locking-scroll'], isLockingScroll && css['is-locking-scroll'],
typeof triggerRef === 'undefined' && css['is-centered'], typeof triggerRef === 'undefined' && css['is-centered'],
!isVisible && css['is-hidden'],
css[variant] css[variant]
)} )}
onClick={onClose} onClick={onClose}

View File

@@ -1,10 +1,10 @@
{ {
"name": "fluxscape-editor", "name": "noodl-editor",
"productName": "Fluxscape", "productName": "Fluxscape",
"description": "Node-Based App Builder for Scalability & Rapid Development, a fork of Noodl", "description": "Node-Based App Builder for Scalability & Rapid Development, a fork of Noodl",
"author": "Fluxscape <contact@fluxscape.io>", "author": "Fluxscape <contact@fluxscape.io>",
"homepage": "https://fluxscape.io", "homepage": "https://fluxscape.io",
"version": "1.1.0", "version": "1.0.0",
"main": "src/main/main.js", "main": "src/main/main.js",
"scripts": { "scripts": {
"build": "npx ts-node -P ./tsconfig.build.json ./scripts/build.ts", "build": "npx ts-node -P ./tsconfig.build.json ./scripts/build.ts",
@@ -58,7 +58,7 @@
] ]
}, },
"dependencies": { "dependencies": {
"@electron/remote": "^2.1.2", "@electron/remote": "^2.1.1",
"@jaames/iro": "^5.5.2", "@jaames/iro": "^5.5.2",
"@microsoft/fetch-event-source": "^2.0.1", "@microsoft/fetch-event-source": "^2.0.1",
"@noodl/git": "file:../noodl-git", "@noodl/git": "file:../noodl-git",
@@ -114,7 +114,7 @@
"babel-loader": "^8.2.4", "babel-loader": "^8.2.4",
"concurrently": "^7.4.0", "concurrently": "^7.4.0",
"css-loader": "^6.7.1", "css-loader": "^6.7.1",
"electron": "31.3.1", "electron": "28.1.4",
"electron-builder": "^24.9.1", "electron-builder": "^24.9.1",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"html-loader": "^3.1.0", "html-loader": "^3.1.0",

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM19 18H6c-2.21 0-4-1.79-4-4 0-2.05 1.53-3.76 3.56-3.97l1.07-.11.5-.95C8.08 7.14 9.94 6 12 6c2.62 0 4.88 1.86 5.39 4.43l.3 1.5 1.53.11c1.56.1 2.78 1.41 2.78 2.96 0 1.65-1.35 3-3 3zm-9-3.82l-2.09-2.09L6.5 13.5 10 17l6.01-6.01-1.41-1.41z"/></svg>

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>

After

Width:  |  Height:  |  Size: 175 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none"><path d="M0 0h24v24H0V0z"/><path opacity=".87" d="M0 0h24v24H0V0z"/></g><path d="M21 3.01H3c-1.1 0-2 .9-2 2V9h2V4.99h18v14.03H3V15H1v4.01c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98v-14c0-1.11-.9-2-2-2zM11 16l4-4-4-4v3H1v2h10v3zM23 3.01H1V9h2V4.99h18v14.03H3V15H1v5.99h22V3.01zM11 16l4-4-4-4v3H1v2h10v3z"/></svg>

After

Width:  |  Height:  |  Size: 408 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M4 6H2v16h16v-2H4V6zm18-4H6v16h16V2zm-3 9H9V9h10v2zm-4 4H9v-2h6v2zm4-8H9V5h10v2z"/></svg>

After

Width:  |  Height:  |  Size: 220 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none"><path d="M0 0h24v24H0V0z"/><path opacity=".87" d="M0 0h24v24H0V0z"/></g><path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7zm-4 6h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"/></svg>

After

Width:  |  Height:  |  Size: 360 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M19.44 12.99l-.01.02c.04-.33.08-.67.08-1.01 0-.34-.03-.66-.07-.99l.01.02 2.44-1.92-2.43-4.22-2.87 1.16.01.01c-.52-.4-1.09-.74-1.71-1h.01L14.44 2H9.57l-.44 3.07h.01c-.62.26-1.19.6-1.71 1l.01-.01-2.88-1.17-2.44 4.22 2.44 1.92.01-.02c-.04.33-.07.65-.07.99 0 .34.03.68.08 1.01l-.01-.02-2.1 1.65-.33.26 2.43 4.2 2.88-1.15-.02-.04c.53.41 1.1.75 1.73 1.01h-.03L9.58 22h4.85s.03-.18.06-.42l.38-2.65h-.01c.62-.26 1.2-.6 1.73-1.01l-.02.04 2.88 1.15 2.43-4.2s-.14-.12-.33-.26l-2.11-1.66zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"/></svg>

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 KiB

View File

@@ -1,142 +0,0 @@
import React, { createContext, useContext, useState, useEffect } from 'react';
import { type ComponentModel } from '@noodl-models/componentmodel';
import { type NodeGraphNode } from '@noodl-models/nodegraphmodel';
import { type NodeLibraryNodeType } from '@noodl-models/nodelibrary';
import { type Slot } from '@noodl-core-ui/types/global';
import { ProjectModel } from '@noodl-models/projectmodel';
import { EventDispatcher } from '../../../../shared/utils/EventDispatcher';
export type NodeReference = {
type: NodeLibraryNodeType | undefined;
displayName: string;
referenaces: {
displayName: string;
node?: NodeGraphNode;
component: ComponentModel;
}[];
};
export interface NodeReferencesContext {
nodeReferences: NodeReference[];
}
const NodeReferencesContext = createContext<NodeReferencesContext>({
nodeReferences: [],
});
// Since all the editor code is not written in React we need a way to be able to
// access this information outside of React too.
let HACK_nodeReferences: NodeReference[] = [];
export function HACK_findNodeReference(componentName: string): NodeReference | undefined {
return HACK_nodeReferences.find(x => x.type?.fullName === componentName);
}
export interface NodeReferencesContextProps {
children: Slot;
}
export function NodeReferencesContextProvider({ children }: NodeReferencesContextProps) {
const [group] = useState({});
const [nodeReferences, setNodeReferences] = useState<NodeReference[]>([]);
useEffect(() => {
function updateIndex() {
const types: { [key: string]: NodeReference['type'] } = {};
const references = new Map<string, NodeReference['referenaces']>();
function handleComponent(component: ComponentModel) {
component.forEachNode((node: NodeGraphNode) => {
const name = node.type.name;
// Add the reference
references.set(name, [
...(references.get(name) || []),
{
displayName: component.displayName || component.name,
node,
component
}
]);
// Repeater
if (name === 'For Each' && node.parameters.template) {
const templateComponent = ProjectModel.instance.getComponentWithName(node.parameters.template);
if (templateComponent) {
references.set(templateComponent.fullName, [
...(references.get(templateComponent.fullName) || []),
{
displayName: component.displayName || component.name,
node,
component
}
]);
handleComponent(templateComponent);
}
}
// Add some metadata for this node if we dont have it yet.
if (!types[name]) {
types[name] = node.type;
}
});
}
// Loop all the nodes in the project
ProjectModel.instance.forEachComponent(handleComponent);
// Combine the result to look a little better.
const results: NodeReference[] = Array.from(references.keys())
.map((key) => ({
type: types[key],
displayName: types[key]?.displayName || key,
referenaces: references.get(key)
}))
.sort((a, b) => b.referenaces.length - a.referenaces.length);
HACK_nodeReferences = results;
setNodeReferences(results);
}
updateIndex();
EventDispatcher.instance.on(
[
'Model.nodeAdded',
'Model.nodeRemoved',
'Model.componentAdded',
'Model.componentRemoved',
'Model.componentRenamed'
],
updateIndex,
group
);
return function () {
EventDispatcher.instance.off(group);
};
}, []);
return (
<NodeReferencesContext.Provider
value={{
nodeReferences,
}}
>
{children}
</NodeReferencesContext.Provider>
);
}
export function useNodeReferencesContext() {
const context = useContext(NodeReferencesContext);
if (context === undefined) {
throw new Error('useNodeReferencesContext must be a child of NodeReferencesContextProvider');
}
return context;
}

View File

@@ -1 +0,0 @@
export * from './NodeReferencesContext';

View File

@@ -48,7 +48,7 @@ export async function execute(command: Command, event: MessageEvent) {
// Deploy to temp folder // Deploy to temp folder
await compilation.deployToFolder(tempDir, { await compilation.deployToFolder(tempDir, {
environment: command.cloudService ? new Environment(command.cloudService) : undefined environment: command.cloudService ? new Environment("", command.cloudService) : undefined
}); });
// Upload to S3 // Upload to S3

View File

@@ -1,9 +1,9 @@
import { Environment, ExternalCloudService } from '@noodl-models/CloudServices/ExternalCloudService';
import { import {
CloudServiceEvent, CloudServiceEvent,
CloudServiceEvents, CloudServiceEvents,
CreateEnvironment, CreateEnvironment,
CreateEnvironmentRequest, CreateEnvironmentRequest,
Environment,
ICloudBackendService, ICloudBackendService,
ICloudService, ICloudService,
UpdateEnvironmentRequest UpdateEnvironmentRequest
@@ -12,6 +12,9 @@ import { ProjectModel } from '@noodl-models/projectmodel';
import { getCloudServices } from '@noodl-models/projectmodel.editor'; import { getCloudServices } from '@noodl-models/projectmodel.editor';
import { Model } from '@noodl-utils/model'; import { Model } from '@noodl-utils/model';
import { GlobalCloudService } from './providers/GlobalCloudService';
import { ProjectCloudService } from './providers/ProjectCloudService';
export type CloudQueueItem = { export type CloudQueueItem = {
frontendId: string; frontendId: string;
environmentId: string; environmentId: string;
@@ -20,7 +23,9 @@ export type CloudQueueItem = {
class CloudBackendService implements ICloudBackendService { class CloudBackendService implements ICloudBackendService {
private _isLoading = false; private _isLoading = false;
private _collection?: Environment[]; private _collection?: Environment[];
private _localExternal = new ExternalCloudService();
private _globalProvider = new GlobalCloudService();
private _projectProvider = new ProjectCloudService();
get isLoading(): boolean { get isLoading(): boolean {
return this._isLoading; return this._isLoading;
@@ -32,12 +37,14 @@ class CloudBackendService implements ICloudBackendService {
constructor(private readonly service: CloudService) {} constructor(private readonly service: CloudService) {}
async fetch(): Promise<Environment[]> { async fetch(project: ProjectModel): Promise<Environment[]> {
this._isLoading = true; this._isLoading = true;
try { try {
// Fetch environments from local machine const projectResults = await this._projectProvider.list(project);
const localEnvironments = await this._localExternal.list(); this._collection = projectResults.map((x) => new Environment("project", x));
this._collection = localEnvironments.map((x) => new Environment(x));
const globalResults = await this._globalProvider.list(project);
this._collection = this._collection.concat(globalResults.map((x) => new Environment("global", x)));
} finally { } finally {
this._isLoading = false; this._isLoading = false;
this.service.notifyListeners(CloudServiceEvent.BackendUpdated); this.service.notifyListeners(CloudServiceEvent.BackendUpdated);
@@ -48,7 +55,7 @@ class CloudBackendService implements ICloudBackendService {
async fromProject(project: ProjectModel): Promise<Environment> { async fromProject(project: ProjectModel): Promise<Environment> {
const activeCloudServices = getCloudServices(project); const activeCloudServices = getCloudServices(project);
if (!this._collection) { if (!this._collection) {
await this.fetch(); await this.fetch(project);
} }
return this.items.find((b) => { return this.items.find((b) => {
@@ -56,16 +63,16 @@ class CloudBackendService implements ICloudBackendService {
}); });
} }
async create(options: CreateEnvironmentRequest): Promise<CreateEnvironment> { async create(project: ProjectModel, options: CreateEnvironmentRequest): Promise<CreateEnvironment> {
return await this._localExternal.create(options); return await this._projectProvider.create(project, options);
} }
async update(options: UpdateEnvironmentRequest): Promise<boolean> { async update(project: ProjectModel, options: UpdateEnvironmentRequest): Promise<boolean> {
return await this._localExternal.update(options); return await this._projectProvider.update(project, options);
} }
async delete(id: string): Promise<boolean> { async delete(project: ProjectModel, id: string): Promise<boolean> {
return await this._localExternal.delete(id); return await this._projectProvider.delete(project, id);
} }
} }
@@ -95,15 +102,11 @@ export class CloudService extends Model<CloudServiceEvent, CloudServiceEvents> i
*/ */
public async prefetch() { public async prefetch() {
this.reset(); this.reset();
await this.fetch(); await this.backend.fetch(ProjectModel.instance);
}
public async fetch() {
await this.backend.fetch();
} }
public async getActiveEnvironment(project: ProjectModel): Promise<Environment> { public async getActiveEnvironment(project: ProjectModel): Promise<Environment> {
await this.backend.fetch(); await this.backend.fetch(project);
return this.backend.fromProject(project); return this.backend.fromProject(project);
} }
} }

View File

@@ -1,3 +1,2 @@
export * from './CloudService'; export * from './CloudService';
export * from './type'; export * from './type';
export * from './ExternalCloudService';

View File

@@ -1,25 +1,21 @@
import { JSONStorage } from '@noodl/platform'; import { JSONStorage } from '@noodl/platform';
import { CreateEnvironment, CreateEnvironmentRequest, UpdateEnvironmentRequest } from '@noodl-models/CloudServices'; import {
CreateEnvironment,
CreateEnvironmentRequest,
EnvironmentDataFormat,
ICloudServiceProvider,
UpdateEnvironmentRequest
} from '@noodl-models/CloudServices';
import { ProjectModel } from '@noodl-models/projectmodel';
/** The data format is separated from our internal model. */ export class GlobalCloudService implements ICloudServiceProvider {
export type EnvironmentDataFormat = { async list(_project: ProjectModel): Promise<EnvironmentDataFormat[]> {
enabled: boolean;
id: string;
name: string;
description: string;
masterKey: string;
appId: string;
endpoint: string;
};
export class ExternalCloudService {
async list(): Promise<EnvironmentDataFormat[]> {
const local = await JSONStorage.get('externalBrokers'); const local = await JSONStorage.get('externalBrokers');
return local.brokers || []; return local.brokers || [];
} }
async create(options: CreateEnvironmentRequest): Promise<CreateEnvironment> { async create(_project: ProjectModel, options: CreateEnvironmentRequest): Promise<CreateEnvironment> {
const id = `${options.url}-${options.appId}`; const id = `${options.url}-${options.appId}`;
const newBroker: EnvironmentDataFormat = { const newBroker: EnvironmentDataFormat = {
@@ -44,7 +40,7 @@ export class ExternalCloudService {
}; };
} }
async update(options: UpdateEnvironmentRequest): Promise<boolean> { async update(_project: ProjectModel, options: UpdateEnvironmentRequest): Promise<boolean> {
const local = await JSONStorage.get('externalBrokers'); const local = await JSONStorage.get('externalBrokers');
const brokers: EnvironmentDataFormat[] = local.brokers || []; const brokers: EnvironmentDataFormat[] = local.brokers || [];
@@ -63,7 +59,7 @@ export class ExternalCloudService {
return true; return true;
} }
async delete(id: string): Promise<boolean> { async delete(_project: ProjectModel, id: string): Promise<boolean> {
const local = await JSONStorage.get('externalBrokers'); const local = await JSONStorage.get('externalBrokers');
const brokers: EnvironmentDataFormat[] = local.brokers || []; const brokers: EnvironmentDataFormat[] = local.brokers || [];
@@ -79,25 +75,3 @@ export class ExternalCloudService {
return true; return true;
} }
} }
export class Environment {
id: string;
name: string;
description: string;
createdAt: string;
masterKeyUpdatedAt: string;
masterKey: string;
appId: string;
url: string;
constructor(item: EnvironmentDataFormat) {
this.id = item.id;
this.name = item.name;
this.description = item.description;
this.createdAt = '';
this.masterKeyUpdatedAt = '';
this.masterKey = item.masterKey;
this.appId = item.appId;
this.url = item.endpoint;
}
}

View File

@@ -0,0 +1,101 @@
import { filesystem } from '@noodl/platform';
import {
CreateEnvironment,
CreateEnvironmentRequest,
EnvironmentDataFormat,
ICloudServiceProvider,
UpdateEnvironmentRequest
} from '@noodl-models/CloudServices';
import { ProjectModel } from '@noodl-models/projectmodel';
/**
* Store the Cloud Service relative to the project folder.
*/
export class ProjectCloudService implements ICloudServiceProvider {
private async load(project: ProjectModel) {
const dirpath = filesystem.resolve(project._retainedProjectDirectory, ".noodl");
const filepath = filesystem.resolve(dirpath, "cloudservices.json");
if (!filesystem.exists(filepath)) {
return []
}
// TODO: Validate file content
return filesystem.readJson(filepath);
}
private async save(project: ProjectModel, items: EnvironmentDataFormat[]) {
const dirpath = filesystem.resolve(project._retainedProjectDirectory, ".noodl");
const filepath = filesystem.resolve(dirpath, "cloudservices.json");
if (!filesystem.exists(filepath)) {
await filesystem.makeDirectory(dirpath);
}
await filesystem.writeJson(filepath, items);
}
async list(project: ProjectModel): Promise<EnvironmentDataFormat[]> {
if (!project || !project._retainedProjectDirectory) {
return []
}
return await this.load(project);
}
async create(project: ProjectModel, options: CreateEnvironmentRequest): Promise<CreateEnvironment> {
const id = `${options.url}-${options.appId}`;
const newBroker: EnvironmentDataFormat = {
enabled: true,
id,
name: options.name,
description: options.description,
masterKey: options.masterKey,
appId: options.appId,
endpoint: options.url
};
const current = await this.load(project);
await this.save(project, [...current, newBroker]);
return {
id: newBroker.id,
appId: newBroker.appId,
url: newBroker.endpoint,
masterKey: newBroker.masterKey
};
}
async update(project: ProjectModel, options: UpdateEnvironmentRequest): Promise<boolean> {
const current = await this.load(project);
// Find and update
const broker = current.find((x) => x.id === options.id);
if (!broker) return false;
if (typeof options.name !== undefined) broker.name = options.name;
if (typeof options.description !== undefined) broker.description = options.description;
if (typeof options.appId !== undefined) broker.appId = options.appId;
if (typeof options.masterKey !== undefined) broker.masterKey = options.masterKey;
if (typeof options.url !== undefined) broker.endpoint = options.url;
await this.save(project, current);
return true;
}
async delete(project: ProjectModel, id: string): Promise<boolean> {
const current = await this.load(project);
// Find the environment
const found = current.find((b) => b.id === id);
if (found) {
// Delete the environment
current.splice(current.indexOf(found), 1);
}
// Save the list
await this.save(project, current);
return true;
}
}

View File

@@ -1,4 +1,3 @@
import { Environment } from '@noodl-models/CloudServices';
import { ProjectModel } from '@noodl-models/projectmodel'; import { ProjectModel } from '@noodl-models/projectmodel';
import { IModel } from '@noodl-utils/model'; import { IModel } from '@noodl-utils/model';
@@ -30,11 +29,11 @@ export interface ICloudBackendService {
get isLoading(): boolean; get isLoading(): boolean;
get items(): Environment[]; get items(): Environment[];
fetch(): Promise<Environment[]>; fetch(project: ProjectModel): Promise<Environment[]>;
fromProject(project: ProjectModel): Promise<Environment> | undefined; fromProject(project: ProjectModel): Promise<Environment> | undefined;
create(options: CreateEnvironmentRequest): Promise<CreateEnvironment>; create(project: ProjectModel, options: CreateEnvironmentRequest): Promise<CreateEnvironment>;
update(options: UpdateEnvironmentRequest): Promise<boolean>; update(project: ProjectModel, options: UpdateEnvironmentRequest): Promise<boolean>;
delete(id: string): Promise<boolean>; delete(project: ProjectModel, id: string): Promise<boolean>;
} }
export enum CloudServiceEvent { export enum CloudServiceEvent {
@@ -55,3 +54,54 @@ export interface ICloudService extends IModel<CloudServiceEvent, CloudServiceEve
get backend(): ICloudBackendService; get backend(): ICloudBackendService;
} }
/** The data format is separated from our internal model. */
export type EnvironmentDataFormat = {
enabled: boolean;
id: string;
name: string;
description: string;
masterKey: string;
appId: string;
endpoint: string;
};
export class Environment {
id: string;
name: string;
description: string;
createdAt: string;
masterKeyUpdatedAt: string;
masterKey: string;
appId: string;
url: string;
provider: string;
get typeDisplayName() {
switch (this.provider) {
case "project": return "Self hosted";
default: return "Global Self hosted"
}
}
constructor(provider: string, item: EnvironmentDataFormat) {
this.provider = provider;
this.id = item.id;
this.name = item.name;
this.description = item.description;
this.createdAt = '';
this.masterKeyUpdatedAt = '';
this.masterKey = item.masterKey;
this.appId = item.appId;
this.url = item.endpoint;
}
}
export interface ICloudServiceProvider {
list(project: ProjectModel | undefined): Promise<EnvironmentDataFormat[]>;
create(project: ProjectModel | undefined, options: CreateEnvironmentRequest): Promise<CreateEnvironment>;
update(project: ProjectModel | undefined, options: UpdateEnvironmentRequest): Promise<boolean>;
delete(project: ProjectModel | undefined, id: string): Promise<boolean>;
}

View File

@@ -57,13 +57,10 @@ export class CloudFunctionAdapter extends NodeTypeAdapter {
// Collect all cloud function components // Collect all cloud function components
const functionRequestNodes = ProjectModel.instance.getNodesWithType('noodl.cloud.request'); const functionRequestNodes = ProjectModel.instance.getNodesWithType('noodl.cloud.request');
const functions = functionRequestNodes const functions = functionRequestNodes.map((r) => {
.map((r) => {
const component = r.owner.owner; const component = r.owner.owner;
return component.fullName; return component.fullName;
}) });
// Remove all the Cloud Trigger functions
.filter((x) => !x.startsWith('/#__cloud__/__noodl_cloud_triggers__'));
ports.push({ ports.push({
plug: 'input', plug: 'input',

View File

@@ -1,6 +1,4 @@
import { NodeGraphContextProvider } from '@noodl-contexts/NodeGraphContext/NodeGraphContext'; import { NodeGraphContextProvider } from '@noodl-contexts/NodeGraphContext/NodeGraphContext';
import { NodeReferencesContextProvider } from '@noodl-contexts/NodeReferencesContext';
import { PluginContextProvider } from '@noodl-contexts/PluginContext';
import { ProjectDesignTokenContextProvider } from '@noodl-contexts/ProjectDesignTokenContext'; import { ProjectDesignTokenContextProvider } from '@noodl-contexts/ProjectDesignTokenContext';
import { useKeyboardCommands } from '@noodl-hooks/useKeyboardCommands'; import { useKeyboardCommands } from '@noodl-hooks/useKeyboardCommands';
import { useModel } from '@noodl-hooks/useModel'; import { useModel } from '@noodl-hooks/useModel';
@@ -8,7 +6,6 @@ import { ipcRenderer } from 'electron';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { platform } from '@noodl/platform'; import { platform } from '@noodl/platform';
import { Keybindings } from '@noodl-constants/Keybindings';
import { App } from '@noodl-models/app'; import { App } from '@noodl-models/app';
import { AppRegistry } from '@noodl-models/app_registry'; import { AppRegistry } from '@noodl-models/app_registry';
import { CloudService } from '@noodl-models/CloudServices'; import { CloudService } from '@noodl-models/CloudServices';
@@ -46,6 +43,7 @@ import { BaseWindow } from '../../views/windows/BaseWindow';
import { whatsnewRender } from '../../whats-new'; import { whatsnewRender } from '../../whats-new';
import { IRouteProps } from '../AppRoute'; import { IRouteProps } from '../AppRoute';
import { useSetupSettings } from './useSetupSettings'; import { useSetupSettings } from './useSetupSettings';
import { PluginContextProvider } from '@noodl-contexts/PluginContext';
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const ImportOverwritePopupTemplate = require('../../templates/importoverwritepopup.html'); const ImportOverwritePopupTemplate = require('../../templates/importoverwritepopup.html');
@@ -157,7 +155,7 @@ export function EditorPage({ route }: EditorPageProps) {
useKeyboardCommands(() => [ useKeyboardCommands(() => [
{ {
handler: () => SidebarModel.instance.switch('search'), handler: () => SidebarModel.instance.switch('search'),
keybinding: Keybindings.SEARCH.hash keybinding: KeyMod.CtrlCmd | KeyCode.KEY_F
}, },
{ {
handler: () => EventDispatcher.instance.emit('viewer-open-devtools'), handler: () => EventDispatcher.instance.emit('viewer-open-devtools'),
@@ -225,7 +223,6 @@ export function EditorPage({ route }: EditorPageProps) {
return ( return (
<NodeGraphContextProvider> <NodeGraphContextProvider>
<NodeReferencesContextProvider>
<ProjectDesignTokenContextProvider> <ProjectDesignTokenContextProvider>
<PluginContextProvider> <PluginContextProvider>
<BaseWindow> <BaseWindow>
@@ -248,7 +245,6 @@ export function EditorPage({ route }: EditorPageProps) {
</BaseWindow> </BaseWindow>
</PluginContextProvider> </PluginContextProvider>
</ProjectDesignTokenContextProvider> </ProjectDesignTokenContextProvider>
</NodeReferencesContextProvider>
</NodeGraphContextProvider> </NodeGraphContextProvider>
); );
} }

View File

@@ -16,7 +16,6 @@ import { projectFromDirectory, unzipIntoDirectory } from '../models/projectmodel
import FileSystem from './filesystem'; import FileSystem from './filesystem';
import { tracker } from './tracker'; import { tracker } from './tracker';
import { guid } from './utils'; import { guid } from './utils';
import { getTopLevelWorkingDirectory } from '@noodl/git/src/core/open';
export interface ProjectItem { export interface ProjectItem {
id: string; id: string;
@@ -268,15 +267,12 @@ export class LocalProjectsModel extends Model {
}); });
} }
/** isGitProject(project: ProjectModel): boolean {
* Check if this project is in a git repository. // TODO: check if there's is git in any parent folder too
*
* @param project // Check if the git folder exists.
* @returns const gitPath = filesystem.join(project._retainedProjectDirectory, '.git');
*/ return filesystem.exists(gitPath);
async isGitProject(project: ProjectModel): Promise<boolean> {
const gitPath = await getTopLevelWorkingDirectory(project._retainedProjectDirectory);
return gitPath !== null;
} }
setCurrentGlobalGitAuth(projectId: string) { setCurrentGlobalGitAuth(projectId: string) {

View File

@@ -1,4 +1,5 @@
import { CloudService } from '@noodl-models/CloudServices'; import { CloudService } from '@noodl-models/CloudServices';
import { ProjectModel } from '@noodl-models/projectmodel';
import SchemaModel from '@noodl-models/schemamodel'; import SchemaModel from '@noodl-models/schemamodel';
class FormCollection { class FormCollection {
@@ -193,7 +194,7 @@ export default class CloudFormation {
}) { }) {
// Create new cloud services if needed // Create new cloud services if needed
if (options.cloudServices.id === undefined) { if (options.cloudServices.id === undefined) {
CloudService.instance.backend.fetch().then((collection) => { CloudService.instance.backend.fetch(ProjectModel.instance).then((collection) => {
// TODO(OS): Cloud formation Cloud Service // TODO(OS): Cloud formation Cloud Service
// // Make sure we have a unique name for the cloud services // // Make sure we have a unique name for the cloud services
// const orgName = options.cloudServices.name; // const orgName = options.cloudServices.name;

View File

@@ -67,14 +67,6 @@ async function _writeFileToFolder({
runtimeType runtimeType
}: WriteFileToFolderArgs) { }: WriteFileToFolderArgs) {
const fullPath = filesystem.join(getExternalFolderPath(), runtimeType, url); const fullPath = filesystem.join(getExternalFolderPath(), runtimeType, url);
if (!filesystem.exists(fullPath)) {
// TODO: Save this warning somewhere, usually, this is not an issue though.
// This occurred because building in dev mode does not create the source map
// files which it expects to copy over.
return;
}
let content = await filesystem.readFile(fullPath); let content = await filesystem.readFile(fullPath);
let filename = url; let filename = url;

View File

@@ -11,14 +11,6 @@ export interface ArrayDiff<T> {
changed: T[]; changed: T[];
unchanged: T[]; unchanged: T[];
} }
export function createEmptyArrayDiff<T>(): ArrayDiff<T> {
return {
deleted: [],
created: [],
changed: [],
unchanged: [],
}
}
export interface ProjectDiffItem { export interface ProjectDiffItem {
graph: TSFixme; graph: TSFixme;

View File

@@ -13,7 +13,6 @@ export default class SchemaHandler {
public dbCollections: TSFixme[]; public dbCollections: TSFixme[];
public systemCollections: TSFixme[]; public systemCollections: TSFixme[];
public configSchema: TSFixme; public configSchema: TSFixme;
public parseServerVersion: string;
constructor() { constructor() {
EventDispatcher.instance.on( EventDispatcher.instance.on(
@@ -42,7 +41,7 @@ export default class SchemaHandler {
return; // No project broker return; // No project broker
} }
CloudService.instance.backend.fetch().then((collection) => { CloudService.instance.backend.fetch(ProjectModel.instance).then((collection) => {
// Find by the Url / Endpoint and app id // Find by the Url / Endpoint and app id
let environment = collection.find((b) => { let environment = collection.find((b) => {
return b.url === activeBroker.endpoint && b.appId === activeBroker.appId; return b.url === activeBroker.endpoint && b.appId === activeBroker.appId;
@@ -120,20 +119,6 @@ export default class SchemaHandler {
console.log(e); console.log(e);
} }
}); });
// Get Parse Server Version & Supported features
fetch(environment.url + '/serverInfo', {
method: 'POST',
body: JSON.stringify({
"_method": "GET",
"_ApplicationId": environment.appId,
"_MasterKey": environment.masterKey,
})
})
.then((response) => response.json())
.then((json) => {
this.parseServerVersion = json.parseServerVersion;
});
}); });
}); });
} }
@@ -144,20 +129,10 @@ export default class SchemaHandler {
ProjectModel.instance.setMetaData('dbCollections', this.dbCollections); ProjectModel.instance.setMetaData('dbCollections', this.dbCollections);
ProjectModel.instance.setMetaData('systemCollections', this.systemCollections); ProjectModel.instance.setMetaData('systemCollections', this.systemCollections);
ProjectModel.instance.setMetaData('dbConfigSchema', this.configSchema); ProjectModel.instance.setMetaData('dbConfigSchema', this.configSchema);
const versionNumbers = this.parseServerVersion?.split(".")
if (versionNumbers && versionNumbers.length > 0) {
// Let's only save the major version number,
// since this will be used to determine which verison of the API to use.
ProjectModel.instance.setMetaData('dbVersionMajor', versionNumbers[0]);
} else {
ProjectModel.instance.setMetaData('dbVersionMajor', undefined);
}
} else { } else {
ProjectModel.instance.setMetaData('dbCollections', undefined); ProjectModel.instance.setMetaData('dbCollections', undefined);
ProjectModel.instance.setMetaData('systemCollections', undefined); ProjectModel.instance.setMetaData('systemCollections', undefined);
ProjectModel.instance.setMetaData('dbConfigSchema', undefined); ProjectModel.instance.setMetaData('dbConfigSchema', undefined);
ProjectModel.instance.setMetaData('dbVersionMajor', undefined);
} }
} }
} }

View File

@@ -1,6 +1,5 @@
import React, { RefObject } from 'react'; import { usePluginContext } from '@noodl-contexts/PluginContext';
import React, { RefObject, useEffect, useRef } from 'react';
import { ProjectModel } from '@noodl-models/projectmodel';
import { ActivityIndicator } from '@noodl-core-ui/components/common/ActivityIndicator'; import { ActivityIndicator } from '@noodl-core-ui/components/common/ActivityIndicator';
import { BaseDialog } from '@noodl-core-ui/components/layout/BaseDialog'; import { BaseDialog } from '@noodl-core-ui/components/layout/BaseDialog';
@@ -55,7 +54,6 @@ export function DeployPopup(props: DeployPopupProps) {
onClose={props.onClose} onClose={props.onClose}
hasArrow hasArrow
isLockingScroll isLockingScroll
alwaysMounted
> >
<DeployPopupChild /> <DeployPopupChild />
</BaseDialog> </BaseDialog>
@@ -64,19 +62,6 @@ export function DeployPopup(props: DeployPopupProps) {
} }
function FluxscapeDeployTab() { function FluxscapeDeployTab() {
const params = {}; // Preview URL: 'http://192.168.0.33:8574/'
return <iframe src="https://portal.fluxscape.io" style={{ width: "100%", height: "50vh", borderStyle: "none" }} />;
const projectId = ProjectModel.instance.id;
if (projectId) {
params['projectId'] = projectId;
}
const urlParams = new URLSearchParams(params);
return (
<iframe
src={`https://portal.fluxscape.io/?${urlParams.toString()}`}
style={{ width: '100%', height: '50vh', borderStyle: 'none' }}
/>
);
} }

View File

@@ -87,7 +87,7 @@ export function CloudServiceCard({
<div className={css['MetaBar']}> <div className={css['MetaBar']}>
<div className={classNames([css['TypeDisplay'], isEditorEnvironment && css['is-editor-environment']])}> <div className={classNames([css['TypeDisplay'], isEditorEnvironment && css['is-editor-environment']])}>
<Icon icon={IconName.CloudCheck} size={IconSize.Small} UNSAFE_style={{ marginRight: 4 }} /> <Icon icon={IconName.CloudCheck} size={IconSize.Small} UNSAFE_style={{ marginRight: 4 }} />
{'Self hosted '} {environment.typeDisplayName + ' '}
{errorMessage && <span className={css['ArchivedDisplay']}>({errorMessage})</span>} {errorMessage && <span className={css['ArchivedDisplay']}>({errorMessage})</span>}
{isEditorEnvironment && <span className={css['UsedInEditorDisplay']}>(Used in editor)</span>} {isEditorEnvironment && <span className={css['UsedInEditorDisplay']}>(Used in editor)</span>}
</div> </div>

View File

@@ -5,6 +5,7 @@ import { CloudService, Environment } from '@noodl-models/CloudServices';
import { ToastType } from '../../../ToastLayer/components/ToastCard'; import { ToastType } from '../../../ToastLayer/components/ToastCard';
import { CloudServiceCard } from '../CloudServiceCard'; import { CloudServiceCard } from '../CloudServiceCard';
import { useCloudServiceContext } from '../CloudServicePanel.context'; import { useCloudServiceContext } from '../CloudServicePanel.context';
import { ProjectModel } from '@noodl-models/projectmodel';
export interface CloudServiceCardItemProps { export interface CloudServiceCardItemProps {
environment: Environment; environment: Environment;
@@ -20,7 +21,7 @@ export function CloudServiceCardItem({ environment, deleteEnvironment }: CloudSe
async function onDelete() { async function onDelete() {
await deleteEnvironment(); await deleteEnvironment();
await runActivity('Deleting cloud service...', async () => { await runActivity('Deleting cloud service...', async () => {
const response: boolean = await CloudService.instance.backend.delete(environment.id); const response: boolean = await CloudService.instance.backend.delete(ProjectModel.instance, environment.id);
return { return {
type: ToastType.Success, type: ToastType.Success,
message: 'Cloud service deleted' message: 'Cloud service deleted'

View File

@@ -12,6 +12,7 @@ import { Text, TextType } from '@noodl-core-ui/components/typography/Text';
import { ToastType } from '../../../ToastLayer/components/ToastCard'; import { ToastType } from '../../../ToastLayer/components/ToastCard';
import { useCloudServiceContext } from '../CloudServicePanel.context'; import { useCloudServiceContext } from '../CloudServicePanel.context';
import { ProjectModel } from '@noodl-models/projectmodel';
function isValidParseUrl(url: string) { function isValidParseUrl(url: string) {
if (!url) return false; if (!url) return false;
@@ -63,7 +64,7 @@ export function CloudServiceCreateModal({ isVisible, onClose }: CloudServiceCrea
async function onCreate() { async function onCreate() {
await runActivity('Creating Cloud Service...', async () => { await runActivity('Creating Cloud Service...', async () => {
await cloudService.backend.create({ await cloudService.backend.create(ProjectModel.instance, {
name, name,
description, description,
masterKey: masterKey ? masterKey : undefined, masterKey: masterKey ? masterKey : undefined,

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import React, { useState } from 'react';
import { FeedbackType } from '@noodl-constants/FeedbackType'; import { FeedbackType } from '@noodl-constants/FeedbackType';
import { CloudService, Environment } from '@noodl-models/CloudServices'; import { CloudService, Environment } from '@noodl-models/CloudServices';
@@ -12,6 +12,7 @@ import { HStack, VStack } from '@noodl-core-ui/components/layout/Stack';
import { Text } from '@noodl-core-ui/components/typography/Text'; import { Text } from '@noodl-core-ui/components/typography/Text';
import { ToastLayer } from '../../../ToastLayer'; import { ToastLayer } from '../../../ToastLayer';
import { ProjectModel } from '@noodl-models/projectmodel';
export interface CloudServiceModalProps { export interface CloudServiceModalProps {
isVisible: boolean; isVisible: boolean;
@@ -60,7 +61,7 @@ function AsSelfHosted({
} }
CloudService.instance.backend CloudService.instance.backend
.update({ .update(ProjectModel.instance, {
id: environment.id, id: environment.id,
name, name,
description, description,
@@ -70,7 +71,7 @@ function AsSelfHosted({
}) })
.then(() => { .then(() => {
ToastLayer.showSuccess(`Updated Cloud Service`); ToastLayer.showSuccess(`Updated Cloud Service`);
CloudService.instance.backend.fetch(); CloudService.instance.backend.fetch(ProjectModel.instance);
}) })
.catch(() => { .catch(() => {
ToastLayer.showError(`Failed to update Cloud Service`); ToastLayer.showError(`Failed to update Cloud Service`);

View File

@@ -35,7 +35,7 @@ export function CloudServiceContextProvider({ children }) {
const { hasActivity, runActivity } = useActivityQueue({ const { hasActivity, runActivity } = useActivityQueue({
onSuccess: async () => { onSuccess: async () => {
// Always fetch all the backends after something have changed // Always fetch all the backends after something have changed
await cloudService.backend.fetch(); await cloudService.backend.fetch(ProjectModel.instance);
} }
}); });

View File

@@ -1,12 +1,14 @@
import { NodeGraphContextTmp } from '@noodl-contexts/NodeGraphContext/NodeGraphContext'; import { NodeGraphContextTmp } from '@noodl-contexts/NodeGraphContext/NodeGraphContext';
import { type NodeReference, useNodeReferencesContext } from '@noodl-contexts/NodeReferencesContext';
import { useFocusRefOnPanelActive } from '@noodl-hooks/useFocusRefOnPanelActive'; import { useFocusRefOnPanelActive } from '@noodl-hooks/useFocusRefOnPanelActive';
import { useNodeLibraryLoaded } from '@noodl-hooks/useNodeLibraryLoaded'; import { useNodeLibraryLoaded } from '@noodl-hooks/useNodeLibraryLoaded';
import React, { useMemo, useRef, useState } from 'react'; import React, { useEffect, useMemo, useRef, useState } from 'react';
import { INodeColorScheme } from '@noodl-types/nodeTypes'; import { INodeColorScheme } from '@noodl-types/nodeTypes';
import { NodeLibrary } from '@noodl-models/nodelibrary'; import { ComponentModel } from '@noodl-models/componentmodel';
import { NodeGraphNode } from '@noodl-models/nodegraphmodel';
import { NodeLibrary, NodeLibraryNodeType } from '@noodl-models/nodelibrary';
import { BasicNodeType } from '@noodl-models/nodelibrary/BasicNodeType'; import { BasicNodeType } from '@noodl-models/nodelibrary/BasicNodeType';
import { ProjectModel } from '@noodl-models/projectmodel';
import { EditorNode } from '@noodl-core-ui/components/common/EditorNode'; import { EditorNode } from '@noodl-core-ui/components/common/EditorNode';
import { IconName, IconSize } from '@noodl-core-ui/components/common/Icon'; import { IconName, IconSize } from '@noodl-core-ui/components/common/Icon';
@@ -24,17 +26,113 @@ import { Section, SectionVariant } from '@noodl-core-ui/components/sidebar/Secti
import { Label } from '@noodl-core-ui/components/typography/Label'; import { Label } from '@noodl-core-ui/components/typography/Label';
import { NodeReferencesPanel_ID } from '.'; import { NodeReferencesPanel_ID } from '.';
import { EventDispatcher } from '../../../../../shared/utils/EventDispatcher';
type ResultItem = {
type: NodeLibraryNodeType;
displayName: string;
referenaces: {
displayName: string;
node?: NodeGraphNode;
component: ComponentModel;
}[];
};
function useNodeReferences() {
const [group] = useState({});
const [result, setResult] = useState<ResultItem[]>([]);
useEffect(() => {
function updateIndex() {
const types: { [key: string]: ResultItem['type'] } = {};
const references = new Map<string, ResultItem['referenaces']>();
function handleComponent(component: ComponentModel) {
component.forEachNode((node: NodeGraphNode) => {
const name = node.type.name;
// Add the reference
references.set(name, [
...(references.get(name) || []),
{
displayName: component.displayName || component.name,
node,
component
}
]);
// Repeater
if (name === 'For Each' && node.parameters.template) {
const templateComponent = ProjectModel.instance.getComponentWithName(node.parameters.template);
if (templateComponent) {
references.set(templateComponent.fullName, [
...(references.get(templateComponent.fullName) || []),
{
displayName: component.displayName || component.name,
node,
component
}
]);
handleComponent(templateComponent);
}
}
// Add some metadata for this node if we dont have it yet.
if (!types[name]) {
types[name] = node.type;
}
});
}
// Loop all the nodes in the project
ProjectModel.instance.forEachComponent(handleComponent);
// Combine the result to look a little better.
const results: ResultItem[] = Array.from(references.keys())
.map((key) => ({
type: types[key],
displayName: types[key]?.displayName || key,
referenaces: references.get(key)
}))
.sort((a, b) => b.referenaces.length - a.referenaces.length);
setResult(results);
}
updateIndex();
EventDispatcher.instance.on(
[
'Model.nodeAdded',
'Model.nodeRemoved',
'Model.componentAdded',
'Model.componentRemoved',
'Model.componentRenamed'
],
updateIndex,
group
);
return function () {
EventDispatcher.instance.off(group);
};
}, []);
return [result];
}
export function NodeReferencesPanel() { export function NodeReferencesPanel() {
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
const [includeCoreNodes, setIncludeCoreNodes] = useState(false); const [includeCoreNodes, setIncludeCoreNodes] = useState(false);
const inputRef = useRef(null); const inputRef = useRef(null);
const { nodeReferences } = useNodeReferencesContext(); const [result] = useNodeReferences();
const nodeLibraryLoaded = useNodeLibraryLoaded(); const nodeLibraryLoaded = useNodeLibraryLoaded();
useFocusRefOnPanelActive(inputRef, NodeReferencesPanel_ID); useFocusRefOnPanelActive(inputRef, NodeReferencesPanel_ID);
function searchFilter(x: NodeReference) { function searchFilter(x: ResultItem) {
if (x.displayName.toLowerCase().includes(searchTerm)) { if (x.displayName.toLowerCase().includes(searchTerm)) {
return true; return true;
} }
@@ -46,7 +144,7 @@ export function NodeReferencesPanel() {
return false; return false;
} }
let filteredResult = nodeReferences.filter(searchFilter); let filteredResult = result.filter(searchFilter);
if (!includeCoreNodes) { if (!includeCoreNodes) {
filteredResult = filteredResult.filter((x) => x.displayName.startsWith('/')); filteredResult = filteredResult.filter((x) => x.displayName.startsWith('/'));
} }
@@ -87,7 +185,7 @@ export function NodeReferencesPanel() {
} }
interface ItemProps { interface ItemProps {
entry: NodeReference; entry: ResultItem;
} }
function Item({ entry }: ItemProps) { function Item({ entry }: ItemProps) {
@@ -147,8 +245,8 @@ function Item({ entry }: ItemProps) {
} }
interface ItemReferenceProps { interface ItemReferenceProps {
entry: NodeReference; entry: ResultItem;
referenace: NodeReference['referenaces'][0]; referenace: ResultItem['referenaces'][0];
colors: INodeColorScheme; colors: INodeColorScheme;
} }

View File

@@ -199,7 +199,6 @@ function BaseVersionControlPanel() {
export function VersionControlPanel() { export function VersionControlPanel() {
const [git, setGit] = useState<Git>(null); const [git, setGit] = useState<Git>(null);
const [state, setState] = useState<'loading' | 'loaded' | 'not-git'>('loading');
async function createGit() { async function createGit() {
const gitClient = new Git(mergeProject); const gitClient = new Git(mergeProject);
@@ -207,18 +206,12 @@ export function VersionControlPanel() {
setGit(gitClient); setGit(gitClient);
} }
const isGitProject = git === null ? LocalProjectsModel.instance.isGitProject(ProjectModel.instance) : true;
useEffect(() => { useEffect(() => {
LocalProjectsModel.instance
.isGitProject(ProjectModel.instance)
.then(async (isGitProject) => {
if (isGitProject) { if (isGitProject) {
await createGit(); createGit();
setState('loaded');
} else {
setState('not-git');
} }
}); }, [isGitProject]);
}, []);
async function setupGit() { async function setupGit() {
const gitClient = new Git(mergeProject); const gitClient = new Git(mergeProject);
@@ -227,7 +220,7 @@ export function VersionControlPanel() {
setGit(gitClient); setGit(gitClient);
} }
if (git === null && state === 'not-git') { if (git === null && !isGitProject) {
return ( return (
<BasePanel isFill title="Version Control"> <BasePanel isFill title="Version Control">
<Box hasXSpacing hasYSpacing> <Box hasXSpacing hasYSpacing>

View File

@@ -6,13 +6,11 @@ import { Commit } from '@noodl/git/src/core/models/snapshot';
import { FileChange } from '@noodl/git/src/core/models/status'; import { FileChange } from '@noodl/git/src/core/models/status';
import { revRange } from '@noodl/git/src/core/rev-list'; import { revRange } from '@noodl/git/src/core/rev-list';
import { ProjectModel } from '@noodl-models/projectmodel';
import { applyPatches } from '@noodl-models/ProjectPatches/applypatches'; import { applyPatches } from '@noodl-models/ProjectPatches/applypatches';
import { mergeProject } from '@noodl-utils/projectmerger'; import { mergeProject } from '@noodl-utils/projectmerger';
import { ProjectDiff, diffProject } from '@noodl-utils/projectmerger.diff'; import { ProjectDiff, diffProject } from '@noodl-utils/projectmerger.diff';
import { useVersionControlContext } from '../context'; import { useVersionControlContext } from '../context';
import { getProjectFilePath } from '../context/DiffUtils';
import { DiffList } from './DiffList'; import { DiffList } from './DiffList';
//Kind: //Kind:
@@ -126,10 +124,7 @@ async function getMergeDiff(repositoryPath: string, commit: Commit, refToDiffTo:
} }
async function getProjectFile(commit: Commit) { async function getProjectFile(commit: Commit) {
const projectFilePath = getProjectFilePath(commit.repositoryDir, ProjectModel.instance._retainedProjectDirectory); const projectContent = JSON.parse(await commit.getFileAsString('project.json'));
const projectContentRaw = await commit.getFileAsString(projectFilePath);
const projectContent = JSON.parse(projectContentRaw);
applyPatches(projectContent); applyPatches(projectContent);
return projectContent; return projectContent;
} }

View File

@@ -61,7 +61,7 @@ export function DiffList({ diff, fileChanges, componentDiffTitle, actions, commi
const [imageDiff, setImageDiff] = useState<IImageDiff>(null); const [imageDiff, setImageDiff] = useState<IImageDiff>(null);
const components = diff?.components ? getChangedComponents(diff.components) : []; const components = diff?.components ? getChangedComponents(diff.components) : [];
const files = (fileChanges || [])?.filter((f) => !f.path.endsWith('project.json')) || []; const files = (fileChanges || [])?.filter((f) => f.path !== 'project.json') || [];
const settings = diff?.settings ? getChangedObjectProperties(diff.settings) : []; const settings = diff?.settings ? getChangedObjectProperties(diff.settings) : [];
const colorStyles = diff?.styles.colors ? getChangedObjectProperties(diff.styles.colors) : []; const colorStyles = diff?.styles.colors ? getChangedObjectProperties(diff.styles.colors) : [];
const textStyles = diff?.styles.text ? getChangedObjectProperties(diff.styles.text) : []; const textStyles = diff?.styles.text ? getChangedObjectProperties(diff.styles.text) : [];

View File

@@ -1,24 +1,21 @@
import path from 'path';
import { getCommit } from '@noodl/git/src/core/logs';
import { FileStatusKind } from '@noodl/git/src/core/models/status';
import { FeedbackType } from '@noodl-constants/FeedbackType';
import { applyPatches } from '@noodl-models/ProjectPatches/applypatches';
import { import {
ProjectDiff, ProjectDiff,
ProjectDiffItem, ProjectDiffItem,
ProjectBasicDiffItem, ProjectBasicDiffItem,
ArrayDiff, ArrayDiff,
diffProject, diffProject
createEmptyArrayDiff
} from '@noodl-utils/projectmerger.diff'; } from '@noodl-utils/projectmerger.diff';
import { IconName } from '@noodl-core-ui/components/common/Icon';
import { ListItemProps } from '@noodl-core-ui/components/layout/ListItem';
import { ProjectModel } from '../../../../models/projectmodel'; import { ProjectModel } from '../../../../models/projectmodel';
export interface ProjectLocalDiff extends ProjectDiff { import { applyPatches } from '@noodl-models/ProjectPatches/applypatches';
import { FileStatusKind } from '@noodl/git/src/core/models/status';
import { IconName } from '@noodl-core-ui/components/common/Icon';
import { ListItemProps } from '@noodl-core-ui/components/layout/ListItem';
import { FeedbackType } from '@noodl-constants/FeedbackType';
import { getCommit } from '@noodl/git/src/core/logs';
export interface ProjectLocalDiff extends ProjectDiff{
baseProject: TSFixme; //Project model as an object from raw json baseProject: TSFixme; //Project model as an object from raw json
commitShaDiffedTo: string; commitShaDiffedTo: string;
} }
@@ -87,22 +84,9 @@ export function getFileStatusIconProps(status: FileStatusKind): Partial<ListItem
} }
} }
export function getProjectFilePath(repositoryPath: string, projectPath: string) { export async function doLocalDiff(repositoryPath: string, headCommitId: string): Promise<ProjectLocalDiff> {
const relativePath = path.relative(repositoryPath, projectPath); const baseCommit = await getCommit(repositoryPath, headCommitId);
const projectFilePath = path.join(relativePath, 'project.json').replaceAll('\\', '/'); const baseProjectJson = await baseCommit.getFileAsString('project.json');
return projectFilePath;
}
export async function doLocalDiff(
repositoryPath: string,
projectPath: string,
headCommitId: string
): Promise<ProjectLocalDiff> {
const projectFilePath = getProjectFilePath(repositoryPath, projectPath);
try {
const baseCommit = await getCommit(projectPath, headCommitId);
const baseProjectJson = await baseCommit.getFileAsString(projectFilePath);
const baseProject = JSON.parse(baseProjectJson); const baseProject = JSON.parse(baseProjectJson);
applyPatches(baseProject); applyPatches(baseProject);
@@ -113,23 +97,4 @@ export async function doLocalDiff(
baseProject, baseProject,
commitShaDiffedTo: headCommitId commitShaDiffedTo: headCommitId
}; };
} catch (error) {
if (error.toString().includes('exists on disk, but not in')) {
console.warn('project.json does not exist in this commit.');
}
// Return empty state
return {
baseProject: {},
commitShaDiffedTo: headCommitId,
components: createEmptyArrayDiff(),
variants: createEmptyArrayDiff(),
settings: createEmptyArrayDiff(),
styles: {
colors: createEmptyArrayDiff(),
text: createEmptyArrayDiff()
},
cloudservices: createEmptyArrayDiff()
};
}
} }

View File

@@ -8,7 +8,6 @@ import { Slot } from '@noodl-core-ui/types/global';
import { doLocalDiff, ProjectLocalDiff } from './DiffUtils'; import { doLocalDiff, ProjectLocalDiff } from './DiffUtils';
import { useVersionControlFetch } from './fetch.context'; import { useVersionControlFetch } from './fetch.context';
import { BranchStatus, IVersionControlContext } from './types'; import { BranchStatus, IVersionControlContext } from './types';
import { ProjectModel } from '@noodl-models/projectmodel';
const VersionControlContext = createContext<IVersionControlContext>({ const VersionControlContext = createContext<IVersionControlContext>({
git: null, git: null,
@@ -58,8 +57,7 @@ export function VersionControlProvider({ git, children }: { git: Git; children:
(async () => { (async () => {
const currentCommitSha = await git.getHeadCommitId(); const currentCommitSha = await git.getHeadCommitId();
if (currentCommitSha) { if (currentCommitSha) {
const projectPath = ProjectModel.instance._retainedProjectDirectory; const diff = await doLocalDiff(git.repositoryPath, currentCommitSha);
const diff = await doLocalDiff(git.repositoryPath, projectPath, currentCommitSha);
setLocalDiff(diff); setLocalDiff(diff);
} }
})(); })();

View File

@@ -17,11 +17,9 @@ import { EventDispatcher } from '../../../../../shared/utils/EventDispatcher';
import View from '../../../../../shared/view'; import View from '../../../../../shared/view';
import { NodeGraphEditor } from '../../nodegrapheditor'; import { NodeGraphEditor } from '../../nodegrapheditor';
import * as NewPopupLayer from '../../PopupLayer/index'; import * as NewPopupLayer from '../../PopupLayer/index';
import { type PopupMenuItem } from '../../PopupLayer/index';
import { ToastLayer } from '../../ToastLayer/ToastLayer'; import { ToastLayer } from '../../ToastLayer/ToastLayer';
import { ComponentsPanelFolder } from './ComponentsPanelFolder'; import { ComponentsPanelFolder } from './ComponentsPanelFolder';
import { ComponentTemplates } from './ComponentTemplates'; import { ComponentTemplates } from './ComponentTemplates';
import { HACK_findNodeReference } from '@noodl-contexts/NodeReferencesContext';
const PopupLayer = require('@noodl-views/popuplayer'); const PopupLayer = require('@noodl-views/popuplayer');
const ComponentsPanelTemplate = require('../../../templates/componentspanel.html'); const ComponentsPanelTemplate = require('../../../templates/componentspanel.html');
@@ -963,7 +961,7 @@ export class ComponentsPanelView extends View {
forRuntimeType: this.getRuntimeType() forRuntimeType: this.getRuntimeType()
}); });
let items: PopupMenuItem[] = templates.map((t) => ({ let items: TSFixme[] = templates.map((t) => ({
icon: IconName.Plus, icon: IconName.Plus,
label: t.label, label: t.label,
onClick: () => { onClick: () => {
@@ -989,10 +987,6 @@ export class ComponentsPanelView extends View {
}); });
} }
// Find references
const nodeReference = HACK_findNodeReference(scope.comp.name);
const nodeReferencesText = `Used in ${nodeReference?.referenaces?.length || 0} places`;
items = items.concat([ items = items.concat([
{ {
icon: IconName.Pencil, icon: IconName.Pencil,
@@ -1017,9 +1011,6 @@ export class ComponentsPanelView extends View {
_this.onDeleteClicked(scope, el); _this.onDeleteClicked(scope, el);
evt.stopPropagation(); evt.stopPropagation();
} }
},
{
label: nodeReferencesText
} }
]); ]);
@@ -1119,16 +1110,6 @@ export class ComponentsPanelView extends View {
} }
]); ]);
if (scope.canBecomeRoot) {
// Find references
const nodeReference = HACK_findNodeReference(scope.folder.component.name);
const nodeReferencesText = `Used in ${nodeReference?.referenaces?.length || 0} places`;
items = items.concat([{
label: nodeReferencesText
}]);
}
const menu = new NewPopupLayer.PopupMenu({ const menu = new NewPopupLayer.PopupMenu({
items: items items: items
}); });

View File

@@ -5,9 +5,9 @@ import { useSidePanelKeyboardCommands } from '@noodl-hooks/useKeyboardCommands';
import classNames from 'classnames'; import classNames from 'classnames';
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { Keybindings } from '@noodl-constants/Keybindings';
import { ComponentModel } from '@noodl-models/componentmodel'; import { ComponentModel } from '@noodl-models/componentmodel';
import { NodeGraphNode } from '@noodl-models/nodegraphmodel'; import { NodeGraphNode } from '@noodl-models/nodegraphmodel';
import { KeyCode, KeyMod } from '@noodl-utils/keyboard/KeyCode';
import { performSearch } from '@noodl-utils/universal-search'; import { performSearch } from '@noodl-utils/universal-search';
import { SearchInput } from '@noodl-core-ui/components/inputs/SearchInput'; import { SearchInput } from '@noodl-core-ui/components/inputs/SearchInput';
@@ -40,7 +40,7 @@ export function SearchPanel() {
inputRef.current.focus(); inputRef.current.focus();
inputRef.current.select(); inputRef.current.select();
}, },
keybinding: Keybindings.SEARCH.hash keybinding: KeyMod.CtrlCmd | KeyCode.KEY_F
} }
], ],
'search' 'search'

View File

@@ -22,11 +22,7 @@ export async function open(basePath: string): Promise<string> {
// console.log("VCS error when opening project: " + e); // console.log("VCS error when opening project: " + e);
// } // }
return basePath;
// Find the relative git repository path
const repositoryPath = await getTopLevelWorkingDirectory(basePath);
return repositoryPath;
} }
/** /**

View File

@@ -32,15 +32,12 @@ class CloudStore {
_initCloudServices() { _initCloudServices() {
_collections = undefined; // clear collection cache, so it's refetched _collections = undefined; // clear collection cache, so it's refetched
const cloudServices = NoodlRuntime.instance.getMetaData('cloudservices'); const cloudServices = NoodlRuntime.instance.getMetaData('cloudservices');
if (cloudServices) { if (cloudServices) {
this.appId = cloudServices.appId; this.appId = cloudServices.appId;
this.endpoint = cloudServices.endpoint; this.endpoint = cloudServices.endpoint;
} }
const dbVersionMajor = NoodlRuntime.instance.getMetaData('dbVersionMajor');
this.dbVersionMajor = dbVersionMajor;
} }
on() { on() {
@@ -73,9 +70,8 @@ class CloudStore {
xhr.open(options.method || 'GET', this.endpoint + path, true); xhr.open(options.method || 'GET', this.endpoint + path, true);
xhr.setRequestHeader('X-Parse-Application-Id', this.appId); xhr.setRequestHeader('X-Parse-Application-Id', this.appId);
if (typeof _noodl_cloudservices !== 'undefined') { if (typeof _noodl_cloudservices !== 'undefined')
xhr.setRequestHeader('X-Parse-Master-Key', _noodl_cloudservices.masterKey); xhr.setRequestHeader('X-Parse-Master-Key', _noodl_cloudservices.masterKey);
}
// Check for current users // Check for current users
var _cu = localStorage['Parse/' + this.appId + '/currentUser']; var _cu = localStorage['Parse/' + this.appId + '/currentUser'];
@@ -172,10 +168,13 @@ class CloudStore {
return; return;
} }
if (options.where) args.push('match=' + encodeURIComponent(JSON.stringify(options.where)));
if (options.limit) args.push('limit=' + options.limit); if (options.limit) args.push('limit=' + options.limit);
if (options.skip) args.push('skip=' + options.skip); if (options.skip) args.push('skip=' + options.skip);
const grouping = {}; const grouping = {
objectId: null
};
Object.keys(options.group).forEach((k) => { Object.keys(options.group).forEach((k) => {
const _g = {}; const _g = {};
@@ -189,20 +188,7 @@ class CloudStore {
grouping[k] = _g; grouping[k] = _g;
}); });
// I don't know which version the API was changed, lets just say above 4 for now.
if (this.dbVersionMajor && this.dbVersionMajor > 4) {
grouping._id = null;
if (options.where) args.push('$match=' + encodeURIComponent(JSON.stringify(options.where)));
args.push('$group=' + JSON.stringify(grouping));
} else {
grouping.objectId = null;
if (options.where) args.push('match=' + encodeURIComponent(JSON.stringify(options.where)));
args.push('group=' + JSON.stringify(grouping)); args.push('group=' + JSON.stringify(grouping));
}
this._makeRequest('/aggregate/' + options.collection + (args.length > 0 ? '?' + args.join('&') : ''), { this._makeRequest('/aggregate/' + options.collection + (args.length > 0 ? '?' + args.join('&') : ''), {
success: function (response) { success: function (response) {
@@ -258,34 +244,11 @@ class CloudStore {
}); });
} }
/**
*
* @param {{
* objectId: string;
* collection: string;
* keys?: string[] | string;
* include?: string[] | string;
* excludeKeys?: string[] | string;
* success: (data: unknown) => void;
* error: (error: unknown) => void;
* }} options
*/
fetch(options) { fetch(options) {
const args = []; const args = [];
if (options.include) { if (options.include)
args.push('include=' + (Array.isArray(options.include) ? options.include.join(',') : options.include)); args.push('include=' + (Array.isArray(options.include) ? options.include.join(',') : options.include));
}
if (options.keys) {
args.push('keys=' + (Array.isArray(options.keys) ? options.keys.join(',') : options.keys));
}
if (options.excludeKeys) {
args.push(
'excludeKeys=' + (Array.isArray(options.excludeKeys) ? options.excludeKeys.join(',') : options.excludeKeys)
);
}
this._makeRequest( this._makeRequest(
'/classes/' + options.collection + '/' + options.objectId + (args.length > 0 ? '?' + args.join('&') : ''), '/classes/' + options.collection + '/' + options.objectId + (args.length > 0 ? '?' + args.join('&') : ''),
@@ -457,8 +420,6 @@ class CloudStore {
* file: { * file: {
* name: string; * name: string;
* } * }
* success: (data: unknown) => void;
* error: (error: unknown) => void;
* }} options * }} options
*/ */
deleteFile(options) { deleteFile(options) {
@@ -589,26 +550,21 @@ function _deserializeJSON(data, type, modelScope) {
} }
function _fromJSON(item, collectionName, modelScope) { function _fromJSON(item, collectionName, modelScope) {
const modelStore = modelScope || Model; const m = (modelScope || Model).get(item.objectId);
m._class = collectionName;
const model = modelStore.get(item.objectId); if (collectionName !== undefined && CloudStore._collections[collectionName] !== undefined)
model._class = collectionName; var schema = CloudStore._collections[collectionName].schema;
let schema = undefined; for (var key in item) {
if (collectionName !== undefined && CloudStore._collections[collectionName] !== undefined) { if (key === 'objectId' || key === 'ACL') continue;
schema = CloudStore._collections[collectionName].schema;
var _type = schema && schema.properties && schema.properties[key] ? schema.properties[key].type : undefined;
m.set(key, _deserializeJSON(item[key], _type, modelScope));
} }
for (const key in item) { return m;
if (key === 'objectId' || key === 'ACL') {
continue;
}
const _type = schema && schema.properties && schema.properties[key] ? schema.properties[key].type : undefined;
model.set(key, _deserializeJSON(item[key], _type, modelScope));
}
return model;
} }
CloudStore._fromJSON = _fromJSON; CloudStore._fromJSON = _fromJSON;

View File

@@ -40,7 +40,8 @@ function convertVisualFilter(query, options) {
if (query.operator === 'exist') { if (query.operator === 'exist') {
_res[query.property] = { $exists: true }; _res[query.property] = { $exists: true };
return _res; return _res;
} else if (query.operator === 'not exist') { }
else if (query.operator === 'not exist') {
_res[query.property] = { $exists: false }; _res[query.property] = { $exists: false };
return _res; return _res;
} }
@@ -79,6 +80,7 @@ function convertVisualFilter(query, options) {
cond = { $regex: value, $options: 'i' }; cond = { $regex: value, $options: 'i' };
} }
_res[query.property] = cond; _res[query.property] = cond;
return _res; return _res;
@@ -161,22 +163,10 @@ function _value(v) {
return v; return v;
} }
/**
*
* @param {Record<string, unknown>} filter
* @param {{
* collectionName?: string;
* modelScope?: unknown;
* error: (error: string) => void;
* }} options
* @returns
*/
function convertFilterOp(filter, options) { function convertFilterOp(filter, options) {
const keys = Object.keys(filter); const keys = Object.keys(filter);
if (keys.length === 0) return {}; if (keys.length === 0) return {};
if (keys.length !== 1) { if (keys.length !== 1) return options.error('Filter must only have one key found ' + keys.join(','));
return options.error('Filter must only have one key found ' + keys.join(','));
}
const res = {}; const res = {};
const key = keys[0]; const key = keys[0];
@@ -189,27 +179,18 @@ function convertFilterOp(filter, options) {
} else if (filter['idContainedIn'] !== undefined) { } else if (filter['idContainedIn'] !== undefined) {
res['objectId'] = { $in: filter['idContainedIn'] }; res['objectId'] = { $in: filter['idContainedIn'] };
} else if (filter['relatedTo'] !== undefined) { } else if (filter['relatedTo'] !== undefined) {
const modelId = filter['relatedTo']['id']; var modelId = filter['relatedTo']['id'];
if (modelId === undefined) { if (modelId === undefined) return options.error('Must provide id in relatedTo filter');
return options.error('Must provide id in relatedTo filter');
}
const relationKey = filter['relatedTo']['key']; var relationKey = filter['relatedTo']['key'];
if (relationKey === undefined) { if (relationKey === undefined) return options.error('Must provide key in relatedTo filter');
return options.error('Must provide key in relatedTo filter');
}
const className = filter['relatedTo']['className'] || (options.modelScope || Model).get(modelId)?._class;
if (typeof className === 'undefined') {
// Either the pointer is loaded as an object or we allow passing in the className.
return options.error('Must preload the Pointer or include className');
}
var m = (options.modelScope || Model).get(modelId);
res['$relatedTo'] = { res['$relatedTo'] = {
object: { object: {
__type: 'Pointer', __type: 'Pointer',
objectId: modelId, objectId: modelId,
className className: m._class
}, },
key: relationKey key: relationKey
}; };
@@ -227,14 +208,13 @@ function convertFilterOp(filter, options) {
else if (opAndValue['containedIn'] !== undefined) res[key] = { $in: opAndValue['containedIn'] }; else if (opAndValue['containedIn'] !== undefined) res[key] = { $in: opAndValue['containedIn'] };
else if (opAndValue['notContainedIn'] !== undefined) res[key] = { $nin: opAndValue['notContainedIn'] }; else if (opAndValue['notContainedIn'] !== undefined) res[key] = { $nin: opAndValue['notContainedIn'] };
else if (opAndValue['pointsTo'] !== undefined) { else if (opAndValue['pointsTo'] !== undefined) {
let schema = null; var m = (options.modelScope || Model).get(opAndValue['pointsTo']);
if (CloudStore._collections[options.collectionName]) { if (CloudStore._collections[options.collectionName])
schema = CloudStore._collections[options.collectionName].schema; var schema = CloudStore._collections[options.collectionName].schema;
}
const targetClass = var targetClass =
schema && schema.properties && schema.properties[key] ? schema.properties[key].targetClass : undefined; schema && schema.properties && schema.properties[key] ? schema.properties[key].targetClass : undefined;
const type = schema && schema.properties && schema.properties[key] ? schema.properties[key].type : undefined; var type = schema && schema.properties && schema.properties[key] ? schema.properties[key].type : undefined;
if (type === 'Relation') { if (type === 'Relation') {
res[key] = { res[key] = {
@@ -243,13 +223,13 @@ function convertFilterOp(filter, options) {
className: targetClass className: targetClass
}; };
} else { } else {
if (Array.isArray(opAndValue['pointsTo'])) { if (Array.isArray(opAndValue['pointsTo']))
res[key] = { res[key] = {
$in: opAndValue['pointsTo'].map((v) => { $in: opAndValue['pointsTo'].map((v) => {
return { __type: 'Pointer', objectId: v, className: targetClass }; return { __type: 'Pointer', objectId: v, className: targetClass };
}) })
}; };
} else { else
res[key] = { res[key] = {
$eq: { $eq: {
__type: 'Pointer', __type: 'Pointer',
@@ -258,7 +238,6 @@ function convertFilterOp(filter, options) {
} }
}; };
} }
}
} else if (opAndValue['matchesRegex'] !== undefined) { } else if (opAndValue['matchesRegex'] !== undefined) {
res[key] = { res[key] = {
$regex: opAndValue['matchesRegex'], $regex: opAndValue['matchesRegex'],
@@ -283,37 +262,38 @@ function convertFilterOp(filter, options) {
var _v = opAndValue['nearSphere']; var _v = opAndValue['nearSphere'];
res[key] = { res[key] = {
$nearSphere: { $nearSphere: {
__type: 'GeoPoint', __type: "GeoPoint",
latitude: _v.latitude, latitude: _v.latitude,
longitude: _v.longitude longitude: _v.longitude,
}, },
$maxDistanceInMiles: _v.$maxDistanceInMiles, $maxDistanceInMiles:_v.$maxDistanceInMiles,
$maxDistanceInKilometers: _v.maxDistanceInKilometers, $maxDistanceInKilometers:_v.maxDistanceInKilometers,
$maxDistanceInRadians: _v.maxDistanceInRadians $maxDistanceInRadians:_v.maxDistanceInRadians
}; };
} else if (opAndValue['withinBox'] !== undefined) { } else if (opAndValue['withinBox'] !== undefined) {
var _v = opAndValue['withinBox']; var _v = opAndValue['withinBox'];
res[key] = { res[key] = {
$within: { $within:{
$box: _v.map((gp) => ({ $box: _v.map(gp => ({
__type: 'GeoPoint', __type:"GeoPoint",
latitude: gp.latitude, latitude:gp.latitude,
longitude: gp.longitude longitude:gp.longitude
})) }))
} }
}; };
} else if (opAndValue['withinPolygon'] !== undefined) { } else if (opAndValue['withinPolygon'] !== undefined) {
var _v = opAndValue['withinPolygon']; var _v = opAndValue['withinPolygon'];
res[key] = { res[key] = {
$geoWithin: { $geoWithin:{
$polygon: _v.map((gp) => ({ $polygon: _v.map(gp => ({
__type: 'GeoPoint', __type:"GeoPoint",
latitude: gp.latitude, latitude:gp.latitude,
longitude: gp.longitude longitude:gp.longitude
})) }))
} }
}; };
} }
} else { } else {
options.error('Unrecognized filter keys ' + keys.join(',')); options.error('Unrecognized filter keys ' + keys.join(','));
} }

View File

@@ -12,7 +12,7 @@ function createRecordsAPI(modelScope) {
return { return {
async query(className, query, options) { async query(className, query, options) {
if (typeof className === 'undefined') throw new Error("'className' is undefined"); if (typeof className === "undefined") throw new Error("'className' is undefined");
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
cloudstore().query({ cloudstore().query({
collection: className, collection: className,
@@ -27,9 +27,9 @@ function createRecordsAPI(modelScope) {
include: options ? options.include : undefined, include: options ? options.include : undefined,
select: options ? options.select : undefined, select: options ? options.select : undefined,
count: options ? options.count : undefined, count: options ? options.count : undefined,
success: (results, count) => { success: (results,count) => {
const _results = results.map((r) => cloudstore()._fromJSON(r, className)); const _results = results.map((r) => cloudstore()._fromJSON(r, className));
if (count !== undefined) resolve({ results: _results, count }); if(count !== undefined) resolve({results:_results,count});
else resolve(_results); else resolve(_results);
}, },
error: (err) => { error: (err) => {
@@ -40,7 +40,7 @@ function createRecordsAPI(modelScope) {
}, },
async count(className, query) { async count(className, query) {
if (typeof className === 'undefined') throw new Error("'className' is undefined"); if (typeof className === "undefined") throw new Error("'className' is undefined");
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
cloudstore().count({ cloudstore().count({
collection: className, collection: className,
@@ -62,7 +62,7 @@ function createRecordsAPI(modelScope) {
}, },
async distinct(className, property, query) { async distinct(className, property, query) {
if (typeof className === 'undefined') throw new Error("'className' is undefined"); if (typeof className === "undefined") throw new Error("'className' is undefined");
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
cloudstore().distinct({ cloudstore().distinct({
collection: className, collection: className,
@@ -85,7 +85,7 @@ function createRecordsAPI(modelScope) {
}, },
async aggregate(className, group, query) { async aggregate(className, group, query) {
if (typeof className === 'undefined') throw new Error("'className' is undefined"); if (typeof className === "undefined") throw new Error("'className' is undefined");
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
cloudstore().aggregate({ cloudstore().aggregate({
collection: className, collection: className,
@@ -107,35 +107,20 @@ function createRecordsAPI(modelScope) {
}); });
}, },
/**
*
* @param {string | { getId(): string; }} objectOrId
* @param {{
* className: string;
* keys?: string[] | string;
* include?: string[] | string;
* excludeKeys?: string[] | string;
* }} options
* @returns {Promise<unknown>}
*/
async fetch(objectOrId, options) { async fetch(objectOrId, options) {
if (typeof objectOrId === 'undefined') return Promise.reject(new Error("'objectOrId' is undefined.")); if (typeof objectOrId === 'undefined') return Promise.reject(new Error("'objectOrId' is undefined."));
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId(); if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class; const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!className) { if (!className) return reject('No class name specified');
return reject('No class name specified');
}
cloudstore().fetch({ cloudstore().fetch({
collection: className, collection: className,
objectId: objectOrId, objectId: objectOrId,
keys: options?.keys, include: options ? options.include : undefined,
include: options?.include,
excludeKeys: options?.excludeKeys,
success: function (response) { success: function (response) {
const record = cloudstore()._fromJSON(response, className); var record = cloudstore()._fromJSON(response, className);
resolve(record); resolve(record);
}, },
error: function (err) { error: function (err) {
@@ -201,7 +186,7 @@ function createRecordsAPI(modelScope) {
}, },
async create(className, properties, options) { async create(className, properties, options) {
if (typeof className === 'undefined') throw new Error("'className' is undefined"); if (typeof className === "undefined") throw new Error("'className' is undefined");
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
cloudstore().create({ cloudstore().create({
collection: className, collection: className,

View File

@@ -1,6 +1,6 @@
'use strict'; "use strict";
var Model = require('./model'); var Model = require("./model");
// Get and set proxy // Get and set proxy
/*const proxies = {} /*const proxies = {}
@@ -221,48 +221,48 @@ Collection.prototype.toJSON = function() {
}*/ }*/
// ---- // ----
Object.defineProperty(Array.prototype, 'items', { Object.defineProperty(Array.prototype, "items", {
enumerable: false, enumerable: false,
get() { get() {
return this; return this;
}, },
set(data) { set(data) {
this.set(data); this.set(data);
} },
}); });
Object.defineProperty(Array.prototype, 'each', { Object.defineProperty(Array.prototype, "each", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: Array.prototype.forEach value: Array.prototype.forEach,
}); });
Object.defineProperty(Array.prototype, 'size', { Object.defineProperty(Array.prototype, "size", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: function () { value: function () {
return this.length; return this.length;
} },
}); });
Object.defineProperty(Array.prototype, 'get', { Object.defineProperty(Array.prototype, "get", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: function (index) { value: function (index) {
return this[index]; return this[index];
} },
}); });
Object.defineProperty(Array.prototype, 'getId', { Object.defineProperty(Array.prototype, "getId", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: function () { value: function () {
return this._id; return this._id;
} },
}); });
Object.defineProperty(Array.prototype, 'id', { Object.defineProperty(Array.prototype, "id", {
enumerable: false, enumerable: false,
get() { get() {
return this.getId(); return this.getId();
} },
}); });
Object.defineProperty(Array.prototype, 'set', { Object.defineProperty(Array.prototype, "set", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: function (src) { value: function (src) {
@@ -323,10 +323,10 @@ Object.defineProperty(Array.prototype, 'set', {
for (i = aItems.length; i < bItems.length; i++) { for (i = aItems.length; i < bItems.length; i++) {
this.add(bItems[i]); this.add(bItems[i]);
} }
} },
}); });
Object.defineProperty(Array.prototype, 'notify', { Object.defineProperty(Array.prototype, "notify", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: async function (event, args) { value: async function (event, args) {
@@ -337,80 +337,80 @@ Object.defineProperty(Array.prototype, 'notify', {
for (var i = 0; i < l.length; i++) { for (var i = 0; i < l.length; i++) {
await l[i](args); await l[i](args);
} }
} },
}); });
Object.defineProperty(Array.prototype, 'contains', { Object.defineProperty(Array.prototype, "contains", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: function (item) { value: function (item) {
return this.indexOf(item) !== -1; return this.indexOf(item) !== -1;
} },
}); });
Object.defineProperty(Array.prototype, 'add', { Object.defineProperty(Array.prototype, "add", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: async function (item) { value: async function (item) {
if (this.contains(item)) return; // Already contains item if (this.contains(item)) return; // Already contains item
this.items.push(item); this.items.push(item);
await this.notify('add', { item: item, index: this.items.length - 1 }); await this.notify("add", { item: item, index: this.items.length - 1 });
await this.notify('change'); await this.notify("change");
await item.notify('add', { collection: this }); await item.notify("add", { collection: this });
} },
}); });
Object.defineProperty(Array.prototype, 'remove', { Object.defineProperty(Array.prototype, "remove", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: function (item) { value: function (item) {
var idx = this.items.indexOf(item); var idx = this.items.indexOf(item);
if (idx !== -1) this.removeAtIndex(idx); if (idx !== -1) this.removeAtIndex(idx);
} },
}); });
Object.defineProperty(Array.prototype, 'addAtIndex', { Object.defineProperty(Array.prototype, "addAtIndex", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: async function (item, index) { value: async function (item, index) {
if (this.contains(item)) return; // Already contains item if (this.contains(item)) return; // Already contains item
this.items.splice(index, 0, item); this.items.splice(index, 0, item);
await this.notify('add', { item: item, index: index }); await this.notify("add", { item: item, index: index });
await this.notify('change'); await this.notify("change");
await item.notify('add', { collection: this, index: index }); await item.notify("add", { collection: this, index: index });
} },
}); });
Object.defineProperty(Array.prototype, 'removeAtIndex', { Object.defineProperty(Array.prototype, "removeAtIndex", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: async function (idx) { value: async function (idx) {
var item = this.items[idx]; var item = this.items[idx];
this.items.splice(idx, 1); this.items.splice(idx, 1);
await this.notify('remove', { item: item, index: idx }); await this.notify("remove", { item: item, index: idx });
await this.notify('change'); await this.notify("change");
await item.notify('remove', { collection: this }); await item.notify("remove", { collection: this });
} },
}); });
Object.defineProperty(Array.prototype, 'on', { Object.defineProperty(Array.prototype, "on", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: function (event, listener) { value: function (event, listener) {
if (!this._listeners) if (!this._listeners)
Object.defineProperty(this, '_listeners', { Object.defineProperty(this, "_listeners", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: {} value: {},
}); });
if (!this._listeners[event]) this._listeners[event] = []; if (!this._listeners[event]) this._listeners[event] = [];
this._listeners[event].push(listener); this._listeners[event].push(listener);
} },
}); });
Object.defineProperty(Array.prototype, 'off', { Object.defineProperty(Array.prototype, "off", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: function (event, listener) { value: function (event, listener) {
@@ -418,20 +418,20 @@ Object.defineProperty(Array.prototype, 'off', {
if (!this._listeners[event]) return; if (!this._listeners[event]) return;
var idx = this._listeners[event].indexOf(listener); var idx = this._listeners[event].indexOf(listener);
if (idx !== -1) this._listeners[event].splice(idx, 1); if (idx !== -1) this._listeners[event].splice(idx, 1);
} },
}); });
class Collection extends Array {} class Collection extends Array {}
const collections = (Collection._collections = {}); var collections = (Collection._collections = {});
Collection.create = function (items) { Collection.create = function (items) {
const name = Model.guid(); const name = Model.guid();
collections[name] = new Collection(); collections[name] = new Collection();
Object.defineProperty(collections[name], '_id', { Object.defineProperty(collections[name], "_id", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: name value: name,
}); });
if (items) { if (items) {
collections[name].set(items); collections[name].set(items);
@@ -439,18 +439,14 @@ Collection.create = function (items) {
return collections[name]; return collections[name];
}; };
/**
* @param {string} name
* @returns {Collection}
*/
Collection.get = function (name) { Collection.get = function (name) {
if (name === undefined) name = Model.guid(); if (name === undefined) name = Model.guid();
if (!collections[name]) { if (!collections[name]) {
collections[name] = new Collection(); collections[name] = new Collection();
Object.defineProperty(collections[name], '_id', { Object.defineProperty(collections[name], "_id", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: name value: name,
}); });
} }

View File

@@ -35,11 +35,6 @@ const _modelProxyHandler = {
} }
}; };
/**
*
* @param {*} id
* @returns {Model}
*/
Model.get = function (id) { Model.get = function (id) {
if (id === undefined) id = Model.guid(); if (id === undefined) id = Model.guid();
if (!models[id]) { if (!models[id]) {
@@ -127,22 +122,13 @@ Model.prototype.fill = function (value = null) {
} }
}; };
/**
* @param {string} name
* @param {unknown} value
* @param {{
* resolve?: boolean;
* forceChange?: boolean;
* silent?: boolean;
* }} args
*/
Model.prototype.set = function (name, value, args) { Model.prototype.set = function (name, value, args) {
if (args && args.resolve && name.indexOf('.') !== -1) { if (args && args.resolve && name.indexOf('.') !== -1) {
// We should resolve path references // We should resolve path references
const path = name.split('.'); var path = name.split('.');
let model = this; var model = this;
for (let i = 0; i < path.length - 1; i++) { for (var i = 0; i < path.length - 1; i++) {
const v = model.get(path[i]); var v = model.get(path[i]);
if (Model.instanceOf(v)) model = v; if (Model.instanceOf(v)) model = v;
else return; // Path resolve failed else return; // Path resolve failed
} }
@@ -152,35 +138,24 @@ Model.prototype.set = function (name, value, args) {
const forceChange = args && args.forceChange; const forceChange = args && args.forceChange;
const oldValue = this.data[name]; var oldValue = this.data[name];
this.data[name] = value; this.data[name] = value;
(forceChange || oldValue !== value) &&
if ((forceChange || oldValue !== value) && (!args || !args.silent)) { (!args || !args.silent) &&
this.notify('change', { name: name, value: value, old: oldValue }); this.notify('change', { name: name, value: value, old: oldValue });
}
}; };
/**
* @returns {string}
*/
Model.prototype.getId = function () { Model.prototype.getId = function () {
return this.id; return this.id;
}; };
/**
* @param {string} name
* @param {{
* resolve?: boolean;
* }} args
* @returns {unknown}
*/
Model.prototype.get = function (name, args) { Model.prototype.get = function (name, args) {
if (args && args.resolve && name.indexOf('.') !== -1) { if (args && args.resolve && name.indexOf('.') !== -1) {
// We should resolve path references // We should resolve path references
const path = name.split('.'); var path = name.split('.');
let model = this; var model = this;
for (let i = 0; i < path.length - 1; i++) { for (var i = 0; i < path.length - 1; i++) {
const v = model.get(path[i]); var v = model.get(path[i]);
if (Model.instanceOf(v)) model = v; if (Model.instanceOf(v)) model = v;
else return; // Path resolve failed else return; // Path resolve failed
} }

View File

@@ -230,7 +230,7 @@ NodeContext.prototype.deregisterComponentModel = function (componentModel) {
NodeContext.prototype.fetchComponentBundle = async function (name) { NodeContext.prototype.fetchComponentBundle = async function (name) {
const fetchBundle = async (name) => { const fetchBundle = async (name) => {
let baseUrl = Noodl.Env['BaseUrl'] || '/'; let baseUrl = Noodl.Env["BaseUrl"] || '/';
let bundleUrl = `${baseUrl}noodl_bundles/${name}.json`; let bundleUrl = `${baseUrl}noodl_bundles/${name}.json`;
const response = await fetch(bundleUrl); const response = await fetch(bundleUrl);
@@ -455,15 +455,6 @@ NodeContext.prototype.setPopupCallbacks = function ({ onShow, onClose }) {
this.onClosePopup = onClose; this.onClosePopup = onClose;
}; };
/**
* @param {string} popupComponent
* @param {Record<string, unknown>} params
* @param {{
* senderNode?: unknown;
* onClosePopup?: (action?: string, results: object) => void;
* }} args
* @returns
*/
NodeContext.prototype.showPopup = async function (popupComponent, params, args) { NodeContext.prototype.showPopup = async function (popupComponent, params, args) {
if (!this.onShowPopup) return; if (!this.onShowPopup) return;

View File

@@ -28,7 +28,6 @@ var DbCollectionNode = {
_this.scheduleAfterInputsHaveUpdated(function () { _this.scheduleAfterInputsHaveUpdated(function () {
_this.flagOutputDirty('count'); _this.flagOutputDirty('count');
_this.flagOutputDirty('firstItemId'); _this.flagOutputDirty('firstItemId');
_this.flagOutputDirty('isEmpty');
collectionChangedScheduled = false; collectionChangedScheduled = false;
}); });
}; };
@@ -67,7 +66,6 @@ var DbCollectionNode = {
_this.flagOutputDirty('count'); _this.flagOutputDirty('count');
_this.flagOutputDirty('firstItemId'); _this.flagOutputDirty('firstItemId');
_this.flagOutputDirty('isEmpty');
} }
if (args.type === 'create') { if (args.type === 'create') {
@@ -93,7 +91,6 @@ var DbCollectionNode = {
_this.flagOutputDirty('count'); _this.flagOutputDirty('count');
_this.flagOutputDirty('firstItemId'); _this.flagOutputDirty('firstItemId');
_this.flagOutputDirty('isEmpty');
} else if (matchesQuery && !_this._internal.collection.contains(m)) { } else if (matchesQuery && !_this._internal.collection.contains(m)) {
// It's not part of the result collection but now matches they query, add it and resort // It's not part of the result collection but now matches they query, add it and resort
_addModelAtCorrectIndex(m); _addModelAtCorrectIndex(m);
@@ -109,7 +106,6 @@ var DbCollectionNode = {
_this.flagOutputDirty('count'); _this.flagOutputDirty('count');
_this.flagOutputDirty('firstItemId'); _this.flagOutputDirty('firstItemId');
_this.flagOutputDirty('isEmpty');
} }
} }
}; };
@@ -157,17 +153,6 @@ var DbCollectionNode = {
} }
} }
}, },
isEmpty: {
type: 'boolean',
displayName: 'Is Empty',
group: 'General',
getter: function () {
if (this._internal.collection) {
return this._internal.collection.size() === 0;
}
return true;
}
},
count: { count: {
type: 'number', type: 'number',
displayName: 'Count', displayName: 'Count',
@@ -204,7 +189,6 @@ var DbCollectionNode = {
setCollection: function (collection) { setCollection: function (collection) {
this.bindCollection(collection); this.bindCollection(collection);
this.flagOutputDirty('firstItemId'); this.flagOutputDirty('firstItemId');
this.flagOutputDirty('isEmpty');
this.flagOutputDirty('items'); this.flagOutputDirty('items');
this.flagOutputDirty('count'); this.flagOutputDirty('count');
}, },
@@ -273,7 +257,7 @@ var DbCollectionNode = {
limit: limit, limit: limit,
skip: skip, skip: skip,
count: count, count: count,
success: (results, count) => { success: (results,count) => {
if (results !== undefined) { if (results !== undefined) {
_c.set( _c.set(
results.map((i) => { results.map((i) => {
@@ -283,9 +267,10 @@ var DbCollectionNode = {
}) })
); );
} }
if (count !== undefined) { if(count !== undefined) {
this._internal.storageSettings.storageTotalCount = count; this._internal.storageSettings.storageTotalCount = count;
if (this.hasOutput('storageTotalCount')) this.flagOutputDirty('storageTotalCount'); if(this.hasOutput('storageTotalCount'))
this.flagOutputDirty('storageTotalCount');
} }
this.setCollection(_c); this.setCollection(_c);
this.sendSignalOnOutput('fetched'); this.sendSignalOnOutput('fetched');
@@ -398,7 +383,7 @@ var DbCollectionNode = {
if (!storageSettings['storageEnableLimit']) return; if (!storageSettings['storageEnableLimit']) return;
else return storageSettings['storageSkip'] || 0; else return storageSettings['storageSkip'] || 0;
}, },
getStorageFetchTotalCount: function () { getStorageFetchTotalCount: function() {
const storageSettings = this._internal.storageSettings; const storageSettings = this._internal.storageSettings;
return !!storageSettings['storageEnableCount']; return !!storageSettings['storageEnableCount'];

View File

@@ -2,10 +2,10 @@
const { Node, EdgeTriggeredInput } = require('../../../../noodl-runtime'); const { Node, EdgeTriggeredInput } = require('../../../../noodl-runtime');
const Model = require('../../../model'); var Model = require('../../../model');
const CloudStore = require('../../../api/cloudstore'); const CloudStore = require('../../../api/cloudstore');
const ModelNodeDefinition = { var ModelNodeDefinition = {
name: 'DbModel2', name: 'DbModel2',
docs: 'https://docs.noodl.net/nodes/data/cloud-data/record', docs: 'https://docs.noodl.net/nodes/data/cloud-data/record',
displayNodeName: 'Record', displayNodeName: 'Record',
@@ -21,11 +21,11 @@ const ModelNodeDefinition = {
} }
], ],
initialize: function () { initialize: function () {
const internal = this._internal; var internal = this._internal;
internal.inputValues = {}; internal.inputValues = {};
internal.relationModelIds = {}; internal.relationModelIds = {};
const _this = this; var _this = this;
this._internal.onModelChangedCallback = function (args) { this._internal.onModelChangedCallback = function (args) {
if (_this.isInputConnected('fetch')) return; if (_this.isInputConnected('fetch')) return;
@@ -109,18 +109,13 @@ const ModelNodeDefinition = {
displayName: 'Id', displayName: 'Id',
group: 'General', group: 'General',
set: function (value) { set: function (value) {
if (value instanceof Model) { if (value instanceof Model) value = value.getId();
// Can be passed as model as well // Can be passed as model as well
value = value.getId(); else if (typeof value === 'object') value = Model.create(value).getId(); // If this is an js object, dereference it
} else if (typeof value === 'object') {
// If this is an js object, dereference it
value = Model.create(value).getId();
}
this._internal.modelId = value; // Wait to fetch data this._internal.modelId = value; // Wait to fetch data
if (this.isInputConnected('fetch') === false) { if (this.isInputConnected('fetch') === false) this.setModelID(value);
this.setModelID(value); else {
} else {
this.flagOutputDirty('id'); this.flagOutputDirty('id');
} }
} }
@@ -143,10 +138,9 @@ const ModelNodeDefinition = {
this.setModel(model); this.setModel(model);
}, },
setModel: function (model) { setModel: function (model) {
if (this._internal.model) { if (this._internal.model)
// Remove old listener if existing // Remove old listener if existing
this._internal.model.off('change', this._internal.onModelChangedCallback); this._internal.model.off('change', this._internal.onModelChangedCallback);
}
this._internal.model = model; this._internal.model = model;
this.flagOutputDirty('id'); this.flagOutputDirty('id');
@@ -154,9 +148,7 @@ const ModelNodeDefinition = {
// We have a new model, mark all outputs as dirty // We have a new model, mark all outputs as dirty
for (var key in model.data) { for (var key in model.data) {
if (this.hasOutput('prop-' + key)) { if (this.hasOutput('prop-' + key)) this.flagOutputDirty('prop-' + key);
this.flagOutputDirty('prop-' + key);
}
} }
this.sendSignalOnOutput('fetched'); this.sendSignalOnOutput('fetched');
}, },
@@ -192,7 +184,7 @@ const ModelNodeDefinition = {
} }
}, },
scheduleFetch: function () { scheduleFetch: function () {
const _this = this; var _this = this;
const internal = this._internal; const internal = this._internal;
this.scheduleOnce('Fetch', function () { this.scheduleOnce('Fetch', function () {
@@ -207,13 +199,12 @@ const ModelNodeDefinition = {
collection: internal.collectionId, collection: internal.collectionId,
objectId: internal.modelId, // Get the objectId part of the model id objectId: internal.modelId, // Get the objectId part of the model id
success: function (response) { success: function (response) {
const model = cloudstore._fromJSON(response, internal.collectionId); var model = cloudstore._fromJSON(response, internal.collectionId);
if (internal.model !== model) { if (internal.model !== model) {
// Check if we need to change model // Check if we need to change model
if (internal.model) { if (internal.model)
// Remove old listener if existing // Remove old listener if existing
internal.model.off('change', internal.onModelChangedCallback); internal.model.off('change', internal.onModelChangedCallback);
}
internal.model = model; internal.model = model;
model.on('change', internal.onModelChangedCallback); model.on('change', internal.onModelChangedCallback);
@@ -222,10 +213,8 @@ const ModelNodeDefinition = {
delete response.objectId; delete response.objectId;
for (const key in response) { for (var key in response) {
if (_this.hasOutput('prop-' + key)) { if (_this.hasOutput('prop-' + key)) _this.flagOutputDirty('prop-' + key);
_this.flagOutputDirty('prop-' + key);
}
} }
_this.sendSignalOnOutput('fetched'); _this.sendSignalOnOutput('fetched');
@@ -237,6 +226,7 @@ const ModelNodeDefinition = {
}); });
}, },
scheduleStore: function () { scheduleStore: function () {
var _this = this;
var internal = this._internal; var internal = this._internal;
if (!internal.model) return; if (!internal.model) return;
@@ -257,6 +247,8 @@ const ModelNodeDefinition = {
}); });
}, },
registerInputIfNeeded: function (name) { registerInputIfNeeded: function (name) {
var _this = this;
if (this.hasInput(name)) { if (this.hasInput(name)) {
return; return;
} }
@@ -336,7 +328,8 @@ function updatePorts(nodeId, parameters, editorConnection, graphModel) {
var p = props[key]; var p = props[key];
if (ports.find((_p) => _p.name === key)) continue; if (ports.find((_p) => _p.name === key)) continue;
if (p.type !== 'Relation') { if (p.type === 'Relation') {
} else {
// Other schema type ports // Other schema type ports
const _typeMap = { const _typeMap = {
String: 'string', String: 'string',
@@ -380,16 +373,16 @@ module.exports = {
function _managePortsForNode(node) { function _managePortsForNode(node) {
updatePorts(node.id, node.parameters, context.editorConnection, graphModel); updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
node.on('parameterUpdated', function () { node.on('parameterUpdated', function (event) {
updatePorts(node.id, node.parameters, context.editorConnection, graphModel); updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
}); });
graphModel.on('metadataChanged.dbCollections', function () { graphModel.on('metadataChanged.dbCollections', function (data) {
CloudStore.invalidateCollections(); CloudStore.invalidateCollections();
updatePorts(node.id, node.parameters, context.editorConnection, graphModel); updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
}); });
graphModel.on('metadataChanged.systemCollections', function () { graphModel.on('metadataChanged.systemCollections', function (data) {
CloudStore.invalidateCollections(); CloudStore.invalidateCollections();
updatePorts(node.id, node.parameters, context.editorConnection, graphModel); updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
}); });

View File

@@ -31,6 +31,8 @@ const DateToStringNode = {
this._internal.currentInput = _value; this._internal.currentInput = _value;
this._format(); this._format();
this.flagOutputDirty('currentValue');
this.sendSignalOnOutput('inputChanged');
} }
} }
}, },
@@ -47,16 +49,10 @@ const DateToStringNode = {
type: 'signal', type: 'signal',
displayName: 'Date Changed', displayName: 'Date Changed',
group: 'Signals' group: 'Signals'
},
onError: {
type: 'signal',
displayName: 'Invalid Date',
group: 'Signals'
} }
}, },
methods: { methods: {
_format() { _format() {
try {
const t = this._internal.currentInput; const t = this._internal.currentInput;
const format = this._internal.formatString; const format = this._internal.formatString;
const date = ('0' + t.getDate()).slice(-2); const date = ('0' + t.getDate()).slice(-2);
@@ -77,15 +73,6 @@ const DateToStringNode = {
.replace(/\{hours\}/g, hours) .replace(/\{hours\}/g, hours)
.replace(/\{minutes\}/g, minutes) .replace(/\{minutes\}/g, minutes)
.replace(/\{seconds\}/g, seconds); .replace(/\{seconds\}/g, seconds);
} catch (error) {
// Set the output to be blank, makes it easier to handle.
this._internal.dateString = '';
this.flagOutputDirty('onError');
}
// Flag that the value have changed
this.flagOutputDirty('currentValue');
this.sendSignalOnOutput('inputChanged');
} }
} }
}; };

View File

@@ -2,8 +2,6 @@ const StringFormatDefinition = {
name: 'String Format', name: 'String Format',
docs: 'https://docs.noodl.net/nodes/string-manipulation/string-format', docs: 'https://docs.noodl.net/nodes/string-manipulation/string-format',
category: 'String Manipulation', category: 'String Manipulation',
usePortAsLabel: 'format',
portLabelTruncationMode: 'length',
initialize() { initialize() {
const internal = this._internal; const internal = this._internal;
internal.format = ''; internal.format = '';

View File

@@ -227,9 +227,6 @@ declare namespace Noodl {
objectOrId: string | { getId(): string; }, objectOrId: string | { getId(): string; },
options?: { options?: {
className?: RecordClassName; className?: RecordClassName;
keys?: string[] | string;
include?: string[] | string;
excludeKeys?: string[] | string;
} }
): Promise<any>; ): Promise<any>;

View File

@@ -1,8 +1,8 @@
const { merge } = require('webpack-merge'); const { merge } = require("webpack-merge");
const common = require('./webpack.viewer.common.js'); const common = require("./webpack.viewer.common.js");
module.exports = merge(common, { module.exports = merge(common, {
mode: 'development', mode: "development",
devtool: 'inline-source-map', devtool: "inline-source-map",
watch: true watch: true,
}); });

View File

@@ -2,6 +2,5 @@ const { merge } = require('webpack-merge');
const common = require('./webpack.viewer.common.js'); const common = require('./webpack.viewer.common.js');
module.exports = merge(common, { module.exports = merge(common, {
mode: 'production', mode: 'production'
devtool: 'source-map'
}); });

View File

@@ -2,18 +2,12 @@ const { RouterHandler } = require('../nodes/navigation/router-handler');
const NoodlRuntime = require('@noodl/runtime'); const NoodlRuntime = require('@noodl/runtime');
const navigation = { const navigation = {
/**
* This is set by "packages/noodl-viewer-react/src/noodl-js-api.js"
* @type {NoodlRuntime}
*/
_noodlRuntime: undefined,
async showPopup(componentPath, params) { async showPopup(componentPath, params) {
return new Promise((resolve) => { return new Promise((resolve) => {
navigation._noodlRuntime.context.showPopup(componentPath, params, { navigation._noodlRuntime.context.showPopup(componentPath, params, {
onClosePopup: (action, results) => { onClosePopup: (action, results) => {
resolve({ resolve({
action: action?.replace('closeAction-', ''), action: action.replace('closeAction-', ''),
parameters: results parameters: results
}); });
} }

View File

@@ -8,8 +8,6 @@ export interface ButtonProps extends Noodl.ReactProps {
enabled: boolean; enabled: boolean;
buttonType: 'button' | 'submit'; buttonType: 'button' | 'submit';
attrs: React.Attributes;
textStyle: Noodl.TextStyle; textStyle: Noodl.TextStyle;
useLabel: boolean; useLabel: boolean;
@@ -98,7 +96,6 @@ export function Button(props: ButtonProps) {
return ( return (
<button <button
{...props.attrs}
className={className} className={className}
disabled={!props.enabled} disabled={!props.enabled}
{...Utils.controlEvents(props)} {...Utils.controlEvents(props)}

View File

@@ -9,8 +9,6 @@ export interface CheckboxProps extends Noodl.ReactProps {
enabled: boolean; enabled: boolean;
checked: boolean; checked: boolean;
attrs: React.Attributes;
useLabel: boolean; useLabel: boolean;
label: string; label: string;
labelSpacing: string; labelSpacing: string;
@@ -49,7 +47,6 @@ export function Checkbox(props: CheckboxProps) {
Layout.align(style, props); Layout.align(style, props);
const inputProps = { const inputProps = {
...props.attrs,
id: props.id, id: props.id,
className: [props.className, 'ndl-controls-checkbox-2'].join(' '), className: [props.className, 'ndl-controls-checkbox-2'].join(' '),
disabled: !props.enabled, disabled: !props.enabled,

View File

@@ -10,8 +10,6 @@ export interface RadioButtonProps extends Noodl.ReactProps {
enabled: boolean; enabled: boolean;
value: string; value: string;
attrs: React.Attributes;
useLabel: boolean; useLabel: boolean;
label: string; label: string;
labelSpacing: string; labelSpacing: string;
@@ -49,7 +47,6 @@ export function RadioButton(props: RadioButtonProps) {
props.checkedChanged && props.checkedChanged(radioButtonGroup ? radioButtonGroup.selected === props.value : false); props.checkedChanged && props.checkedChanged(radioButtonGroup ? radioButtonGroup.selected === props.value : false);
const inputProps = { const inputProps = {
...props.attrs,
id: props.id, id: props.id,
disabled: !props.enabled, disabled: !props.enabled,
className: [props.className, 'ndl-controls-radio-2'].join(' '), className: [props.className, 'ndl-controls-radio-2'].join(' '),

View File

@@ -12,8 +12,6 @@ export interface SelectProps extends Noodl.ReactProps {
textStyle: Noodl.TextStyle; textStyle: Noodl.TextStyle;
items: TSFixme; items: TSFixme;
attrs: React.Attributes;
placeholder: string; placeholder: string;
placeholderOpacity: string; placeholderOpacity: string;
@@ -83,7 +81,6 @@ export function Select(props: SelectProps) {
} }
const inputProps = { const inputProps = {
...props.attrs,
id: props.id, id: props.id,
className: props.className, className: props.className,
style: { style: {

View File

@@ -9,8 +9,6 @@ export interface SliderProps extends Noodl.ReactProps {
id: string; id: string;
enabled: boolean; enabled: boolean;
attrs: React.Attributes;
value: number; value: number;
min: number; min: number;
max: number; max: number;
@@ -105,7 +103,6 @@ export function Slider(props: SliderProps) {
const className = `ndl-controls-range2 ${instanceClassId} ${props.className ? props.className : ''} `; const className = `ndl-controls-range2 ${instanceClassId} ${props.className ? props.className : ''} `;
const inputProps: React.InputHTMLAttributes<HTMLInputElement> = { const inputProps: React.InputHTMLAttributes<HTMLInputElement> = {
...props.attrs,
id: props.id, id: props.id,
style: { style: {
width: '100%', width: '100%',

View File

@@ -17,8 +17,6 @@ export interface TextInputProps extends Noodl.ReactProps {
type: 'text' | 'textArea' | 'email' | 'number' | 'password' | 'url'; type: 'text' | 'textArea' | 'email' | 'number' | 'password' | 'url';
textStyle: Noodl.TextStyle; textStyle: Noodl.TextStyle;
attrs: React.Attributes;
enabled: boolean; enabled: boolean;
placeholder: string; placeholder: string;
@@ -151,7 +149,6 @@ export class TextInput extends React.Component<TextInputProps, State> {
inputStyles.color = props.noodlNode.context.styles.resolveColor(inputStyles.color); inputStyles.color = props.noodlNode.context.styles.resolveColor(inputStyles.color);
const inputProps = { const inputProps = {
...props.attrs,
id: props.id, id: props.id,
value: this.state.value, value: this.state.value,
...Utils.controlEvents(props), ...Utils.controlEvents(props),

View File

@@ -7,8 +7,6 @@ export interface ColumnsProps extends Noodl.ReactProps {
marginX: string; marginX: string;
marginY: string; marginY: string;
attrs: React.Attributes;
justifyContent: 'flex-start' | 'flex-end' | 'center'; justifyContent: 'flex-start' | 'flex-end' | 'center';
direction: 'row' | 'column'; direction: 'row' | 'column';
minWidth: string; minWidth: string;
@@ -117,10 +115,7 @@ export function Columns(props: ColumnsProps) {
// ForEachCompoent breaks the layout but is needed to send onMount/onUnmount // ForEachCompoent breaks the layout but is needed to send onMount/onUnmount
if (!Array.isArray(props.children)) { if (!Array.isArray(props.children)) {
// @ts-expect-error props.children.type is any children = [props.children];
if (props.children.type !== ForEachComponent) {
children = [props.children]
}
} else { } else {
children = props.children.filter((child) => child.type !== ForEachComponent); children = props.children.filter((child) => child.type !== ForEachComponent);
forEachComponent = props.children.find((child) => child.type === ForEachComponent); forEachComponent = props.children.find((child) => child.type === ForEachComponent);
@@ -128,11 +123,9 @@ export function Columns(props: ColumnsProps) {
return ( return (
<div <div
{...props.attrs}
className={['columns-container', props.className].join(' ')} className={['columns-container', props.className].join(' ')}
ref={containerRef} ref={containerRef}
style={{ style={{
visibility: containerWidth === null ? "hidden" : "visible",
marginTop: parseFloat(props.marginY) * -1, marginTop: parseFloat(props.marginY) * -1,
marginLeft: parseFloat(props.marginX) * -1, marginLeft: parseFloat(props.marginX) * -1,
display: 'flex', display: 'flex',

View File

@@ -19,8 +19,6 @@ BScroll.use(Slide);
export interface GroupProps extends Noodl.ReactProps { export interface GroupProps extends Noodl.ReactProps {
as?: keyof JSX.IntrinsicElements | React.ComponentType<unknown>; as?: keyof JSX.IntrinsicElements | React.ComponentType<unknown>;
attrs: React.Attributes;
scrollSnapEnabled: boolean; scrollSnapEnabled: boolean;
showScrollbar: boolean; showScrollbar: boolean;
scrollEnabled: boolean; scrollEnabled: boolean;
@@ -273,7 +271,6 @@ export class Group extends React.Component<GroupProps> {
<Component <Component
// @ts-expect-error Lets hope that the type passed here is always static! // @ts-expect-error Lets hope that the type passed here is always static!
className={props.className} className={props.className}
{...props.attrs}
{...props.dom} {...props.dom}
{...PointerListeners(props)} {...PointerListeners(props)}
style={style} style={style}

View File

@@ -10,7 +10,6 @@ export interface ImageProps extends Noodl.ReactProps {
src: string; src: string;
onLoad?: () => void; onLoad?: () => void;
}; };
attrs: React.Attributes;
} }
export function Image(props: ImageProps) { export function Image(props: ImageProps) {
@@ -31,5 +30,5 @@ export function Image(props: ImageProps) {
} }
} }
return <img {...props.attrs} className={props.className} {...props.dom} {...PointerListeners(props)} style={style} />; return <img className={props.className} {...props.dom} {...PointerListeners(props)} style={style} />;
} }

View File

@@ -7,8 +7,6 @@ import { Noodl } from '../../../types';
export interface TextProps extends Noodl.ReactProps { export interface TextProps extends Noodl.ReactProps {
as?: keyof JSX.IntrinsicElements | React.ComponentType<unknown>; as?: keyof JSX.IntrinsicElements | React.ComponentType<unknown>;
attrs: React.Attributes;
textStyle: Noodl.TextStyle; textStyle: Noodl.TextStyle;
text: string; text: string;
@@ -50,7 +48,6 @@ export function Text(props: TextProps) {
return ( return (
<Component <Component
className={['ndl-visual-text', props.className].join(' ')} className={['ndl-visual-text', props.className].join(' ')}
{...props.attrs}
{...props.dom} {...props.dom}
{...PointerListeners(props)} {...PointerListeners(props)}
style={style} style={style}

View File

@@ -1,9 +1,10 @@
'use strict'; 'use strict';
const { Node } = require('@noodl/runtime'); const { Node } = require('@noodl/runtime');
const Model = require('@noodl/runtime/src/model');
const VariableNodeDefinition = { var Model = require('@noodl/runtime/src/model');
var VariableNodeDefinition = {
name: 'Variable', name: 'Variable',
docs: 'https://docs.noodl.net/nodes/data/variable', docs: 'https://docs.noodl.net/nodes/data/variable',
category: 'Data', category: 'Data',

View File

@@ -30,24 +30,11 @@ const ButtonNode = {
] ]
}, },
initialize() { initialize() {
this.props.attrs = {};
this.props.layout = 'row'; //Used to tell child nodes what layout to expect this.props.layout = 'row'; //Used to tell child nodes what layout to expect
}, },
getReactComponent() { getReactComponent() {
return Button; return Button;
}, },
inputs: {
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
}
},
inputCss: { inputCss: {
backgroundColor: { backgroundColor: {
index: 100, index: 100,

View File

@@ -31,7 +31,6 @@ const CheckBoxNode = {
] ]
}, },
initialize() { initialize() {
this.props.attrs = {};
this.props.sizeMode = 'explicit'; this.props.sizeMode = 'explicit';
this.props.id = 'input-' + guid(); this.props.id = 'input-' + guid();
this.props.checked = this._internal.checked = false; this.props.checked = this._internal.checked = false;
@@ -95,16 +94,6 @@ const CheckBoxNode = {
this.flagOutputDirty('checked'); this.flagOutputDirty('checked');
this._updateVisualState(); this._updateVisualState();
} }
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
} }
}, },
inputCss: { inputCss: {

View File

@@ -31,7 +31,6 @@ const OptionsNode = {
] ]
}, },
initialize: function () { initialize: function () {
this.props.attrs = {};
this._itemsChanged = () => { this._itemsChanged = () => {
this.forceUpdate(); this.forceUpdate();
}; };
@@ -91,16 +90,6 @@ const OptionsNode = {
this.flagOutputDirty('value'); this.flagOutputDirty('value');
} }
} }
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
} }
}, },
inputProps: { inputProps: {

View File

@@ -31,7 +31,6 @@ const RadioButtonNode = {
] ]
}, },
initialize() { initialize() {
this.props.attrs = {};
this.props.sizeMode = 'explicit'; this.props.sizeMode = 'explicit';
this.props.id = 'input-' + guid(); this.props.id = 'input-' + guid();
@@ -62,16 +61,6 @@ const RadioButtonNode = {
set(value) { set(value) {
this.setStyle({ backgroundColor: value }, 'fill'); this.setStyle({ backgroundColor: value }, 'fill');
} }
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
} }
}, },
inputProps: { inputProps: {

View File

@@ -27,7 +27,6 @@ const RangeNode = {
] ]
}, },
initialize() { initialize() {
this.props.attrs = {};
this.props.sizeMode = 'contentHeight'; this.props.sizeMode = 'contentHeight';
this.props.id = 'input-' + guid(); this.props.id = 'input-' + guid();
@@ -68,16 +67,6 @@ const RangeNode = {
set(value) { set(value) {
this._setInputValue(value); this._setInputValue(value);
} }
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
} }
}, },
outputs: { outputs: {

View File

@@ -43,7 +43,6 @@ const TextInputNode = {
return TextInput; return TextInput;
}, },
initialize() { initialize() {
this.props.attrs = {};
this.props.startValue = ''; this.props.startValue = '';
this.props.id = this._internal.controlId = 'input-' + guid(); this.props.id = this._internal.controlId = 'input-' + guid();
}, },
@@ -168,16 +167,6 @@ const TextInputNode = {
break; break;
} }
} }
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
} }
}, },
inputCss: { inputCss: {

View File

@@ -40,8 +40,8 @@ const ClosePopupNode = {
this._internal.closeCallback = cb; this._internal.closeCallback = cb;
}, },
scheduleClose: function () { scheduleClose: function () {
const _this = this; var _this = this;
const internal = this._internal; var internal = this._internal;
if (!internal.hasScheduledClose) { if (!internal.hasScheduledClose) {
internal.hasScheduledClose = true; internal.hasScheduledClose = true;
this.scheduleAfterInputsHaveUpdated(function () { this.scheduleAfterInputsHaveUpdated(function () {
@@ -51,9 +51,8 @@ const ClosePopupNode = {
} }
}, },
close: function () { close: function () {
if (this._internal.closeCallback) { if (this._internal.closeCallback)
this._internal.closeCallback(this._internal.closeAction, this._internal.resultValues); this._internal.closeCallback(this._internal.closeAction, this._internal.resultValues);
}
}, },
closeActionTriggered: function (name) { closeActionTriggered: function (name) {
this._internal.closeAction = name; this._internal.closeAction = name;
@@ -113,8 +112,9 @@ module.exports = {
var closeActions = node.parameters['closeActions']; var closeActions = node.parameters['closeActions'];
if (closeActions) { if (closeActions) {
closeActions = closeActions ? closeActions.split(',') : undefined; closeActions = closeActions ? closeActions.split(',') : undefined;
for (const i in closeActions) { for (var i in closeActions) {
const p = closeActions[i]; var p = closeActions[i];
ports.push({ ports.push({
type: 'signal', type: 'signal',
plug: 'input', plug: 'input',

View File

@@ -192,7 +192,8 @@ function setup(context, graphModel) {
enums: pages.map((p) => ({ enums: pages.map((p) => ({
label: p.label, label: p.label,
value: p.id value: p.id
})) })),
allowEditOnly: true
}, },
group: 'General', group: 'General',
displayName: 'Target Page', displayName: 'Target Page',

View File

@@ -1,5 +1,3 @@
import React from 'react';
import ASyncQueue from '../../async-queue'; import ASyncQueue from '../../async-queue';
import { createNodeFromReactComponent } from '../../react-component-node'; import { createNodeFromReactComponent } from '../../react-component-node';
@@ -77,13 +75,10 @@ const PageStack = {
const info = [{ type: 'text', value: 'Active Components:' }]; const info = [{ type: 'text', value: 'Active Components:' }];
return info.concat( return info.concat(
this._internal.stack.map((p) => { this._internal.stack.map((p, i) => ({
const pageInfo = this._findPage(p.pageId);
return {
type: 'text', type: 'text',
value: '- ' + pageInfo.label value: '- ' + this._internal.pages.find((pi) => pi.id === p.pageId).label
}; }))
})
); );
}, },
defaultCss: { defaultCss: {
@@ -175,7 +170,6 @@ const PageStack = {
topPageName: { topPageName: {
type: 'string', type: 'string',
displayName: 'Top Component Name', displayName: 'Top Component Name',
group: 'General',
get() { get() {
return this._internal.topPageName; return this._internal.topPageName;
} }
@@ -183,7 +177,6 @@ const PageStack = {
stackDepth: { stackDepth: {
type: 'number', type: 'number',
displayName: 'Stack Depth', displayName: 'Stack Depth',
group: 'General',
get() { get() {
return this._internal.stackDepth; return this._internal.stackDepth;
} }
@@ -196,31 +189,12 @@ const PageStack = {
_deregisterPageStack() { _deregisterPageStack() {
NavigationHandler.instance.deregisterPageStack(this._internal.name, this); NavigationHandler.instance.deregisterPageStack(this._internal.name, this);
}, },
/** _pageNameForId(id) {
* @param {String} pageIdOrLabel if (this._internal.pages === undefined) return;
*/ const page = this._internal.pages.find((p) => p.id === id);
_findPage(pageIdOrLabel) { if (page === undefined) return;
if (this._internal.pageInfo[pageIdOrLabel]) {
const pageInfo = this._internal.pageInfo[pageIdOrLabel];
const pageRef = this._internal.pages.find((x) => x.id === pageIdOrLabel);
return {
component: String(pageInfo.component),
label: String(pageRef.label),
id: String(pageIdOrLabel)
};
}
const pageRef = this._internal.pages.find((x) => x.label === pageIdOrLabel); return page.label;
if (pageRef) {
const pageInfo = this._internal.pageInfo[pageRef.id];
return {
component: String(pageInfo.component),
label: String(pageRef.label),
id: String(pageRef.id)
};
}
return undefined;
}, },
setPageOutputs(outputs) { setPageOutputs(outputs) {
for (const prop in outputs) { for (const prop in outputs) {
@@ -256,9 +230,8 @@ const PageStack = {
if (this._internal.pages === undefined || this._internal.pages.length === 0) return; if (this._internal.pages === undefined || this._internal.pages.length === 0) return;
let startPageId; var startPageId,
let params = {}; params = {};
var pageFromUrl = this.matchPageFromUrl(); var pageFromUrl = this.matchPageFromUrl();
if (pageFromUrl !== undefined) { if (pageFromUrl !== undefined) {
// We have an url matching a page, use that page as start page // We have an url matching a page, use that page as start page
@@ -266,16 +239,13 @@ const PageStack = {
params = Object.assign({}, pageFromUrl.query, pageFromUrl.params); params = Object.assign({}, pageFromUrl.query, pageFromUrl.params);
} else { } else {
startPageId = this._internal.startPageId; var startPageId = this._internal.startPageId;
if (startPageId === undefined) startPageId = this._internal.pages[0].id; if (startPageId === undefined) startPageId = this._internal.pages[0].id;
} }
// Find the page by either ID or by Label var pageInfo = this._internal.pageInfo[startPageId];
const pageInfo = this._findPage(startPageId);
if (pageInfo === undefined || pageInfo.component === undefined) { if (pageInfo === undefined || pageInfo.component === undefined) return; // No component specified for page
// No page was found
return;
}
var content = await this.nodeScope.createNode(pageInfo.component, guid()); var content = await this.nodeScope.createNode(pageInfo.component, guid());
@@ -299,7 +269,7 @@ const PageStack = {
]; ];
this.setPageOutputs({ this.setPageOutputs({
topPageName: pageInfo.label, topPageName: this._pageNameForId(startPageId),
stackDepth: this._internal.stack.length stackDepth: this._internal.stack.length
}); });
}, },
@@ -488,22 +458,13 @@ const PageStack = {
this._internal.asyncQueue.enqueue(this.replaceAsync.bind(this, args)); this._internal.asyncQueue.enqueue(this.replaceAsync.bind(this, args));
}, },
async replaceAsync(args) { async replaceAsync(args) {
if (this._internal.pages === undefined || this._internal.pages.length === 0) { if (this._internal.pages === undefined || this._internal.pages.length === 0) return;
return;
}
if (this._internal.isTransitioning) { if (this._internal.isTransitioning) return;
return;
}
const pageId = args.target || this._internal.pages[0].id; var pageId = args.target || this._internal.pages[0].id;
var pageInfo = this._internal.pageInfo[pageId];
// Find the page by either ID or by Label if (pageInfo === undefined || pageInfo.component === undefined) return; // No component specified for page
const pageInfo = this._findPage(pageId);
if (pageInfo === undefined || pageInfo.component === undefined) {
// No page was found
return;
}
// Remove all current pages in the stack // Remove all current pages in the stack
var children = this.getChildren(); var children = this.getChildren();
@@ -537,7 +498,7 @@ const PageStack = {
]; ];
this.setPageOutputs({ this.setPageOutputs({
topPageName: pageInfo.label, topPageName: this._pageNameForId(pageId),
stackDepth: this._internal.stack.length stackDepth: this._internal.stack.length
}); });
@@ -549,22 +510,13 @@ const PageStack = {
this._internal.asyncQueue.enqueue(this.navigateAsync.bind(this, args)); this._internal.asyncQueue.enqueue(this.navigateAsync.bind(this, args));
}, },
async navigateAsync(args) { async navigateAsync(args) {
if (this._internal.pages === undefined || this._internal.pages.length === 0) { if (this._internal.pages === undefined || this._internal.pages.length === 0) return;
return;
}
if (this._internal.isTransitioning) { if (this._internal.isTransitioning) return;
return;
}
const pageId = args.target || this._internal.pages[0].id; var pageId = args.target || this._internal.pages[0].id;
var pageInfo = this._internal.pageInfo[pageId];
// Find the page by either ID or by Label if (pageInfo === undefined || pageInfo.component === undefined) return; // No component specified for page
const pageInfo = this._findPage(pageId);
if (pageInfo === undefined || pageInfo.component === undefined) {
// No page was found
return;
}
// Create the container group // Create the container group
const group = this.createPageContainer(); const group = this.createPageContainer();
@@ -578,7 +530,7 @@ const PageStack = {
group.addChild(content); group.addChild(content);
// Connect navigate back nodes // Connect navigate back nodes
const navigateBackNodes = content.nodeScope.getNodesWithType('PageStackNavigateBack'); var navigateBackNodes = content.nodeScope.getNodesWithType('PageStackNavigateBack');
if (navigateBackNodes && navigateBackNodes.length > 0) { if (navigateBackNodes && navigateBackNodes.length > 0) {
for (var j = 0; j < navigateBackNodes.length; j++) { for (var j = 0; j < navigateBackNodes.length; j++) {
navigateBackNodes[j]._setBackCallback(this.back.bind(this)); navigateBackNodes[j]._setBackCallback(this.back.bind(this));
@@ -586,8 +538,8 @@ const PageStack = {
} }
// Push the new top // Push the new top
const top = this._internal.stack[this._internal.stack.length - 1]; var top = this._internal.stack[this._internal.stack.length - 1];
const newTop = { var newTop = {
from: top.page, from: top.page,
page: group, page: group,
pageInfo: pageInfo, pageInfo: pageInfo,
@@ -599,7 +551,7 @@ const PageStack = {
}; };
this._internal.stack.push(newTop); this._internal.stack.push(newTop);
this.setPageOutputs({ this.setPageOutputs({
topPageName: pageInfo.label, topPageName: this._pageNameForId(args.target),
stackDepth: this._internal.stack.length stackDepth: this._internal.stack.length
}); });
this._updateUrlWithTopPage(); this._updateUrlWithTopPage();
@@ -632,11 +584,8 @@ const PageStack = {
this.addChild(top.from, 0); this.addChild(top.from, 0);
top.backCallback && top.backCallback(args.backAction, args.results); top.backCallback && top.backCallback(args.backAction, args.results);
// Find the page by either ID or by Label
const pageInfo = this._findPage(this._internal.stack[this._internal.stack.length - 2].pageId);
this.setPageOutputs({ this.setPageOutputs({
topPageName: pageInfo.label, topPageName: this._pageNameForId(this._internal.stack[this._internal.stack.length - 2].pageId),
stackDepth: this._internal.stack.length - 1 stackDepth: this._internal.stack.length - 1
}); });

View File

@@ -53,24 +53,15 @@ const ShowPopupNode = {
this.context.showPopup(this._internal.target, this._internal.popupParams, { this.context.showPopup(this._internal.target, this._internal.popupParams, {
senderNode: this.nodeScope.componentOwner, senderNode: this.nodeScope.componentOwner,
/**
* @param {string | undefined} action
* @param {*} results
*/
onClosePopup: (action, results) => { onClosePopup: (action, results) => {
this._internal.closeResults = results; this._internal.closeResults = results;
for (const key in results) { for (var key in results) {
if (this.hasOutput('closeResult-' + key)) { if (this.hasOutput('closeResult-' + key)) this.flagOutputDirty('closeResult-' + key);
this.flagOutputDirty('closeResult-' + key);
}
} }
if (!action) { if (!action) this.sendSignalOnOutput('Closed');
this.sendSignalOnOutput('Closed'); else this.sendSignalOnOutput(action);
} else {
this.sendSignalOnOutput(action);
}
} }
}); });
}, },

View File

@@ -2,7 +2,8 @@
const { Node } = require('@noodl/runtime'); const { Node } = require('@noodl/runtime');
const Collection = require('@noodl/runtime/src/collection'); var Model = require('@noodl/runtime/src/model'),
Collection = require('@noodl/runtime/src/collection');
var CollectionNode = { var CollectionNode = {
name: 'Collection2', name: 'Collection2',
@@ -26,7 +27,6 @@ var CollectionNode = {
_this.scheduleAfterInputsHaveUpdated(function () { _this.scheduleAfterInputsHaveUpdated(function () {
_this.sendSignalOnOutput('changed'); _this.sendSignalOnOutput('changed');
_this.flagOutputDirty('firstItemId');
_this.flagOutputDirty('count'); _this.flagOutputDirty('count');
collectionChangedScheduled = false; collectionChangedScheduled = false;
}); });
@@ -117,17 +117,6 @@ var CollectionNode = {
return this._internal.collection; return this._internal.collection;
} }
}, },
firstItemId: {
type: 'string',
displayName: 'First Item Id',
group: 'General',
getter: function () {
if (this._internal.collection) {
var firstItem = this._internal.collection.get(0);
if (firstItem !== undefined) return firstItem.getId();
}
}
},
count: { count: {
type: 'number', type: 'number',
displayName: 'Count', displayName: 'Count',
@@ -161,7 +150,6 @@ var CollectionNode = {
collection.on('change', this._internal.collectionChangedCallback); collection.on('change', this._internal.collectionChangedCallback);
this.flagOutputDirty('items'); this.flagOutputDirty('items');
this.flagOutputDirty('firstItemId');
this.flagOutputDirty('count'); this.flagOutputDirty('count');
}, },
setSourceCollection: function (collection) { setSourceCollection: function (collection) {

View File

@@ -1,24 +1,20 @@
'use strict'; 'use strict';
const { Node } = require('@noodl/runtime');
const Model = require('@noodl/runtime/src/model'); const Model = require('@noodl/runtime/src/model');
const Collection = require('@noodl/runtime/src/collection'); const Collection = require('@noodl/runtime/src/collection');
const SetVariableNodeDefinition = { var SetVariableNodeDefinition = {
name: 'Set Variable', name: 'Set Variable',
docs: 'https://docs.noodl.net/nodes/data/variable/set-variable', docs: 'https://docs.noodl.net/nodes/data/variable/set-variable',
category: 'Data', category: 'Data',
usePortAsLabel: 'name', usePortAsLabel: 'name',
color: 'data', color: 'data',
initialize: function () { initialize: function () {
const internal = this._internal; var internal = this._internal;
internal.variablesModel = Model.get('--ndl--global-variables');
},
getInspectInfo() {
if (this._internal.name) {
return this._internal.variablesModel.get(this._internal.name);
}
return '[No value set]'; internal.variablesModel = Model.get('--ndl--global-variables');
}, },
outputs: { outputs: {
done: { done: {
@@ -78,22 +74,17 @@ const SetVariableNodeDefinition = {
if (this.hasScheduledStore) return; if (this.hasScheduledStore) return;
this.hasScheduledStore = true; this.hasScheduledStore = true;
const internal = this._internal; var internal = this._internal;
this.scheduleAfterInputsHaveUpdated(function () { this.scheduleAfterInputsHaveUpdated(function () {
this.hasScheduledStore = false; this.hasScheduledStore = false;
let value = internal.setWith === 'emptyString' ? '' : internal.value; var value = internal.setWith === 'emptyString' ? '' : internal.value;
// Can set arrays with "id" or array
if (internal.setWith === 'object' && typeof value === 'string') value = Model.get(value);
// Can set arrays with "id" or array
if (internal.setWith === 'array' && typeof value === 'string') value = Collection.get(value);
if (internal.setWith === 'object' && typeof value === 'string') value = Model.get(value); // Can set arrays with "id" or array
if (internal.setWith === 'array' && typeof value === 'string') value = Collection.get(value); // Can set arrays with "id" or array
if (internal.setWith === 'boolean') value = !!value; if (internal.setWith === 'boolean') value = !!value;
// use forceChange to always trigger Variable nodes to send the value on //use forceChange to always trigger Variable nodes to send the value on their output, even if it's the same value twice
// their output, even if it's the same value twice
internal.variablesModel.set(internal.name, value, { internal.variablesModel.set(internal.name, value, {
forceChange: true forceChange: true
}); });
@@ -105,13 +96,12 @@ const SetVariableNodeDefinition = {
return; return;
} }
if (name === 'value') { if (name === 'value')
this.registerInput(name, { this.registerInput(name, {
set: this.setValue.bind(this) set: this.setValue.bind(this)
}); });
} }
} }
}
}; };
module.exports = { module.exports = {

View File

@@ -43,10 +43,6 @@ const OpenFilePicker = {
input.accept = this._internal.acceptedFileTypes; input.accept = this._internal.acceptedFileTypes;
if (this._internal.capture) {
input.capture = this._internal.capture;
}
input.onchange = onChange; input.onchange = onChange;
input.click(); input.click();
} }
@@ -58,14 +54,6 @@ const OpenFilePicker = {
set(value) { set(value) {
this._internal.acceptedFileTypes = value; this._internal.acceptedFileTypes = value;
} }
},
capture: {
group: 'General',
type: 'string',
displayName: 'Capture',
set(value) {
this._internal.capture = value;
}
} }
}, },
outputs: { outputs: {

View File

@@ -4,7 +4,7 @@ const Switch = {
name: 'Switch', name: 'Switch',
docs: 'https://docs.noodl.net/nodes/logic/switch', docs: 'https://docs.noodl.net/nodes/logic/switch',
category: 'Logic', category: 'Logic',
initialize() { initialize: function () {
this._internal.state = false; this._internal.state = false;
this._internal.initialized = false; this._internal.initialized = false;
}, },
@@ -15,7 +15,7 @@ const Switch = {
on: { on: {
displayName: 'On', displayName: 'On',
group: 'Change State', group: 'Change State',
valueChangedToTrue() { valueChangedToTrue: function () {
if (this._internal.state === true) { if (this._internal.state === true) {
return; return;
} }
@@ -27,7 +27,7 @@ const Switch = {
off: { off: {
displayName: 'Off', displayName: 'Off',
group: 'Change State', group: 'Change State',
valueChangedToTrue() { valueChangedToTrue: function () {
if (this._internal.state === false) { if (this._internal.state === false) {
return; return;
} }
@@ -39,7 +39,7 @@ const Switch = {
flip: { flip: {
displayName: 'Flip', displayName: 'Flip',
group: 'Change State', group: 'Change State',
valueChangedToTrue() { valueChangedToTrue: function () {
this._internal.state = !this._internal.state; this._internal.state = !this._internal.state;
this.flagOutputDirty('state'); this.flagOutputDirty('state');
this.emitSignals(); this.emitSignals();
@@ -50,7 +50,7 @@ const Switch = {
displayName: 'State', displayName: 'State',
group: 'General', group: 'General',
default: false, default: false,
set(value) { set: function (value) {
this._internal.state = !!value; this._internal.state = !!value;
this.flagOutputDirty('state'); this.flagOutputDirty('state');
this.emitSignals(); this.emitSignals();
@@ -61,15 +61,10 @@ const Switch = {
state: { state: {
type: 'boolean', type: 'boolean',
displayName: 'Current State', displayName: 'Current State',
getter() { getter: function () {
return this._internal.state; return this._internal.state;
} }
}, },
switched: {
displayName: 'Switched',
type: 'signal',
group: 'Signals'
},
switchedToOn: { switchedToOn: {
displayName: 'Switched To On', displayName: 'Switched To On',
type: 'signal', type: 'signal',
@@ -82,13 +77,12 @@ const Switch = {
} }
}, },
prototypeExtensions: { prototypeExtensions: {
emitSignals() { emitSignals: function () {
if (this._internal.state === true) { if (this._internal.state === true) {
this.sendSignalOnOutput('switchedToOn'); this.sendSignalOnOutput('switchedToOn');
} else { } else {
this.sendSignalOnOutput('switchedToOff'); this.sendSignalOnOutput('switchedToOff');
} }
this.sendSignalOnOutput('switched');
} }
} }
}; };

View File

@@ -1,13 +1,18 @@
'use strict'; 'use strict';
const { Node, EdgeTriggeredInput } = require('@noodl/runtime');
const UserService = require('./userservice'); const UserService = require('./userservice');
const LoginNodeDefinition = { var LoginNodeDefinition = {
name: 'net.noodl.user.LogIn', name: 'net.noodl.user.LogIn',
docs: 'https://docs.noodl.net/nodes/data/user/log-in', docs: 'https://docs.noodl.net/nodes/data/user/log-in',
displayNodeName: 'Log In', displayNodeName: 'Log In',
category: 'Cloud Services', category: 'Cloud Services',
color: 'data', color: 'data',
initialize: function () {
var internal = this._internal;
},
getInspectInfo() {},
outputs: { outputs: {
success: { success: {
type: 'signal', type: 'signal',
@@ -23,7 +28,7 @@ const LoginNodeDefinition = {
type: 'string', type: 'string',
displayName: 'Error', displayName: 'Error',
group: 'Error', group: 'Error',
getter() { getter: function () {
return this._internal.error; return this._internal.error;
} }
} }
@@ -32,7 +37,7 @@ const LoginNodeDefinition = {
login: { login: {
displayName: 'Do', displayName: 'Do',
group: 'Actions', group: 'Actions',
valueChangedToTrue() { valueChangedToTrue: function () {
this.scheduleLogIn(); this.scheduleLogIn();
} }
}, },
@@ -40,7 +45,7 @@ const LoginNodeDefinition = {
displayName: 'Username', displayName: 'Username',
type: 'string', type: 'string',
group: 'General', group: 'General',
set(value) { set: function (value) {
this._internal.username = value; this._internal.username = value;
} }
}, },
@@ -48,13 +53,13 @@ const LoginNodeDefinition = {
displayName: 'Password', displayName: 'Password',
type: 'string', type: 'string',
group: 'General', group: 'General',
set(value) { set: function (value) {
this._internal.password = value; this._internal.password = value;
} }
} }
}, },
methods: { methods: {
setError(err) { setError: function (err) {
this._internal.error = err; this._internal.error = err;
this.flagOutputDirty('error'); this.flagOutputDirty('error');
this.sendSignalOnOutput('failure'); this.sendSignalOnOutput('failure');
@@ -71,7 +76,9 @@ const LoginNodeDefinition = {
this.context.editorConnection.clearWarning(this.nodeScope.componentOwner.name, this.id, 'user-login-warning'); this.context.editorConnection.clearWarning(this.nodeScope.componentOwner.name, this.id, 'user-login-warning');
} }
}, },
scheduleLogIn() { scheduleLogIn: function () {
const internal = this._internal;
if (this.logInScheduled === true) return; if (this.logInScheduled === true) return;
this.logInScheduled = true; this.logInScheduled = true;
@@ -93,7 +100,89 @@ const LoginNodeDefinition = {
} }
}; };
/*function updatePorts(nodeId, parameters, editorConnection, dbCollections) {
var ports = [];
ports.push({
name: 'collectionName',
displayName: "Class",
group: "General",
type: { name: 'enum', enums: (dbCollections !== undefined) ? dbCollections.map((c) => { return { value: c.name, label: c.name } }) : [], allowEditOnly: true },
plug: 'input'
})
if (parameters.collectionName && dbCollections) {
// Fetch ports from collection keys
var c = dbCollections.find((c) => c.name === parameters.collectionName);
if (c && c.schema && c.schema.properties) {
var props = c.schema.properties;
for (var key in props) {
var p = props[key];
if (ports.find((_p) => _p.name === key)) continue;
if(p.type === 'Relation') {
}
else { // Other schema type ports
const _typeMap = {
"String":"string",
"Boolean":"boolean",
"Number":"number",
"Date":"date"
}
ports.push({
type: {
name: _typeMap[p.type]?_typeMap[p.type]:'*',
},
plug: 'output',
group: 'Properties',
name: 'prop-' + key,
displayName: key,
})
ports.push({
type: 'signal',
plug: 'output',
group: 'Changed Events',
displayName: key+ ' Changed',
name: 'changed-' + key,
})
}
}
}
}
editorConnection.sendDynamicPorts(nodeId, ports);
}*/
module.exports = { module.exports = {
node: LoginNodeDefinition, node: LoginNodeDefinition,
setup(_context, _graphModel) {} setup: function (context, graphModel) {
/* if (!context.editorConnection || !context.editorConnection.isRunningLocally()) {
return;
}
function _managePortsForNode(node) {
updatePorts(node.id, node.parameters, context.editorConnection, graphModel.getMetaData('dbCollections'));
node.on("parameterUpdated", function (event) {
updatePorts(node.id, node.parameters, context.editorConnection, graphModel.getMetaData('dbCollections'));
});
graphModel.on('metadataChanged.dbCollections', function (data) {
updatePorts(node.id, node.parameters, context.editorConnection, data);
})
}
graphModel.on("editorImportComplete", ()=> {
graphModel.on("nodeAdded.DbModel2", function (node) {
_managePortsForNode(node)
})
for(const node of graphModel.getNodesWithType('DbModel2')) {
_managePortsForNode(node)
}
})*/
}
}; };

View File

@@ -1,13 +1,18 @@
'use strict'; 'use strict';
const { Node, EdgeTriggeredInput } = require('@noodl/runtime');
const UserService = require('./userservice'); const UserService = require('./userservice');
const LogOutNodeDefinition = { var LogOutNodeDefinition = {
name: 'net.noodl.user.LogOut', name: 'net.noodl.user.LogOut',
docs: 'https://docs.noodl.net/nodes/data/user/log-out', docs: 'https://docs.noodl.net/nodes/data/user/log-out',
displayNodeName: 'Log Out', displayNodeName: 'Log Out',
category: 'Cloud Services', category: 'Cloud Services',
color: 'data', color: 'data',
initialize: function () {
var internal = this._internal;
},
getInspectInfo() {},
outputs: { outputs: {
success: { success: {
type: 'signal', type: 'signal',
@@ -23,7 +28,7 @@ const LogOutNodeDefinition = {
type: 'string', type: 'string',
displayName: 'Error', displayName: 'Error',
group: 'Error', group: 'Error',
getter() { getter: function () {
return this._internal.error; return this._internal.error;
} }
} }
@@ -32,13 +37,13 @@ const LogOutNodeDefinition = {
login: { login: {
displayName: 'Do', displayName: 'Do',
group: 'Actions', group: 'Actions',
valueChangedToTrue() { valueChangedToTrue: function () {
this.scheduleLogOut(); this.scheduleLogOut();
} }
} }
}, },
methods: { methods: {
setError(err) { setError: function (err) {
this._internal.error = err; this._internal.error = err;
this.flagOutputDirty('error'); this.flagOutputDirty('error');
this.sendSignalOnOutput('failure'); this.sendSignalOnOutput('failure');
@@ -55,7 +60,9 @@ const LogOutNodeDefinition = {
this.context.editorConnection.clearWarning(this.nodeScope.componentOwner.name, this.id, 'user-login-warning'); this.context.editorConnection.clearWarning(this.nodeScope.componentOwner.name, this.id, 'user-login-warning');
} }
}, },
scheduleLogOut() { scheduleLogOut: function () {
const internal = this._internal;
if (this.logOutScheduled === true) return; if (this.logOutScheduled === true) return;
this.logOutScheduled = true; this.logOutScheduled = true;
@@ -77,5 +84,5 @@ const LogOutNodeDefinition = {
module.exports = { module.exports = {
node: LogOutNodeDefinition, node: LogOutNodeDefinition,
setup(_context, _graphModel) {} setup: function (context, graphModel) {}
}; };

View File

@@ -1,17 +1,20 @@
'use strict'; 'use strict';
const { Node, EdgeTriggeredInput } = require('@noodl/runtime');
const UserService = require('./userservice'); const UserService = require('./userservice');
const SignUpNodeDefinition = { var SignUpNodeDefinition = {
name: 'net.noodl.user.SignUp', name: 'net.noodl.user.SignUp',
docs: 'https://docs.noodl.net/nodes/data/user/sign-up', docs: 'https://docs.noodl.net/nodes/data/user/sign-up',
displayNodeName: 'Sign Up', displayNodeName: 'Sign Up',
category: 'Cloud Services', category: 'Cloud Services',
color: 'data', color: 'data',
initialize() { initialize: function () {
const internal = this._internal; var internal = this._internal;
internal.userProperties = {}; internal.userProperties = {};
}, },
getInspectInfo() {},
outputs: { outputs: {
success: { success: {
type: 'signal', type: 'signal',
@@ -27,7 +30,7 @@ const SignUpNodeDefinition = {
type: 'string', type: 'string',
displayName: 'Error', displayName: 'Error',
group: 'Error', group: 'Error',
getter() { getter: function () {
return this._internal.error; return this._internal.error;
} }
} }
@@ -36,7 +39,7 @@ const SignUpNodeDefinition = {
signup: { signup: {
displayName: 'Do', displayName: 'Do',
group: 'Actions', group: 'Actions',
valueChangedToTrue() { valueChangedToTrue: function () {
this.scheduleSignUp(); this.scheduleSignUp();
} }
}, },
@@ -44,7 +47,7 @@ const SignUpNodeDefinition = {
displayName: 'Username', displayName: 'Username',
type: 'string', type: 'string',
group: 'General', group: 'General',
set(value) { set: function (value) {
this._internal.username = value; this._internal.username = value;
} }
}, },
@@ -52,7 +55,7 @@ const SignUpNodeDefinition = {
displayName: 'Password', displayName: 'Password',
type: 'string', type: 'string',
group: 'General', group: 'General',
set(value) { set: function (value) {
this._internal.password = value; this._internal.password = value;
} }
}, },
@@ -60,13 +63,13 @@ const SignUpNodeDefinition = {
displayName: 'Email', displayName: 'Email',
type: 'string', type: 'string',
group: 'General', group: 'General',
set(value) { set: function (value) {
this._internal.email = value; this._internal.email = value;
} }
} }
}, },
methods: { methods: {
setError(err) { setError: function (err) {
this._internal.error = err; this._internal.error = err;
this.flagOutputDirty('error'); this.flagOutputDirty('error');
this.sendSignalOnOutput('failure'); this.sendSignalOnOutput('failure');
@@ -83,7 +86,7 @@ const SignUpNodeDefinition = {
this.context.editorConnection.clearWarning(this.nodeScope.componentOwner.name, this.id, 'user-login-warning'); this.context.editorConnection.clearWarning(this.nodeScope.componentOwner.name, this.id, 'user-login-warning');
} }
}, },
scheduleSignUp() { scheduleSignUp: function () {
const internal = this._internal; const internal = this._internal;
if (this.signUpScheduled === true) return; if (this.signUpScheduled === true) return;
@@ -106,24 +109,23 @@ const SignUpNodeDefinition = {
}); });
}); });
}, },
setUserProperty(name, value) { setUserProperty: function (name, value) {
this._internal.userProperties[name] = value; this._internal.userProperties[name] = value;
}, },
registerInputIfNeeded(name) { registerInputIfNeeded: function (name) {
if (this.hasInput(name)) { if (this.hasInput(name)) {
return; return;
} }
if (name.startsWith('prop-')) { if (name.startsWith('prop-'))
return this.registerInput(name, { return this.registerInput(name, {
set: this.setUserProperty.bind(this, name.substring('prop-'.length)) set: this.setUserProperty.bind(this, name.substring('prop-'.length))
}); });
} }
} }
}
}; };
function updatePorts(nodeId, _parameters, editorConnection, systemCollections) { function updatePorts(nodeId, parameters, editorConnection, systemCollections) {
var ports = []; var ports = [];
if (systemCollections) { if (systemCollections) {
@@ -167,7 +169,7 @@ function updatePorts(nodeId, _parameters, editorConnection, systemCollections) {
module.exports = { module.exports = {
node: SignUpNodeDefinition, node: SignUpNodeDefinition,
setup(context, graphModel) { setup: function (context, graphModel) {
if (!context.editorConnection || !context.editorConnection.isRunningLocally()) { if (!context.editorConnection || !context.editorConnection.isRunningLocally()) {
return; return;
} }
@@ -175,7 +177,7 @@ module.exports = {
function _managePortsForNode(node) { function _managePortsForNode(node) {
updatePorts(node.id, node.parameters, context.editorConnection, graphModel.getMetaData('systemCollections')); updatePorts(node.id, node.parameters, context.editorConnection, graphModel.getMetaData('systemCollections'));
node.on('parameterUpdated', function (_event) { node.on('parameterUpdated', function (event) {
updatePorts(node.id, node.parameters, context.editorConnection, graphModel.getMetaData('systemCollections')); updatePorts(node.id, node.parameters, context.editorConnection, graphModel.getMetaData('systemCollections'));
}); });

View File

@@ -23,7 +23,7 @@ const ColumnsNode = {
] ]
}, },
initialize() { this.props.attrs = {}; initialize() {
this.props.layoutString = '1 2 1'; this.props.layoutString = '1 2 1';
this.props.minWidth = 0; this.props.minWidth = 0;
this.props.marginX = 16; this.props.marginX = 16;
@@ -62,16 +62,6 @@ const ColumnsNode = {
); );
} }
this.forceUpdate();
}
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate(); this.forceUpdate();
} }
} }

View File

@@ -11,7 +11,6 @@ const GroupNode = {
groupPriority: ['General', 'Style', 'Events', 'Mounted', 'Hover Events', 'Pointer Events', 'Focus', 'Scroll'] groupPriority: ['General', 'Style', 'Events', 'Mounted', 'Hover Events', 'Pointer Events', 'Focus', 'Scroll']
}, },
initialize() { initialize() {
this.props.attrs = {};
this._internal = { this._internal = {
scrollElementDuration: 500, scrollElementDuration: 500,
scrollIndexDuration: 500, scrollIndexDuration: 500,
@@ -144,16 +143,6 @@ const GroupNode = {
valueChangedToTrue() { valueChangedToTrue() {
this.context.setNodeFocused(this, true); this.context.setNodeFocused(this, true);
} }
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
} }
}, },
inputProps: { inputProps: {

View File

@@ -27,7 +27,6 @@ const ImageNode = {
] ]
}, },
initialize() { initialize() {
this.props.attrs = {};
this.props.default = ''; this.props.default = '';
}, },
getReactComponent() { getReactComponent() {
@@ -87,16 +86,6 @@ const ImageNode = {
this.props.dom.src = getAbsoluteUrl(url); this.props.dom.src = getAbsoluteUrl(url);
this.forceUpdate(); this.forceUpdate();
} }
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
} }
}, },
inputProps: { inputProps: {
@@ -125,12 +114,6 @@ const ImageNode = {
propPath: 'dom', propPath: 'dom',
type: 'signal', type: 'signal',
group: 'Events' group: 'Events'
},
onError: {
displayName: 'On Error',
propPath: 'dom',
type: 'signal',
group: 'Events'
} }
} }
}; };

View File

@@ -20,9 +20,6 @@ const TextNode = {
nodeDoubleClickAction: { nodeDoubleClickAction: {
focusPort: 'text' focusPort: 'text'
}, },
initialize() {
this.props.attrs = {};
},
getReactComponent() { getReactComponent() {
return Text; return Text;
}, },
@@ -138,16 +135,6 @@ const TextNode = {
break; break;
} }
} }
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
} }
} }
}; };

View File

@@ -4,7 +4,6 @@
{"url":"noodl-app.png"}, {"url":"noodl-app.png"},
{"url":"load_terminator.js"}, {"url":"load_terminator.js"},
{"url":"noodl.deploy.js"}, {"url":"noodl.deploy.js"},
{"url":"noodl.deploy.js.map"},
{"url":"react.production.min.js"}, {"url":"react.production.min.js"},
{"url":"react-dom.production.min.js"} {"url":"react-dom.production.min.js"}
] ]

View File

@@ -274,9 +274,6 @@ declare namespace Noodl {
objectOrId: string | { getId(): string; }, objectOrId: string | { getId(): string; },
options?: { options?: {
className?: RecordClassName; className?: RecordClassName;
keys?: string[] | string;
include?: string[] | string;
excludeKeys?: string[] | string;
} }
): Promise<any>; ): Promise<any>;
@@ -485,12 +482,7 @@ declare namespace Noodl {
const Records: RecordsApi; const Records: RecordsApi;
interface CurrentUserObject { interface CurrentUserObject {
id: string; UserId: string;
email: string;
emailVerified: boolean;
username: string;
Properties: unknown;
/** /**
* Log out the current user and terminate the session. * Log out the current user and terminate the session.

View File

@@ -1,8 +1,10 @@
const path = require('path'); const path = require("path");
module.exports = { module.exports = {
// Allows to define the output path of the files built by the viewer. // Allows to define the output path of the files built by the viewer.
// //
// For example in the CLI, we will also build this, just with a different output path. // For example in the CLI, we will also build this, just with a different output path.
outPath: process.env.OUT_PATH || path.resolve(__dirname, '../../noodl-editor/src/external') outPath:
process.env.OUT_PATH ||
path.resolve(__dirname, "../../noodl-editor/src/external"),
}; };

Some files were not shown because too many files have changed in this diff Show More