Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a55a9d811f | ||
|
|
d639fde539 | ||
|
|
b4b68df0ae | ||
|
|
981b6406c0 | ||
|
|
5e199cfa0b | ||
|
|
6eb6d385a4 | ||
|
|
39d402049c | ||
|
|
9cf78dc536 | ||
|
|
fb93911abd | ||
|
|
49e665774b | ||
|
|
25eda48a3e | ||
|
|
83121a9276 | ||
|
|
28216a08d0 | ||
|
|
c684505ebf | ||
|
|
07a15f62bc | ||
|
|
33d2011a27 | ||
|
|
3548df523b | ||
|
|
e7d8be2a22 | ||
|
|
ce833e16c2 | ||
|
|
bb43bedd1b | ||
|
|
78c5a4d7bf | ||
|
|
0029c4c6d2 | ||
|
|
243948cf49 | ||
|
|
f5b7cda053 | ||
|
|
c41fc95f00 | ||
|
|
e83805b300 | ||
|
|
72672dfcdf | ||
|
|
01ebdeece7 | ||
|
|
3804b350b0 | ||
|
|
a90c8256ac | ||
|
|
cbb1de0747 | ||
|
|
31c13515f0 | ||
|
|
e26c21ec6a | ||
|
|
4f4539934c | ||
|
|
65a141da5c | ||
|
|
22317a454d | ||
|
|
e620174fa1 | ||
|
|
49d33649df | ||
|
|
9fbf37481b | ||
|
|
0cea547c4f | ||
|
|
6e251f7173 | ||
|
|
ef91cf73a8 | ||
|
|
fad5d9842f | ||
|
|
9f26001bc8 | ||
|
|
bcb9781035 | ||
|
|
70189e5d59 | ||
|
|
e40d3d1b04 | ||
|
|
a5ab8472a9 | ||
|
|
f2bdfb14c9 | ||
|
|
b4e0bfae0a | ||
|
|
2c13f6dcb3 | ||
|
|
cd8b0e7093 | ||
|
|
250ce9b4c4 | ||
|
|
334233b8b4 | ||
|
|
b2d7d9d14d | ||
|
|
5603603668 | ||
|
|
74c946175b | ||
|
|
915e66bd59 | ||
|
|
9b741d7ec0 | ||
|
|
561379dac2 | ||
|
|
3074dfd9ae | ||
|
|
1bfd5c9ff9 | ||
|
|
22a45b74e4 | ||
|
|
0ea255a2a3 | ||
|
|
2d67fb19aa | ||
|
|
229d704bb1 | ||
|
|
3a77f3c766 | ||
|
|
7dee9083bf | ||
|
|
0c27d7d9bd | ||
|
|
0ea1c5b379 | ||
|
|
f4c4fc430b | ||
|
|
38706a4615 | ||
|
|
c6f34e97e1 | ||
|
|
babc2e0a42 | ||
|
|
b83801cd1d | ||
|
|
ab4c934b26 | ||
|
|
6b74a02b30 | ||
|
|
cdb1f790c6 | ||
|
|
b4bf7ee4d1 | ||
|
|
d7a9ee6fd1 | ||
|
|
5d97e97a02 | ||
|
|
46ab81d988 | ||
|
|
986a709b06 | ||
|
|
eaab261be9 | ||
|
|
b5920e7d76 | ||
|
|
9cebcace5a | ||
|
|
5497d301ef | ||
|
|
b47f852c7f | ||
|
|
05681171df | ||
|
|
1cb463b85f | ||
|
|
8ba6ef7870 | ||
|
|
80ddb1fd5f | ||
|
|
f9010b3041 | ||
|
|
5382f56c89 | ||
|
|
ab80400e05 | ||
|
|
2807a9a019 | ||
|
|
f212f570b5 | ||
|
|
6d84915363 | ||
|
|
725c1bf71e | ||
|
|
e4f3dfc970 | ||
|
|
b5253a6bc0 | ||
|
|
edc2939d62 | ||
|
|
6c66ad4264 | ||
|
|
be12ca902a | ||
|
|
532336c95f | ||
|
|
4760951963 | ||
|
|
49170e8ba4 | ||
|
|
51cb76965a | ||
|
|
5d8d8fda64 | ||
|
|
901155057d | ||
|
|
e7cc80f9c6 | ||
|
|
19214d2c92 | ||
|
|
b3f8dc4f39 | ||
|
|
c27e0498e3 | ||
|
|
f90b9a192c | ||
|
|
4783c97432 | ||
|
|
c47bb6dc12 | ||
|
|
5599962713 | ||
|
|
82d82a7487 | ||
|
|
4628e4d51d | ||
|
|
b19a3e8a2a | ||
|
|
1ca7c2f734 | ||
|
|
1a17308ebc | ||
|
|
bb3ee67256 | ||
|
|
e2c19a5c3f | ||
|
|
6f3a254a52 | ||
|
|
58d0f59832 | ||
|
|
13947616f2 | ||
|
|
933b58e2e5 | ||
|
|
e882e4aa17 | ||
|
|
48ead9b9fc | ||
|
|
9f7ee32765 | ||
|
|
d34d8125c5 | ||
|
|
64f3832257 | ||
|
|
71cf4c580f | ||
|
|
e4f46da8f9 | ||
|
|
acad839530 | ||
|
|
46f06c8da1 | ||
|
|
9205cd7410 | ||
|
|
770383635f | ||
|
|
6c6ab09596 | ||
|
|
5e5eb236f1 | ||
|
|
3f4609d2a2 | ||
|
|
fb119ec3a3 | ||
|
|
2589171a3e | ||
|
|
1b65ead4be | ||
|
|
eebe11c094 | ||
|
|
88164a3e93 | ||
|
|
5292bbecf6 | ||
|
|
e9e780a3ce | ||
|
|
b796fd2581 | ||
|
|
e3415ca597 | ||
|
|
a02d7a2553 | ||
|
|
0f1b6b083b | ||
|
|
c8e1090610 | ||
|
|
ddb5cec68b | ||
|
|
bd3536ffb9 | ||
|
|
d41c1ee841 | ||
|
|
ce1d11b55a | ||
|
|
6f38d7cb1c | ||
|
|
1d364a3cc1 | ||
|
|
f3eae752e7 | ||
|
|
f44fdf84f8 | ||
|
|
044b0cd9c4 | ||
|
|
052d173ca1 | ||
|
|
c412be02c8 | ||
|
|
daf931a5c1 | ||
|
|
f48db81990 | ||
|
|
968dc9d8d7 | ||
|
|
1ce0be81c7 | ||
|
|
c325df7d21 | ||
|
|
77ec0a7fa6 | ||
|
|
d991d3c6d8 | ||
|
|
330a4419da | ||
|
|
eec316ef37 | ||
|
|
6c83ccf15f | ||
|
|
ab57e73c85 | ||
|
|
939365f7f4 | ||
|
|
b32831853a | ||
|
|
2d8b1a472c | ||
|
|
34bd7d3456 | ||
|
|
bae4a48ed2 | ||
|
|
fe4e66a405 | ||
|
|
5173efe21d | ||
|
|
e94dc8f0bc | ||
|
|
edcfba0948 | ||
|
|
dbc0bda0d2 | ||
|
|
69a6d0c684 | ||
|
|
38f7164dca | ||
|
|
b25d7ed939 | ||
|
|
8cc4873063 | ||
|
|
9c416977cc | ||
|
|
df26b33c79 | ||
|
|
ccf61b13f5 | ||
|
|
7e29e89eec | ||
|
|
5163f1cee0 | ||
|
|
7427253fb3 | ||
|
|
7f4ee3df34 | ||
|
|
b56e857c8f | ||
|
|
a3f6bc8bc4 | ||
|
|
f1833b6ba5 | ||
|
|
89d5e93f0e | ||
|
|
a7af71f158 | ||
|
|
a64801809a | ||
|
|
0ef5b9635e | ||
|
|
7cf8719d3f | ||
|
|
285387f39a | ||
|
|
adc9f2d49d | ||
|
|
cc9d943769 | ||
|
|
c3aaf097fc | ||
|
|
69f78d5f76 | ||
|
|
a69c8cb53c | ||
|
|
b96b5476cd | ||
|
|
708024605b | ||
|
|
b495a0142a | ||
|
|
4c85b86231 | ||
|
|
e46c37c7a2 | ||
|
|
35e1d8c2eb | ||
|
|
2832cd657b | ||
|
|
00bdb0bf55 | ||
|
|
8b4b2ecb18 | ||
|
|
d2c15d88d7 | ||
|
|
04343af119 | ||
|
|
e771458a32 | ||
|
|
733cd009aa | ||
|
|
91ce33f075 | ||
|
|
0f451562a3 | ||
|
|
e99bae6b62 | ||
|
|
cfbf99befa | ||
|
|
c600f77b84 | ||
|
|
cbe390f0ba | ||
|
|
b689983799 | ||
|
|
8cf817342d | ||
|
|
0e5ab4fc9c | ||
|
|
3216188a7e | ||
|
|
f2369f10e9 | ||
|
|
03283c3746 | ||
|
|
af860e0084 | ||
|
|
2e6e2815d6 | ||
|
|
9ba5cb776c | ||
|
|
cb592731c5 | ||
|
|
82f1f64d65 | ||
|
|
a7adaa8abd | ||
|
|
ce49da77ac | ||
|
|
0e8bc6b0e1 | ||
|
|
2f95b6e7c8 | ||
|
|
762517f448 | ||
|
|
201dd62c81 | ||
|
|
f6ea959c01 | ||
|
|
1d71efeccc | ||
|
|
93310265a2 | ||
|
|
3803169987 | ||
|
|
173a4a0aa0 | ||
|
|
4a21ea2b1a | ||
|
|
002c7002e7 | ||
|
|
c9382e0c15 | ||
|
|
68318ba410 | ||
|
|
9def104a84 | ||
|
|
c52629cda2 | ||
|
|
95b763d9ed | ||
|
|
7ed3876d19 | ||
|
|
2935e989f7 | ||
|
|
5f182b7fb6 | ||
|
|
fdc7198274 | ||
|
|
d779b98eda | ||
|
|
74a4c7da77 | ||
|
|
c24227daab | ||
|
|
5695d038ce | ||
|
|
87fcc7793b | ||
|
|
9597fef6d4 | ||
|
|
04fc3f89f4 | ||
|
|
c276d6d9b8 | ||
|
|
58663b6968 | ||
|
|
0bea7d4ff4 | ||
|
|
e12959b97d | ||
|
|
b966f2e6a5 | ||
|
|
772c4fe326 | ||
|
|
e2e0784a4a | ||
|
|
b577dcc3d5 | ||
|
|
c741f8b2c4 | ||
|
|
09cb005c7a | ||
|
|
c4aa0fd4b1 | ||
|
|
50b1a9d422 | ||
|
|
4205a510ca | ||
|
|
d8dd7f6f17 | ||
|
|
333220bc4a | ||
|
|
4b3654d4a4 | ||
|
|
2da3d2955e | ||
|
|
3bde6ee880 | ||
|
|
1bbe09a7d0 | ||
|
|
577fb7cfab | ||
|
|
d4df6f7b78 | ||
|
|
cf121c24b6 | ||
|
|
690f4a7a76 | ||
|
|
56ed632842 | ||
|
|
314386131f | ||
|
|
c2a6938339 | ||
|
|
4433eadae3 | ||
|
|
11d9ce5a53 | ||
|
|
90813f382f | ||
|
|
44e9bb95c5 | ||
|
|
4eb633389d | ||
|
|
0b17ec8bd9 | ||
|
|
14d4ba3c6c | ||
|
|
15997b3136 | ||
|
|
55abcc302e | ||
|
|
326186e7e8 | ||
|
|
8c89eabc2c | ||
|
|
6bd88aeaae | ||
|
|
fae6f15d19 | ||
|
|
7590fd7418 | ||
|
|
69d8b89783 | ||
|
|
5309042a27 |
No files matched your search
@@ -20,3 +20,4 @@ launch.json
|
||||
package-lock.json
|
||||
.idea
|
||||
dist
|
||||
/nbproject/private/
|
||||
@@ -2,7 +2,7 @@ exports.newDesktopApp = function newDesktopApp() {
|
||||
|
||||
let thisObject = {
|
||||
userProfiles: undefined,
|
||||
p2pNetworkClient: undefined,
|
||||
p2pNetworkClientIdentity: undefined,
|
||||
p2pNetwork: undefined,
|
||||
p2pNetworkPeers: undefined,
|
||||
webSocketsInterface: undefined,
|
||||
@@ -25,13 +25,13 @@ exports.newDesktopApp = function newDesktopApp() {
|
||||
/*
|
||||
We set up ourselves as a Network Client.
|
||||
*/
|
||||
thisObject.p2pNetworkClient = SA.projects.network.modules.p2pNetworkClient.newNetworkModulesP2PNetworkClient()
|
||||
await thisObject.p2pNetworkClient.initialize()
|
||||
thisObject.p2pNetworkClientIdentity = SA.projects.network.modules.p2pNetworkClientIdentity.newNetworkModulesP2PNetworkClientIdentity()
|
||||
await thisObject.p2pNetworkClientIdentity.initialize()
|
||||
/*
|
||||
We will read all user profiles plugins and get from there our network identity.
|
||||
*/
|
||||
thisObject.userProfiles = SA.projects.network.modules.userProfiles.newNetworkModulesUserProfiles()
|
||||
await thisObject.userProfiles.initialize(global.env.DESKTOP_APP_SIGNING_ACCOUNT, thisObject.p2pNetworkClient)
|
||||
await thisObject.userProfiles.initialize(global.env.DESKTOP_APP_SIGNING_ACCOUNT, thisObject.p2pNetworkClientIdentity)
|
||||
/*
|
||||
We set up the P2P Network.
|
||||
*/
|
||||
@@ -43,7 +43,7 @@ exports.newDesktopApp = function newDesktopApp() {
|
||||
thisObject.p2pNetworkPeers = SA.projects.network.modules.p2pNetworkPeers.newNetworkModulesP2PNetworkPeers()
|
||||
await thisObject.p2pNetworkPeers.initialize(
|
||||
'Network Client',
|
||||
thisObject.p2pNetworkClient,
|
||||
thisObject.p2pNetworkClientIdentity,
|
||||
thisObject.p2pNetwork,
|
||||
global.env.DESKTOP_APP_MAX_OUTGOING_PEERS
|
||||
)
|
||||
@@ -68,11 +68,11 @@ exports.newDesktopApp = function newDesktopApp() {
|
||||
*/
|
||||
thisObject.webSocketsInterface = DK.projects.socialTrading.modules.webSocketsInterface.newDesktopModulesWebSocketsInterface()
|
||||
thisObject.webSocketsInterface.initialize()
|
||||
console.log('Desktop Client Web Sockets Interface ......................................... Listening at port ' + JSON.parse(DK.desktopApp.p2pNetworkClient.node.config).webSocketsPort)
|
||||
console.log('Desktop Client Web Sockets Interface ......................................... Listening at port ' + JSON.parse(DK.desktopApp.p2pNetworkClientIdentity.node.config).webSocketsPort)
|
||||
|
||||
thisObject.httpInterface = DK.projects.socialTrading.modules.httpInterface.newDesktopModulesHttpInterface()
|
||||
thisObject.httpInterface.initialize()
|
||||
console.log('Desktop Client Http Interface ................................................ Listening at port ' + JSON.parse(DK.desktopApp.p2pNetworkClient.node.config).webPort)
|
||||
console.log('Desktop Client Http Interface ................................................ Listening at port ' + JSON.parse(DK.desktopApp.p2pNetworkClientIdentity.node.config).webPort)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
const express = require("./backend/src/index");
|
||||
exports.newDesktopApp = function newDesktopApp() {
|
||||
|
||||
let thisObject = {
|
||||
userProfiles: undefined,
|
||||
p2pNetworkClientIdentity: undefined,
|
||||
p2pNetwork: undefined,
|
||||
p2pNetworkPeers: undefined,
|
||||
webSocketsInterface: undefined,
|
||||
webAppInterface: undefined,
|
||||
p2pNetworkInterface: undefined,
|
||||
socialGraph: undefined,
|
||||
run: run
|
||||
}
|
||||
|
||||
DK.desktopApp = thisObject
|
||||
|
||||
return thisObject
|
||||
|
||||
async function run() {
|
||||
|
||||
await setupNetwork()
|
||||
await setupServices()
|
||||
|
||||
async function setupNetwork() {
|
||||
/*
|
||||
We set up ourselves as a Network Client.
|
||||
*/
|
||||
thisObject.p2pNetworkClientIdentity = SA.projects.network.modules.p2pNetworkClientIdentity.newNetworkModulesP2PNetworkClientIdentity()
|
||||
await thisObject.p2pNetworkClientIdentity.initialize()
|
||||
/*
|
||||
We will read all user profiles plugins and get from there our network identity.
|
||||
*/
|
||||
thisObject.userProfiles = SA.projects.network.modules.userProfiles.newNetworkModulesUserProfiles()
|
||||
await thisObject.userProfiles.initialize(global.env.DESKTOP_APP_SIGNING_ACCOUNT, thisObject.p2pNetworkClientIdentity)
|
||||
/*
|
||||
We set up the P2P Network.
|
||||
*/
|
||||
thisObject.p2pNetwork = SA.projects.network.modules.p2pNetwork.newNetworkModulesP2PNetwork()
|
||||
await thisObject.p2pNetwork.initialize('Network Client')
|
||||
/*
|
||||
Set up the connections to network nodes.
|
||||
*/
|
||||
thisObject.p2pNetworkPeers = SA.projects.network.modules.p2pNetworkPeers.newNetworkModulesP2PNetworkPeers()
|
||||
await thisObject.p2pNetworkPeers.initialize(
|
||||
'Network Client',
|
||||
thisObject.p2pNetworkClientIdentity,
|
||||
thisObject.p2pNetwork,
|
||||
global.env.DESKTOP_APP_MAX_OUTGOING_PEERS
|
||||
)
|
||||
}
|
||||
|
||||
async function setupServices() {
|
||||
/*
|
||||
This is where we will process all the messages comming from our web app.
|
||||
*/
|
||||
thisObject.webAppInterface = DK.projects.socialTrading.modules.webAppInterface.newSocialTradingModulesWebAppInterface() // this sends events to the p2p network
|
||||
/*
|
||||
This is where we will process all the events comming from the p2p network.
|
||||
*/
|
||||
thisObject.p2pNetworkInterface = DK.projects.socialTrading.modules.p2pNetworkInterface.newSocialTradingModulesP2PNetworkInterface() // this receives events from p2p network
|
||||
/*
|
||||
This is the Personal Social Graph for the user running this App.
|
||||
*/
|
||||
thisObject.socialGraph = DK.projects.socialTrading.modules.socialGraph.newSocialTradingModulesSocialGraph() //
|
||||
await thisObject.socialGraph.initialize()
|
||||
|
||||
|
||||
let express = require('./backend/src/index')
|
||||
let expressPort = JSON.parse(DK.desktopApp.p2pNetworkClientIdentity.node.config).webPort;
|
||||
express.startExpress(expressPort,SA);
|
||||
console.log('express Interface ................................................ Listening at port ' + expressPort);
|
||||
|
||||
/*TODO change this to have a definite port number*/
|
||||
let react = require('./frontend/scripts/start')
|
||||
let reactPort = (+JSON.parse(DK.desktopApp.p2pNetworkClientIdentity.node.config).webPort + 1);
|
||||
react.start(reactPort);
|
||||
console.log('react Interface ................................................ Listening at port ' + reactPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
function newWebApp() {
|
||||
/*
|
||||
In it's current state of development, the Web App only has one module.
|
||||
|
||||
Everything is being coded here until some structure emerges.
|
||||
*/
|
||||
let thisObject = {
|
||||
messageReceived: messageReceived,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize() {
|
||||
|
||||
}
|
||||
|
||||
async function initialize() {
|
||||
try {
|
||||
setupRootObject(UI, 'UI')
|
||||
setupRootObject(SA, 'SA')
|
||||
await UI.projects.socialTrading.modules.webSocketsWebAppClient.initialize()
|
||||
loadWUserProfileTimeline()
|
||||
loadWhoToFollow()
|
||||
setupEventHandlers()
|
||||
} catch (err) {
|
||||
console.log('[ERROR] initialize -> err.stack = ' + err.stack)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
function setupRootObject(rootObject, rootObjectName) {
|
||||
/!*
|
||||
Here we will setup the UI object, with all the
|
||||
projects and spaces.
|
||||
*!/
|
||||
for (let i = 0; i < UI.schemas.projectSchema.length; i++) {
|
||||
let projectDefinition = UI.schemas.projectSchema[i]
|
||||
rootObject.projects[projectDefinition.propertyName] = {}
|
||||
let projectInstance = rootObject.projects[projectDefinition.propertyName]
|
||||
|
||||
projectInstance.utilities = {}
|
||||
projectInstance.globals = {}
|
||||
projectInstance.functionLibraries = {}
|
||||
projectInstance.modules = {}
|
||||
|
||||
if (projectDefinition[rootObjectName] === undefined) {
|
||||
continue
|
||||
}
|
||||
|
||||
/!* Set up Globals of this Project *!/
|
||||
if (projectDefinition[rootObjectName].globals !== undefined) {
|
||||
for (let j = 0; j < projectDefinition[rootObjectName].globals.length; j++) {
|
||||
let globalDefinition = projectDefinition[rootObjectName].globals[j]
|
||||
|
||||
if (exports[globalDefinition.functionName] === undefined) {
|
||||
projectInstance.globals[globalDefinition.propertyName] = eval(globalDefinition.functionName + '()')
|
||||
} else {
|
||||
projectInstance.globals[globalDefinition.propertyName] = eval('exports.' + globalDefinition.functionName + '()')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/!* Set up Utilities of this Project *!/
|
||||
if (projectDefinition[rootObjectName].utilities !== undefined) {
|
||||
for (let j = 0; j < projectDefinition[rootObjectName].utilities.length; j++) {
|
||||
let utilityDefinition = projectDefinition[rootObjectName].utilities[j]
|
||||
|
||||
if (exports[utilityDefinition.functionName] === undefined) {
|
||||
projectInstance.utilities[utilityDefinition.propertyName] = eval(utilityDefinition.functionName + '()')
|
||||
} else {
|
||||
projectInstance.utilities[utilityDefinition.propertyName] = eval('exports.' + utilityDefinition.functionName + '()')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/!* Set up Function Libraries of this Project *!/
|
||||
if (projectDefinition[rootObjectName].functionLibraries !== undefined) {
|
||||
for (let j = 0; j < projectDefinition[rootObjectName].functionLibraries.length; j++) {
|
||||
let functionLibraryDefinition = projectDefinition[rootObjectName].functionLibraries[j]
|
||||
|
||||
if (exports[functionLibraryDefinition.functionName] === undefined) {
|
||||
projectInstance.functionLibraries[functionLibraryDefinition.propertyName] = eval(functionLibraryDefinition.functionName + '()')
|
||||
} else {
|
||||
projectInstance.functionLibraries[functionLibraryDefinition.propertyName] = eval('exports.' + functionLibraryDefinition.functionName + '()')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/!* Set up Modules of this Project *!/
|
||||
if (projectDefinition[rootObjectName].modules !== undefined) {
|
||||
for (let j = 0; j < projectDefinition[rootObjectName].modules.length; j++) {
|
||||
let functionLibraryDefinition = projectDefinition[rootObjectName].modules[j]
|
||||
|
||||
if (exports[functionLibraryDefinition.functionName] === undefined) {
|
||||
projectInstance.modules[functionLibraryDefinition.propertyName] = eval(functionLibraryDefinition.functionName + '()')
|
||||
} else {
|
||||
projectInstance.modules[functionLibraryDefinition.propertyName] = eval('exports.' + functionLibraryDefinition.functionName + '()')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
async function loadWUserProfileTimeline() {
|
||||
let queryMessage = {
|
||||
queryType: SA.projects.socialTrading.globals.queryTypes.EVENTS,
|
||||
emitterUserProfileId: undefined,
|
||||
initialIndex: SA.projects.socialTrading.globals.queryConstants.INITIAL_INDEX_LAST,
|
||||
amountRequested: 100,
|
||||
direction: SA.projects.socialTrading.globals.queryConstants.DIRECTION_PAST
|
||||
}
|
||||
|
||||
let query = {
|
||||
requestType: 'Query',
|
||||
queryMessage: JSON.stringify(queryMessage)
|
||||
}
|
||||
|
||||
await UI.projects.socialTrading.modules.webSocketsWebAppClient.sendMessage(
|
||||
JSON.stringify(query)
|
||||
)
|
||||
.then(console.log)
|
||||
.catch(onError)
|
||||
|
||||
function onError(errorMessage) {
|
||||
console.log('[ERROR] Query not executed. ' + errorMessage)
|
||||
console.log('[ERROR] query = ' + JSON.stringify(query))
|
||||
}
|
||||
|
||||
function addToContentDiv(events) {
|
||||
try {
|
||||
let contentDiv = document.getElementById('content-div')
|
||||
/!*
|
||||
Delete al current content.
|
||||
*!/
|
||||
while (contentDiv.childNodes.length > 4) {
|
||||
let childToRemove = contentDiv.childNodes[contentDiv.childNodes.length - 1]
|
||||
contentDiv.removeChild(childToRemove)
|
||||
}
|
||||
/!*
|
||||
Render the timeline.
|
||||
*!/
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
let event = events[i]
|
||||
|
||||
let postDiv = document.createElement("div")
|
||||
postDiv.setAttribute("class", "post-div")
|
||||
let textNode
|
||||
|
||||
switch (event.eventType) {
|
||||
case SA.projects.socialTrading.globals.eventTypes.NEW_USER_POST: {
|
||||
textNode = document.createTextNode(event.emitterUserProfile.userProfileHandle + " POSTED " + event.postText)
|
||||
break
|
||||
}
|
||||
case SA.projects.socialTrading.globals.eventTypes.FOLLOW_USER_PROFILE: {
|
||||
textNode = document.createTextNode(event.emitterUserProfile.userProfileHandle + " FOLLOWED " + event.targetUserProfile.userProfileHandle)
|
||||
break
|
||||
}
|
||||
case SA.projects.socialTrading.globals.eventTypes.UNFOLLOW_USER_PROFILE: {
|
||||
textNode = document.createTextNode(event.emitterUserProfile.userProfileHandle + " UNFOLLOWED " + event.targetUserProfile.userProfileHandle)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
postDiv.appendChild(textNode)
|
||||
contentDiv.appendChild(postDiv)
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.log('[ERROR] err.stack = ' + err.stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
async function loadWhoToFollow() {
|
||||
let queryMessage = {
|
||||
queryType: SA.projects.socialTrading.globals.queryTypes.UNFOLLOWED_USER_PROFILES,
|
||||
emitterUserProfileId: undefined,
|
||||
initialIndex: SA.projects.socialTrading.globals.queryConstants.INITIAL_INDEX_FIRST,
|
||||
amountRequested: 3,
|
||||
direction: SA.projects.socialTrading.globals.queryConstants.DIRECTION_UP
|
||||
}
|
||||
|
||||
let query = {
|
||||
requestType: 'Query',
|
||||
queryMessage: JSON.stringify(queryMessage)
|
||||
}
|
||||
|
||||
await UI.projects.socialTrading.modules.webSocketsWebAppClient.sendMessage(
|
||||
JSON.stringify(query)
|
||||
)
|
||||
.then(addWhoToFollowTable)
|
||||
.catch(onError)
|
||||
|
||||
function onError(errorMessage) {
|
||||
console.log('[ERROR] Query not executed. ' + errorMessage)
|
||||
console.log('[ERROR] query = ' + JSON.stringify(query))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
function setupEventHandlers() {
|
||||
/*
|
||||
Add events to process button clicks , and mouseWheel.
|
||||
*/
|
||||
document.addEventListener("click", onClick)
|
||||
document.addEventListener('mousewheel', onMouseWheel, false)
|
||||
|
||||
async function onClick(event) {
|
||||
|
||||
if (event.target && event.target.nodeName === "BUTTON") {
|
||||
switch (event.target.name) {
|
||||
case 'New Post': {
|
||||
let textArea = document.getElementById("new-post-text-area")
|
||||
await sendNewPostEvent(
|
||||
textArea.value
|
||||
)
|
||||
.then(updateTextArea)
|
||||
.catch(onError)
|
||||
|
||||
function updateTextArea() {
|
||||
textArea.value = ""
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
case 'Follow Profile': {
|
||||
await sendTargetUserProfileEvent(
|
||||
event.target.userProfileId,
|
||||
SA.projects.socialTrading.globals.eventTypes.FOLLOW_USER_PROFILE
|
||||
)
|
||||
.then(updateButton)
|
||||
.catch(onError)
|
||||
|
||||
function updateButton() {
|
||||
let span = document.getElementById('profile-to-follow-span-' + event.target.userProfileId)
|
||||
let button = document.getElementById('profile-to-follow-button-' + event.target.userProfileId)
|
||||
span.setAttribute("class", "profile-to-unfollow-span")
|
||||
button.setAttribute("class", "profile-to-unfollow-button")
|
||||
button.name = 'Unfollow Profile'
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
case 'Unfollow Profile': {
|
||||
await sendTargetUserProfileEvent(
|
||||
event.target.userProfileId,
|
||||
SA.projects.socialTrading.globals.eventTypes.UNFOLLOW_USER_PROFILE
|
||||
)
|
||||
.then(updateButton)
|
||||
.catch(onError)
|
||||
|
||||
/*
|
||||
function updateButton() {
|
||||
let span = document.getElementById('profile-to-follow-span-' + event.target.userProfileId)
|
||||
let button = document.getElementById('profile-to-follow-button-' + event.target.userProfileId)
|
||||
span.setAttribute("class", "profile-to-follow-span")
|
||||
button.setAttribute("class", "profile-to-follow-button")
|
||||
button.name = 'Follow Profile'
|
||||
}
|
||||
*/
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Error Handling
|
||||
*/
|
||||
function onError(errorMessage) {
|
||||
console.log('[ERROR] Click event failed. ' + errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseWheel(event) {
|
||||
let scrollDiv = document.getElementById("scroll-div")
|
||||
scrollDiv.scrollTop = scrollDiv.scrollTop + event.deltaY
|
||||
}
|
||||
}
|
||||
|
||||
async function sendTargetUserProfileEvent(
|
||||
userProfileId,
|
||||
eventType
|
||||
) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
asyncCall(resolve, reject)
|
||||
})
|
||||
|
||||
async function asyncCall(resolve, reject) {
|
||||
let eventMessage
|
||||
let event
|
||||
|
||||
eventMessage = {
|
||||
eventType: eventType,
|
||||
eventId: SA.projects.foundations.utilities.miscellaneousFunctions.genereteUniqueId(),
|
||||
targetUserProfileId: userProfileId,
|
||||
timestamp: (new Date()).valueOf()
|
||||
}
|
||||
|
||||
event = {
|
||||
requestType: 'Event',
|
||||
eventMessage: JSON.stringify(eventMessage)
|
||||
}
|
||||
|
||||
await UI.projects.socialTrading.modules.webSocketsWebAppClient.sendMessage(
|
||||
JSON.stringify(event)
|
||||
)
|
||||
.then(resolve)
|
||||
.catch(onError)
|
||||
|
||||
function onError(errorMessage) {
|
||||
console.log('[ERROR] Event not executed. ' + errorMessage)
|
||||
console.log('[ERROR] event = ' + JSON.stringify(event))
|
||||
reject(errorMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function sendNewPostEvent(
|
||||
postText
|
||||
) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
asyncCall(resolve, reject)
|
||||
})
|
||||
|
||||
async function asyncCall(resolve, reject) {
|
||||
let eventMessage
|
||||
let event
|
||||
|
||||
eventMessage = {
|
||||
eventType: SA.projects.socialTrading.globals.eventTypes.NEW_USER_POST,
|
||||
eventId: SA.projects.foundations.utilities.miscellaneousFunctions.genereteUniqueId(),
|
||||
postText: postText,
|
||||
timestamp: (new Date()).valueOf()
|
||||
}
|
||||
|
||||
event = {
|
||||
requestType: 'Event',
|
||||
eventMessage: JSON.stringify(eventMessage)
|
||||
}
|
||||
|
||||
await UI.projects.socialTrading.modules.webSocketsWebAppClient.sendMessage(
|
||||
JSON.stringify(event)
|
||||
)
|
||||
.then(resolve)
|
||||
.catch(onError)
|
||||
|
||||
function onError(errorMessage) {
|
||||
console.log('[ERROR] Event not executed. ' + errorMessage)
|
||||
console.log('[ERROR] event = ' + JSON.stringify(event))
|
||||
reject(errorMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function messageReceived(message) {
|
||||
window.alert(message)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
exports.startExpress = (port, SA) => {
|
||||
const express = require("express");
|
||||
const app = express();
|
||||
const webAppInterface = DK.projects.socialTrading.modules.webAppInterface.newSocialTradingModulesWebAppInterface();
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
// define a route handler for the default home page
|
||||
app.get("/health-check", (req, res) => {
|
||||
res.send("all good");
|
||||
});
|
||||
|
||||
app.get("/profiles", async (req, res) => {
|
||||
let profiles = await whoToFollow();
|
||||
console.log(profiles)
|
||||
res.send(profiles);
|
||||
});
|
||||
|
||||
app.post("/follow", async (req, res) => {
|
||||
let follow = followProfile(req.body.userProfileId, SA.projects.socialTrading.globals.eventTypes.FOLLOW_USER_PROFILE);
|
||||
res.send("Followed")
|
||||
});
|
||||
|
||||
app.post("/unFollow", async (req, res) => {
|
||||
let follow = followProfile(req.body.userProfileId, SA.projects.socialTrading.globals.eventTypes.UNFOLLOW_USER_PROFILE);
|
||||
res.send("UnFollow")
|
||||
});
|
||||
|
||||
|
||||
app.post("/post", async (req, res) => {
|
||||
console.log(req)
|
||||
let post = createPost(req);
|
||||
res.send(post)
|
||||
});
|
||||
|
||||
// start the Express server
|
||||
app.listen(port, () => {
|
||||
console.log(`server started at http://localhost:${port}`);
|
||||
});
|
||||
|
||||
async function createPost(postText) {
|
||||
try {
|
||||
let eventMessage;
|
||||
let event;
|
||||
|
||||
eventMessage = {
|
||||
eventType: SA.projects.socialTrading.globals.eventTypes.NEW_USER_POST,
|
||||
eventId: SA.projects.foundations.utilities.miscellaneousFunctions.genereteUniqueId(),
|
||||
postText: postText,
|
||||
timestamp: (new Date()).valueOf()
|
||||
}
|
||||
|
||||
event = {
|
||||
requestType: 'Event',
|
||||
eventMessage: JSON.stringify(eventMessage)
|
||||
}
|
||||
|
||||
return await webAppInterface.sendMessage(
|
||||
JSON.stringify(event)
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
async function whoToFollow() {
|
||||
|
||||
try {
|
||||
let queryMessage = {
|
||||
queryType: SA.projects.socialTrading.globals.queryTypes.UNFOLLOWED_USER_PROFILES,
|
||||
emitterUserProfileId: undefined,
|
||||
initialIndex: SA.projects.socialTrading.globals.queryConstants.INITIAL_INDEX_FIRST,
|
||||
amountRequested: 3,
|
||||
direction: SA.projects.socialTrading.globals.queryConstants.DIRECTION_UP
|
||||
}
|
||||
|
||||
let query = {
|
||||
requestType: 'Query',
|
||||
queryMessage: JSON.stringify(queryMessage)
|
||||
}
|
||||
|
||||
return await webAppInterface.messageReceived(
|
||||
JSON.stringify(query)
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function followProfile(userProfileId, eventType) {
|
||||
|
||||
try {
|
||||
let queryMessage = {
|
||||
queryType: SA.projects.socialTrading.globals.queryTypes.UNFOLLOWED_USER_PROFILES,
|
||||
emitterUserProfileId: undefined,
|
||||
initialIndex: SA.projects.socialTrading.globals.queryConstants.INITIAL_INDEX_FIRST,
|
||||
amountRequested: 3,
|
||||
direction: SA.projects.socialTrading.globals.queryConstants.DIRECTION_UP
|
||||
}
|
||||
|
||||
let query = {
|
||||
requestType: 'Query',
|
||||
queryMessage: JSON.stringify(queryMessage)
|
||||
}
|
||||
|
||||
return await webAppInterface.messageReceived(
|
||||
JSON.stringify(query)
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const paths = require('./paths');
|
||||
|
||||
// Make sure that including paths.js after env.js will read .env variables.
|
||||
delete require.cache[require.resolve('./paths')];
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV;
|
||||
if (!NODE_ENV) {
|
||||
throw new Error(
|
||||
'The NODE_ENV environment variable is required but was not specified.'
|
||||
);
|
||||
}
|
||||
|
||||
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
|
||||
const dotenvFiles = [
|
||||
`${paths.dotenv}.${NODE_ENV}.local`,
|
||||
// Don't include `.env.local` for `test` environment
|
||||
// since normally you expect tests to produce the same
|
||||
// results for everyone
|
||||
NODE_ENV !== 'test' && `${paths.dotenv}.local`,
|
||||
`${paths.dotenv}.${NODE_ENV}`,
|
||||
paths.dotenv,
|
||||
].filter(Boolean);
|
||||
|
||||
// Load environment variables from .env* files. Suppress warnings using silent
|
||||
// if this file is missing. dotenv will never modify any environment variables
|
||||
// that have already been set. Variable expansion is supported in .env files.
|
||||
// https://github.com/motdotla/dotenv
|
||||
// https://github.com/motdotla/dotenv-expand
|
||||
dotenvFiles.forEach(dotenvFile => {
|
||||
if (fs.existsSync(dotenvFile)) {
|
||||
require('dotenv-expand')(
|
||||
require('dotenv').config({
|
||||
path: dotenvFile,
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// We support resolving modules according to `NODE_PATH`.
|
||||
// This lets you use absolute paths in imports inside large monorepos:
|
||||
// https://github.com/facebook/create-react-app/issues/253.
|
||||
// It works similar to `NODE_PATH` in Node itself:
|
||||
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
|
||||
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
|
||||
// Otherwise, we risk importing Node.js core modules into an app instead of webpack shims.
|
||||
// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
|
||||
// We also resolve them to make sure all tools using them work consistently.
|
||||
const appDirectory = fs.realpathSync(process.cwd());
|
||||
process.env.NODE_PATH = (process.env.NODE_PATH || '')
|
||||
.split(path.delimiter)
|
||||
.filter(folder => folder && !path.isAbsolute(folder))
|
||||
.map(folder => path.resolve(appDirectory, folder))
|
||||
.join(path.delimiter);
|
||||
|
||||
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
|
||||
// injected into the application via DefinePlugin in webpack configuration.
|
||||
const REACT_APP = /^REACT_APP_/i;
|
||||
|
||||
function getClientEnvironment(publicUrl) {
|
||||
const raw = Object.keys(process.env)
|
||||
.filter(key => REACT_APP.test(key))
|
||||
.reduce(
|
||||
(env, key) => {
|
||||
env[key] = process.env[key];
|
||||
return env;
|
||||
},
|
||||
{
|
||||
// Useful for determining whether we’re running in production mode.
|
||||
// Most importantly, it switches React into the correct mode.
|
||||
NODE_ENV: process.env.NODE_ENV || 'development',
|
||||
// Useful for resolving the correct path to static assets in `public`.
|
||||
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
|
||||
// This should only be used as an escape hatch. Normally you would put
|
||||
// images into the `src` and `import` them in code to get their paths.
|
||||
PUBLIC_URL: publicUrl,
|
||||
// We support configuring the sockjs pathname during development.
|
||||
// These settings let a developer run multiple simultaneous projects.
|
||||
// They are used as the connection `hostname`, `pathname` and `port`
|
||||
// in webpackHotDevClient. They are used as the `sockHost`, `sockPath`
|
||||
// and `sockPort` options in webpack-dev-server.
|
||||
WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST,
|
||||
WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH,
|
||||
WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,
|
||||
// Whether or not react-refresh is enabled.
|
||||
// react-refresh is not 100% stable at this time,
|
||||
// which is why it's disabled by default.
|
||||
// It is defined here so it is available in the webpackHotDevClient.
|
||||
FAST_REFRESH: process.env.FAST_REFRESH !== 'false',
|
||||
}
|
||||
);
|
||||
// Stringify all values so we can feed into webpack DefinePlugin
|
||||
const stringified = {
|
||||
'process.env': Object.keys(raw).reduce((env, key) => {
|
||||
env[key] = JSON.stringify(raw[key]);
|
||||
return env;
|
||||
}, {}),
|
||||
};
|
||||
|
||||
return { raw, stringified };
|
||||
}
|
||||
|
||||
module.exports = getClientEnvironment;
|
||||
@@ -0,0 +1,66 @@
|
||||
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const paths = require('./paths');
|
||||
|
||||
// Ensure the certificate and key provided are valid and if not
|
||||
// throw an easy to debug error
|
||||
function validateKeyAndCerts({ cert, key, keyFile, crtFile }) {
|
||||
let encrypted;
|
||||
try {
|
||||
// publicEncrypt will throw an error with an invalid cert
|
||||
encrypted = crypto.publicEncrypt(cert, Buffer.from('test'));
|
||||
} catch (err) {
|
||||
throw new Error(
|
||||
`The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// privateDecrypt will throw an error with an invalid key
|
||||
crypto.privateDecrypt(key, encrypted);
|
||||
} catch (err) {
|
||||
throw new Error(
|
||||
`The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${
|
||||
err.message
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Read file and throw an error if it doesn't exist
|
||||
function readEnvFile(file, type) {
|
||||
if (!fs.existsSync(file)) {
|
||||
throw new Error(
|
||||
`You specified ${chalk.cyan(
|
||||
type
|
||||
)} in your env, but the file "${chalk.yellow(file)}" can't be found.`
|
||||
);
|
||||
}
|
||||
return fs.readFileSync(file);
|
||||
}
|
||||
|
||||
// Get the https config
|
||||
// Return cert files if provided in env, otherwise just true or false
|
||||
function getHttpsConfig() {
|
||||
const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env;
|
||||
const isHttps = HTTPS === 'true';
|
||||
|
||||
if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) {
|
||||
const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE);
|
||||
const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE);
|
||||
const config = {
|
||||
cert: readEnvFile(crtFile, 'SSL_CRT_FILE'),
|
||||
key: readEnvFile(keyFile, 'SSL_KEY_FILE'),
|
||||
};
|
||||
|
||||
validateKeyAndCerts({ ...config, keyFile, crtFile });
|
||||
return config;
|
||||
}
|
||||
return isHttps;
|
||||
}
|
||||
|
||||
module.exports = getHttpsConfig;
|
||||
@@ -0,0 +1,134 @@
|
||||
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const paths = require('./paths');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const resolve = require('resolve');
|
||||
|
||||
/**
|
||||
* Get additional module paths based on the baseUrl of a compilerOptions object.
|
||||
*
|
||||
* @param {Object} options
|
||||
*/
|
||||
function getAdditionalModulePaths(options = {}) {
|
||||
const baseUrl = options.baseUrl;
|
||||
|
||||
if (!baseUrl) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
||||
|
||||
// We don't need to do anything if `baseUrl` is set to `node_modules`. This is
|
||||
// the default behavior.
|
||||
if (path.relative(paths.appNodeModules, baseUrlResolved) === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Allow the user set the `baseUrl` to `appSrc`.
|
||||
if (path.relative(paths.appSrc, baseUrlResolved) === '') {
|
||||
return [paths.appSrc];
|
||||
}
|
||||
|
||||
// If the path is equal to the root directory we ignore it here.
|
||||
// We don't want to allow importing from the root directly as source files are
|
||||
// not transpiled outside of `src`. We do allow importing them with the
|
||||
// absolute path (e.g. `src/Components/Button.js`) but we set that up with
|
||||
// an alias.
|
||||
if (path.relative(paths.appPath, baseUrlResolved) === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Otherwise, throw an error.
|
||||
throw new Error(
|
||||
chalk.red.bold(
|
||||
"Your project's `baseUrl` can only be set to `src` or `node_modules`." +
|
||||
' Create React App does not support other values at this time.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get webpack aliases based on the baseUrl of a compilerOptions object.
|
||||
*
|
||||
* @param {*} options
|
||||
*/
|
||||
function getWebpackAliases(options = {}) {
|
||||
const baseUrl = options.baseUrl;
|
||||
|
||||
if (!baseUrl) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
||||
|
||||
if (path.relative(paths.appPath, baseUrlResolved) === '') {
|
||||
return {
|
||||
src: paths.appSrc,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get jest aliases based on the baseUrl of a compilerOptions object.
|
||||
*
|
||||
* @param {*} options
|
||||
*/
|
||||
function getJestAliases(options = {}) {
|
||||
const baseUrl = options.baseUrl;
|
||||
|
||||
if (!baseUrl) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
||||
|
||||
if (path.relative(paths.appPath, baseUrlResolved) === '') {
|
||||
return {
|
||||
'^src/(.*)$': '<rootDir>/src/$1',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getModules() {
|
||||
// Check if TypeScript is setup
|
||||
const hasTsConfig = fs.existsSync(paths.appTsConfig);
|
||||
const hasJsConfig = fs.existsSync(paths.appJsConfig);
|
||||
|
||||
if (hasTsConfig && hasJsConfig) {
|
||||
throw new Error(
|
||||
'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.'
|
||||
);
|
||||
}
|
||||
|
||||
let config;
|
||||
|
||||
// If there's a tsconfig.json we assume it's a
|
||||
// TypeScript project and set up the config
|
||||
// based on tsconfig.json
|
||||
if (hasTsConfig) {
|
||||
const ts = require(resolve.sync('typescript', {
|
||||
basedir: paths.appNodeModules,
|
||||
}));
|
||||
config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config;
|
||||
// Otherwise we'll check if there is jsconfig.json
|
||||
// for non TS projects.
|
||||
} else if (hasJsConfig) {
|
||||
config = require(paths.appJsConfig);
|
||||
}
|
||||
|
||||
config = config || {};
|
||||
const options = config.compilerOptions || {};
|
||||
|
||||
const additionalModulePaths = getAdditionalModulePaths(options);
|
||||
|
||||
return {
|
||||
additionalModulePaths: additionalModulePaths,
|
||||
webpackAliases: getWebpackAliases(options),
|
||||
jestAliases: getJestAliases(options),
|
||||
hasTsConfig,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = getModules();
|
||||
@@ -0,0 +1,68 @@
|
||||
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath');
|
||||
const {appPublic} = require("./paths");
|
||||
|
||||
// Make sure any symlinks in the project folder are resolved:
|
||||
// https://github.com/facebook/create-react-app/issues/637
|
||||
const appDirectory = fs.realpathSync(process.cwd());
|
||||
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
|
||||
|
||||
// We use `PUBLIC_URL` environment variable or "homepage" field to infer
|
||||
// "public path" at which the app is served.
|
||||
// webpack needs to know it to put the right <script> hrefs into HTML even in
|
||||
// single-page apps that may serve index.html for nested URLs like /todos/42.
|
||||
// We can't use a relative path in HTML because we don't want to load something
|
||||
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
|
||||
const publicUrlOrPath = getPublicUrlOrPath(
|
||||
process.env.NODE_ENV === 'development',
|
||||
require(resolveApp('package.json')).homepage,
|
||||
process.env.PUBLIC_URL
|
||||
);
|
||||
|
||||
const buildPath = process.env.BUILD_PATH || 'DesktopReact/build';
|
||||
|
||||
const moduleFileExtensions = [
|
||||
'web.mjs',
|
||||
'mjs',
|
||||
'web.js',
|
||||
'js',
|
||||
'web.ts',
|
||||
'ts',
|
||||
'web.tsx',
|
||||
'tsx',
|
||||
'json',
|
||||
'web.jsx',
|
||||
'jsx',
|
||||
];
|
||||
|
||||
// Resolve file paths in the same order as webpack
|
||||
const resolveModule = (resolveFn, filePath) => {
|
||||
const extension = moduleFileExtensions.find(extension =>
|
||||
fs.existsSync(resolveFn(`${filePath}.${extension}`))
|
||||
);
|
||||
|
||||
if (extension) {
|
||||
return resolveFn(`${filePath}.${extension}`);
|
||||
}
|
||||
|
||||
return resolveFn(`${filePath}.js`);
|
||||
};
|
||||
|
||||
// config after eject: we're in ./config/
|
||||
module.exports = {
|
||||
dotenv: resolveApp('.env'),
|
||||
appPath: resolveApp('.'),
|
||||
appBuild: resolveApp(buildPath),
|
||||
appPublic: resolveApp('DesktopReact/frontend/public'),
|
||||
appHtml: resolveApp('DesktopReact/frontend/public/index.html'),
|
||||
appIndexJs: resolveModule(resolveApp, 'DesktopReact/frontend/src/index'),
|
||||
appPackageJson: resolveApp('package.json'),
|
||||
appSrc: resolveApp('DesktopReact/frontend/src'),
|
||||
appNodeModules: resolveApp('node_modules'),
|
||||
publicUrlOrPath,
|
||||
};
|
||||
|
||||
module.exports.moduleFileExtensions = moduleFileExtensions;
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
|
||||
const { resolveModuleName } = require('ts-pnp');
|
||||
|
||||
exports.resolveModuleName = (
|
||||
typescript,
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost
|
||||
) => {
|
||||
return resolveModuleName(
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost,
|
||||
typescript.resolveModuleName
|
||||
);
|
||||
};
|
||||
|
||||
exports.resolveTypeReferenceDirective = (
|
||||
typescript,
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost
|
||||
) => {
|
||||
return resolveModuleName(
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost,
|
||||
typescript.resolveTypeReferenceDirective
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,757 @@
|
||||
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const resolve = require('resolve');
|
||||
const PnpWebpackPlugin = require('pnp-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
|
||||
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||
const safePostCssParser = require('postcss-safe-parser');
|
||||
const ManifestPlugin = require('webpack-manifest-plugin');
|
||||
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
|
||||
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
|
||||
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
|
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
||||
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
|
||||
const ESLintPlugin = require('eslint-webpack-plugin');
|
||||
const paths = require('./paths');
|
||||
const modules = require('./modules');
|
||||
const getClientEnvironment = require('./env');
|
||||
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
|
||||
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin');
|
||||
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
|
||||
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
|
||||
|
||||
const postcssNormalize = require('postcss-normalize');
|
||||
|
||||
const appPackageJson = require(paths.appPackageJson);
|
||||
|
||||
// Source maps are resource heavy and can cause out of memory issue for large source files.
|
||||
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
|
||||
|
||||
const webpackDevClientEntry = require.resolve(
|
||||
'react-dev-utils/webpackHotDevClient'
|
||||
);
|
||||
const reactRefreshOverlayEntry = require.resolve(
|
||||
'react-dev-utils/refreshOverlayInterop'
|
||||
);
|
||||
|
||||
// Some apps do not need the benefits of saving a web request, so not inlining the chunk
|
||||
// makes for a smoother build process.
|
||||
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
|
||||
|
||||
const emitErrorsAsWarnings = process.env.ESLINT_NO_DEV_ERRORS === 'true';
|
||||
const disableESLintPlugin = process.env.DISABLE_ESLINT_PLUGIN === 'true';
|
||||
|
||||
const imageInlineSizeLimit = parseInt(
|
||||
process.env.IMAGE_INLINE_SIZE_LIMIT || '10000'
|
||||
);
|
||||
|
||||
// Check if TypeScript is setup
|
||||
const useTypeScript = fs.existsSync(paths.appTsConfig);
|
||||
|
||||
// Get the path to the uncompiled service worker (if it exists).
|
||||
const swSrc = paths.swSrc;
|
||||
|
||||
// style files regexes
|
||||
const cssRegex = /\.css$/;
|
||||
const cssModuleRegex = /\.module\.css$/;
|
||||
const sassRegex = /\.(scss|sass)$/;
|
||||
const sassModuleRegex = /\.module\.(scss|sass)$/;
|
||||
|
||||
const hasJsxRuntime = (() => {
|
||||
if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
require.resolve('react/jsx-runtime');
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
|
||||
// This is the production and development configuration.
|
||||
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
|
||||
module.exports = function (webpackEnv) {
|
||||
const isEnvDevelopment = webpackEnv === 'development';
|
||||
const isEnvProduction = webpackEnv === 'production';
|
||||
|
||||
// Variable used for enabling profiling in Production
|
||||
// passed into alias object. Uses a flag if passed into the build command
|
||||
const isEnvProductionProfile =
|
||||
isEnvProduction && process.argv.includes('--profile');
|
||||
|
||||
// We will provide `paths.publicUrlOrPath` to our app
|
||||
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
|
||||
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
|
||||
// Get environment variables to inject into our app.
|
||||
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
|
||||
|
||||
const shouldUseReactRefresh = env.raw.FAST_REFRESH;
|
||||
|
||||
// common function to get style loaders
|
||||
const getStyleLoaders = (cssOptions, preProcessor) => {
|
||||
const loaders = [
|
||||
isEnvDevelopment && require.resolve('style-loader'),
|
||||
isEnvProduction && {
|
||||
loader: MiniCssExtractPlugin.loader,
|
||||
// css is located in `static/css`, use '../../' to locate index.html folder
|
||||
// in production `paths.publicUrlOrPath` can be a relative path
|
||||
options: paths.publicUrlOrPath.startsWith('.')
|
||||
? { publicPath: '../../' }
|
||||
: {},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: cssOptions,
|
||||
},
|
||||
{
|
||||
// Options for PostCSS as we reference these options twice
|
||||
// Adds vendor prefixing based on your specified browser support in
|
||||
// package.json
|
||||
loader: require.resolve('postcss-loader'),
|
||||
options: {
|
||||
// Necessary for external CSS imports to work
|
||||
// https://github.com/facebook/create-react-app/issues/2677
|
||||
ident: 'postcss',
|
||||
plugins: () => [
|
||||
require('postcss-flexbugs-fixes'),
|
||||
require('postcss-preset-env')({
|
||||
autoprefixer: {
|
||||
flexbox: 'no-2009',
|
||||
},
|
||||
stage: 3,
|
||||
}),
|
||||
// Adds PostCSS Normalize as the reset css with default options,
|
||||
// so that it honors browserslist config in package.json
|
||||
// which in turn let's users customize the target behavior as per their needs.
|
||||
postcssNormalize(),
|
||||
],
|
||||
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
|
||||
},
|
||||
},
|
||||
].filter(Boolean);
|
||||
if (preProcessor) {
|
||||
loaders.push(
|
||||
{
|
||||
loader: require.resolve('resolve-url-loader'),
|
||||
options: {
|
||||
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
|
||||
root: paths.appSrc,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve(preProcessor),
|
||||
options: {
|
||||
sourceMap: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
return loaders;
|
||||
};
|
||||
|
||||
return {
|
||||
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
|
||||
// Stop compilation early in production
|
||||
bail: isEnvProduction,
|
||||
devtool: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
? 'source-map'
|
||||
: false
|
||||
: isEnvDevelopment && 'cheap-module-source-map',
|
||||
// These are the "entry points" to our application.
|
||||
// This means they will be the "root" imports that are included in JS bundle.
|
||||
entry:
|
||||
isEnvDevelopment && !shouldUseReactRefresh
|
||||
? [
|
||||
// Include an alternative client for WebpackDevServer. A client's job is to
|
||||
// connect to WebpackDevServer by a socket and get notified about changes.
|
||||
// When you save a file, the client will either apply hot updates (in case
|
||||
// of CSS changes), or refresh the page (in case of JS changes). When you
|
||||
// make a syntax error, this client will display a syntax error overlay.
|
||||
// Note: instead of the default WebpackDevServer client, we use a custom one
|
||||
// to bring better experience for Create React App users. You can replace
|
||||
// the line below with these two lines if you prefer the stock client:
|
||||
//
|
||||
// require.resolve('webpack-dev-server/client') + '?/',
|
||||
// require.resolve('webpack/hot/dev-server'),
|
||||
//
|
||||
// When using the experimental react-refresh integration,
|
||||
// the webpack plugin takes care of injecting the dev client for us.
|
||||
webpackDevClientEntry,
|
||||
// Finally, this is your app's code:
|
||||
paths.appIndexJs,
|
||||
// We include the app code last so that if there is a runtime error during
|
||||
// initialization, it doesn't blow up the WebpackDevServer client, and
|
||||
// changing JS code would still trigger a refresh.
|
||||
]
|
||||
: paths.appIndexJs,
|
||||
output: {
|
||||
// The build folder.
|
||||
path: isEnvProduction ? paths.appBuild : undefined,
|
||||
// Add /* filename */ comments to generated require()s in the output.
|
||||
pathinfo: isEnvDevelopment,
|
||||
// There will be one main bundle, and one file per asynchronous chunk.
|
||||
// In development, it does not produce real files.
|
||||
filename: isEnvProduction
|
||||
? 'static/js/[name].[contenthash:8].js'
|
||||
: isEnvDevelopment && 'static/js/bundle.js',
|
||||
// TODO: remove this when upgrading to webpack 5
|
||||
futureEmitAssets: true,
|
||||
// There are also additional JS chunk files if you use code splitting.
|
||||
chunkFilename: isEnvProduction
|
||||
? 'static/js/[name].[contenthash:8].chunk.js'
|
||||
: isEnvDevelopment && 'static/js/[name].chunk.js',
|
||||
// webpack uses `publicPath` to determine where the app is being served from.
|
||||
// It requires a trailing slash, or the file assets will get an incorrect path.
|
||||
// We inferred the "public path" (such as / or /my-project) from homepage.
|
||||
publicPath: paths.publicUrlOrPath,
|
||||
// Point sourcemap entries to original disk location (format as URL on Windows)
|
||||
devtoolModuleFilenameTemplate: isEnvProduction
|
||||
? info =>
|
||||
path
|
||||
.relative(paths.appSrc, info.absoluteResourcePath)
|
||||
.replace(/\\/g, '/')
|
||||
: isEnvDevelopment &&
|
||||
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
|
||||
// Prevents conflicts when multiple webpack runtimes (from different apps)
|
||||
// are used on the same page.
|
||||
jsonpFunction: `webpackJsonp${appPackageJson.name}`,
|
||||
// this defaults to 'window', but by setting it to 'this' then
|
||||
// module chunks which are built will work in web workers as well.
|
||||
globalObject: 'this',
|
||||
},
|
||||
optimization: {
|
||||
minimize: isEnvProduction,
|
||||
minimizer: [
|
||||
// This is only used in production mode
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
parse: {
|
||||
// We want terser to parse ecma 8 code. However, we don't want it
|
||||
// to apply any minification steps that turns valid ecma 5 code
|
||||
// into invalid ecma 5 code. This is why the 'compress' and 'output'
|
||||
// sections only apply transformations that are ecma 5 safe
|
||||
// https://github.com/facebook/create-react-app/pull/4234
|
||||
ecma: 8,
|
||||
},
|
||||
compress: {
|
||||
ecma: 5,
|
||||
warnings: false,
|
||||
// Disabled because of an issue with Uglify breaking seemingly valid code:
|
||||
// https://github.com/facebook/create-react-app/issues/2376
|
||||
// Pending further investigation:
|
||||
// https://github.com/mishoo/UglifyJS2/issues/2011
|
||||
comparisons: false,
|
||||
// Disabled because of an issue with Terser breaking valid code:
|
||||
// https://github.com/facebook/create-react-app/issues/5250
|
||||
// Pending further investigation:
|
||||
// https://github.com/terser-js/terser/issues/120
|
||||
inline: 2,
|
||||
},
|
||||
mangle: {
|
||||
safari10: true,
|
||||
},
|
||||
// Added for profiling in devtools
|
||||
keep_classnames: isEnvProductionProfile,
|
||||
keep_fnames: isEnvProductionProfile,
|
||||
output: {
|
||||
ecma: 5,
|
||||
comments: false,
|
||||
// Turned on because emoji and regex is not minified properly using default
|
||||
// https://github.com/facebook/create-react-app/issues/2488
|
||||
ascii_only: true,
|
||||
},
|
||||
},
|
||||
sourceMap: shouldUseSourceMap,
|
||||
}),
|
||||
// This is only used in production mode
|
||||
new OptimizeCSSAssetsPlugin({
|
||||
cssProcessorOptions: {
|
||||
parser: safePostCssParser,
|
||||
map: shouldUseSourceMap
|
||||
? {
|
||||
// `inline: false` forces the sourcemap to be output into a
|
||||
// separate file
|
||||
inline: false,
|
||||
// `annotation: true` appends the sourceMappingURL to the end of
|
||||
// the css file, helping the browser find the sourcemap
|
||||
annotation: true,
|
||||
}
|
||||
: false,
|
||||
},
|
||||
cssProcessorPluginOptions: {
|
||||
preset: ['default', { minifyFontValues: { removeQuotes: false } }],
|
||||
},
|
||||
}),
|
||||
],
|
||||
// Automatically split vendor and commons
|
||||
// https://twitter.com/wSokra/status/969633336732905474
|
||||
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
|
||||
splitChunks: {
|
||||
chunks: 'all',
|
||||
name: isEnvDevelopment,
|
||||
},
|
||||
// Keep the runtime chunk separated to enable long term caching
|
||||
// https://twitter.com/wSokra/status/969679223278505985
|
||||
// https://github.com/facebook/create-react-app/issues/5358
|
||||
runtimeChunk: {
|
||||
name: entrypoint => `runtime-${entrypoint.name}`,
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
// This allows you to set a fallback for where webpack should look for modules.
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebook/create-react-app/issues/253
|
||||
modules: ['node_modules', paths.appNodeModules].concat(
|
||||
modules.additionalModulePaths || []
|
||||
),
|
||||
// These are the reasonable defaults supported by the Node ecosystem.
|
||||
// We also include JSX as a common component filename extension to support
|
||||
// some tools, although we do not recommend using it, see:
|
||||
// https://github.com/facebook/create-react-app/issues/290
|
||||
// `web` extension prefixes have been added for better support
|
||||
// for React Native Web.
|
||||
extensions: paths.moduleFileExtensions
|
||||
.map(ext => `.${ext}`)
|
||||
.filter(ext => useTypeScript || !ext.includes('ts')),
|
||||
alias: {
|
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
'react-native': 'react-native-web',
|
||||
// Allows for better profiling with ReactDevTools
|
||||
...(isEnvProductionProfile && {
|
||||
'react-dom$': 'react-dom/profiling',
|
||||
'scheduler/tracing': 'scheduler/tracing-profiling',
|
||||
}),
|
||||
...(modules.webpackAliases || {}),
|
||||
},
|
||||
plugins: [
|
||||
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
|
||||
// guards against forgotten dependencies and such.
|
||||
PnpWebpackPlugin,
|
||||
// Prevents users from importing files from outside of src/ (or node_modules/).
|
||||
// This often causes confusion because we only process files within src/ with babel.
|
||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
||||
// please link the files into your node_modules/ and let module-resolution kick in.
|
||||
// Make sure your source files are compiled, as they will not be processed in any way.
|
||||
new ModuleScopePlugin(paths.appSrc, [
|
||||
paths.appPackageJson,
|
||||
reactRefreshOverlayEntry,
|
||||
]),
|
||||
],
|
||||
},
|
||||
resolveLoader: {
|
||||
plugins: [
|
||||
// Also related to Plug'n'Play, but this time it tells webpack to load its loaders
|
||||
// from the current package.
|
||||
PnpWebpackPlugin.moduleLoader(module),
|
||||
],
|
||||
},
|
||||
module: {
|
||||
strictExportPresence: true,
|
||||
rules: [
|
||||
// Disable require.ensure as it's not a standard language feature.
|
||||
{ parser: { requireEnsure: false } },
|
||||
{
|
||||
// "oneOf" will traverse all following loaders until one will
|
||||
// match the requirements. When no loader matches it will fall
|
||||
// back to the "file" loader at the end of the loader list.
|
||||
oneOf: [
|
||||
// TODO: Merge this config once `image/avif` is in the mime-db
|
||||
// https://github.com/jshttp/mime-db
|
||||
{
|
||||
test: [/\.avif$/],
|
||||
loader: require.resolve('url-loader'),
|
||||
options: {
|
||||
limit: imageInlineSizeLimit,
|
||||
mimetype: 'image/avif',
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// "url" loader works like "file" loader except that it embeds assets
|
||||
// smaller than specified limit in bytes as data URLs to avoid requests.
|
||||
// A missing `test` is equivalent to a match.
|
||||
{
|
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
|
||||
loader: require.resolve('url-loader'),
|
||||
options: {
|
||||
limit: imageInlineSizeLimit,
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// Process application JS with Babel.
|
||||
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
|
||||
{
|
||||
test: /\.(js|mjs|jsx|ts|tsx)$/,
|
||||
include: paths.appSrc,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
customize: require.resolve(
|
||||
'babel-preset-react-app/webpack-overrides'
|
||||
),
|
||||
presets: [
|
||||
[
|
||||
require.resolve('babel-preset-react-app'),
|
||||
{
|
||||
runtime: hasJsxRuntime ? 'automatic' : 'classic',
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
plugins: [
|
||||
[
|
||||
require.resolve('babel-plugin-named-asset-import'),
|
||||
{
|
||||
loaderMap: {
|
||||
svg: {
|
||||
ReactComponent:
|
||||
'@svgr/webpack?-svgo,+titleProp,+ref![path]',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
isEnvDevelopment &&
|
||||
shouldUseReactRefresh &&
|
||||
require.resolve('react-refresh/babel'),
|
||||
].filter(Boolean),
|
||||
// This is a feature of `babel-loader` for webpack (not Babel itself).
|
||||
// It enables caching results in ./node_modules/.cache/babel-loader/
|
||||
// directory for faster rebuilds.
|
||||
cacheDirectory: true,
|
||||
// See #6846 for context on why cacheCompression is disabled
|
||||
cacheCompression: false,
|
||||
compact: isEnvProduction,
|
||||
},
|
||||
},
|
||||
// Process any JS outside of the app with Babel.
|
||||
// Unlike the application JS, we only compile the standard ES features.
|
||||
{
|
||||
test: /\.(js|mjs)$/,
|
||||
exclude: /@babel(?:\/|\\{1,2})runtime/,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
babelrc: false,
|
||||
configFile: false,
|
||||
compact: false,
|
||||
presets: [
|
||||
[
|
||||
require.resolve('babel-preset-react-app/dependencies'),
|
||||
{ helpers: true },
|
||||
],
|
||||
],
|
||||
cacheDirectory: true,
|
||||
// See #6846 for context on why cacheCompression is disabled
|
||||
cacheCompression: false,
|
||||
|
||||
// Babel sourcemaps are needed for debugging into node_modules
|
||||
// code. Without the options below, debuggers like VSCode
|
||||
// show incorrect code and set breakpoints on the wrong lines.
|
||||
sourceMaps: shouldUseSourceMap,
|
||||
inputSourceMap: shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
// "postcss" loader applies autoprefixer to our CSS.
|
||||
// "css" loader resolves paths in CSS and adds assets as dependencies.
|
||||
// "style" loader turns CSS into JS modules that inject <style> tags.
|
||||
// In production, we use MiniCSSExtractPlugin to extract that CSS
|
||||
// to a file, but in development "style" loader enables hot editing
|
||||
// of CSS.
|
||||
// By default we support CSS Modules with the extension .module.css
|
||||
{
|
||||
test: cssRegex,
|
||||
exclude: cssModuleRegex,
|
||||
use: getStyleLoaders({
|
||||
importLoaders: 1,
|
||||
sourceMap: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
: isEnvDevelopment,
|
||||
}),
|
||||
// Don't consider CSS imports dead code even if the
|
||||
// containing package claims to have no side effects.
|
||||
// Remove this when webpack adds a warning or an error for this.
|
||||
// See https://github.com/webpack/webpack/issues/6571
|
||||
sideEffects: true,
|
||||
},
|
||||
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
|
||||
// using the extension .module.css
|
||||
{
|
||||
test: cssModuleRegex,
|
||||
use: getStyleLoaders({
|
||||
importLoaders: 1,
|
||||
sourceMap: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
: isEnvDevelopment,
|
||||
modules: {
|
||||
getLocalIdent: getCSSModuleLocalIdent,
|
||||
},
|
||||
}),
|
||||
},
|
||||
// Opt-in support for SASS (using .scss or .sass extensions).
|
||||
// By default we support SASS Modules with the
|
||||
// extensions .module.scss or .module.sass
|
||||
{
|
||||
test: sassRegex,
|
||||
exclude: sassModuleRegex,
|
||||
use: getStyleLoaders(
|
||||
{
|
||||
importLoaders: 3,
|
||||
sourceMap: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
: isEnvDevelopment,
|
||||
},
|
||||
'sass-loader'
|
||||
),
|
||||
// Don't consider CSS imports dead code even if the
|
||||
// containing package claims to have no side effects.
|
||||
// Remove this when webpack adds a warning or an error for this.
|
||||
// See https://github.com/webpack/webpack/issues/6571
|
||||
sideEffects: true,
|
||||
},
|
||||
// Adds support for CSS Modules, but using SASS
|
||||
// using the extension .module.scss or .module.sass
|
||||
{
|
||||
test: sassModuleRegex,
|
||||
use: getStyleLoaders(
|
||||
{
|
||||
importLoaders: 3,
|
||||
sourceMap: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
: isEnvDevelopment,
|
||||
modules: {
|
||||
getLocalIdent: getCSSModuleLocalIdent,
|
||||
},
|
||||
},
|
||||
'sass-loader'
|
||||
),
|
||||
},
|
||||
// "file" loader makes sure those assets get served by WebpackDevServer.
|
||||
// When you `import` an asset, you get its (virtual) filename.
|
||||
// In production, they would get copied to the `build` folder.
|
||||
// This loader doesn't use a "test" so it will catch all modules
|
||||
// that fall through the other loaders.
|
||||
{
|
||||
loader: require.resolve('file-loader'),
|
||||
// Exclude `js` files to keep "css" loader working as it injects
|
||||
// its runtime that would otherwise be processed through "file" loader.
|
||||
// Also exclude `html` and `json` extensions so they get processed
|
||||
// by webpacks internal loaders.
|
||||
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
|
||||
options: {
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// ** STOP ** Are you adding a new loader?
|
||||
// Make sure to add the new loader(s) before the "file" loader.
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
// Generates an `index.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin(
|
||||
Object.assign(
|
||||
{},
|
||||
{
|
||||
inject: true,
|
||||
template: paths.appHtml,
|
||||
},
|
||||
isEnvProduction
|
||||
? {
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeRedundantAttributes: true,
|
||||
useShortDoctype: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
keepClosingSlash: true,
|
||||
minifyJS: true,
|
||||
minifyCSS: true,
|
||||
minifyURLs: true,
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
)
|
||||
),
|
||||
// Inlines the webpack runtime script. This script is too small to warrant
|
||||
// a network request.
|
||||
// https://github.com/facebook/create-react-app/issues/5358
|
||||
isEnvProduction &&
|
||||
shouldInlineRuntimeChunk &&
|
||||
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// It will be an empty string unless you specify "homepage"
|
||||
// in `package.json`, in which case it will be the pathname of that URL.
|
||||
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
|
||||
// This gives some necessary context to module not found errors, such as
|
||||
// the requesting resource.
|
||||
new ModuleNotFoundPlugin(paths.appPath),
|
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
|
||||
// It is absolutely essential that NODE_ENV is set to production
|
||||
// during a production build.
|
||||
// Otherwise React will be compiled in the very slow development mode.
|
||||
new webpack.DefinePlugin(env.stringified),
|
||||
// This is necessary to emit hot updates (CSS and Fast Refresh):
|
||||
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
|
||||
// Experimental hot reloading for React .
|
||||
// https://github.com/facebook/react/tree/master/packages/react-refresh
|
||||
isEnvDevelopment &&
|
||||
shouldUseReactRefresh &&
|
||||
new ReactRefreshWebpackPlugin({
|
||||
overlay: {
|
||||
entry: webpackDevClientEntry,
|
||||
// The expected exports are slightly different from what the overlay exports,
|
||||
// so an interop is included here to enable feedback on module-level errors.
|
||||
module: reactRefreshOverlayEntry,
|
||||
// Since we ship a custom dev client and overlay integration,
|
||||
// the bundled socket handling logic can be eliminated.
|
||||
sockIntegration: false,
|
||||
},
|
||||
}),
|
||||
// Watcher doesn't work well if you mistype casing in a path so we use
|
||||
// a plugin that prints an error when you attempt to do this.
|
||||
// See https://github.com/facebook/create-react-app/issues/240
|
||||
isEnvDevelopment && new CaseSensitivePathsPlugin(),
|
||||
// If you require a missing module and then `npm install` it, you still have
|
||||
// to restart the development server for webpack to discover it. This plugin
|
||||
// makes the discovery automatic so you don't have to restart.
|
||||
// See https://github.com/facebook/create-react-app/issues/186
|
||||
isEnvDevelopment &&
|
||||
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
|
||||
isEnvProduction &&
|
||||
new MiniCssExtractPlugin({
|
||||
// Options similar to the same options in webpackOptions.output
|
||||
// both options are optional
|
||||
filename: 'static/css/[name].[contenthash:8].css',
|
||||
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
|
||||
}),
|
||||
// Generate an asset manifest file with the following content:
|
||||
// - "files" key: Mapping of all asset filenames to their corresponding
|
||||
// output file so that tools can pick it up without having to parse
|
||||
// `index.html`
|
||||
// - "entrypoints" key: Array of files which are included in `index.html`,
|
||||
// can be used to reconstruct the HTML if necessary
|
||||
new ManifestPlugin({
|
||||
fileName: 'asset-manifest.json',
|
||||
publicPath: paths.publicUrlOrPath,
|
||||
generate: (seed, files, entrypoints) => {
|
||||
const manifestFiles = files.reduce((manifest, file) => {
|
||||
manifest[file.name] = file.path;
|
||||
return manifest;
|
||||
}, seed);
|
||||
const entrypointFiles = entrypoints.main.filter(
|
||||
fileName => !fileName.endsWith('.map')
|
||||
);
|
||||
|
||||
return {
|
||||
files: manifestFiles,
|
||||
entrypoints: entrypointFiles,
|
||||
};
|
||||
},
|
||||
}),
|
||||
// Moment.js is an extremely popular library that bundles large locale files
|
||||
// by default due to how webpack interprets its code. This is a practical
|
||||
// solution that requires the user to opt into importing specific locales.
|
||||
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
|
||||
// You can remove this if you don't use Moment.js:
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||
// Generate a service worker script that will precache, and keep up to date,
|
||||
// the HTML & assets that are part of the webpack build.
|
||||
isEnvProduction &&
|
||||
fs.existsSync(swSrc) &&
|
||||
new WorkboxWebpackPlugin.InjectManifest({
|
||||
swSrc,
|
||||
dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./,
|
||||
exclude: [/\.map$/, /asset-manifest\.json$/, /LICENSE/],
|
||||
// Bump up the default maximum size (2mb) that's precached,
|
||||
// to make lazy-loading failure scenarios less likely.
|
||||
// See https://github.com/cra-template/pwa/issues/13#issuecomment-722667270
|
||||
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
|
||||
}),
|
||||
// TypeScript type checking
|
||||
useTypeScript &&
|
||||
new ForkTsCheckerWebpackPlugin({
|
||||
typescript: resolve.sync('typescript', {
|
||||
basedir: paths.appNodeModules,
|
||||
}),
|
||||
async: isEnvDevelopment,
|
||||
checkSyntacticErrors: true,
|
||||
resolveModuleNameModule: process.versions.pnp
|
||||
? `${__dirname}/pnpTs.js`
|
||||
: undefined,
|
||||
resolveTypeReferenceDirectiveModule: process.versions.pnp
|
||||
? `${__dirname}/pnpTs.js`
|
||||
: undefined,
|
||||
tsconfig: paths.appTsConfig,
|
||||
reportFiles: [
|
||||
// This one is specifically to match during CI tests,
|
||||
// as micromatch doesn't match
|
||||
// '../cra-template-typescript/template/src/App.tsx'
|
||||
// otherwise.
|
||||
'../**/src/**/*.{ts,tsx}',
|
||||
'**/src/**/*.{ts,tsx}',
|
||||
'!**/src/**/__tests__/**',
|
||||
'!**/src/**/?(*.)(spec|test).*',
|
||||
'!**/src/setupProxy.*',
|
||||
'!**/src/setupTests.*',
|
||||
],
|
||||
silent: true,
|
||||
// The formatter is invoked directly in WebpackDevServerUtils during development
|
||||
formatter: isEnvProduction ? typescriptFormatter : undefined,
|
||||
}),
|
||||
!disableESLintPlugin &&
|
||||
new ESLintPlugin({
|
||||
// Plugin options
|
||||
extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
|
||||
formatter: require.resolve('react-dev-utils/eslintFormatter'),
|
||||
eslintPath: require.resolve('eslint'),
|
||||
failOnError: !(isEnvDevelopment && emitErrorsAsWarnings),
|
||||
context: paths.appSrc,
|
||||
cache: true,
|
||||
cacheLocation: path.resolve(
|
||||
paths.appNodeModules,
|
||||
'.cache/.eslintcache'
|
||||
),
|
||||
// ESLint class options
|
||||
cwd: paths.appPath,
|
||||
resolvePluginsRelativeTo: __dirname,
|
||||
baseConfig: {
|
||||
extends: [require.resolve('eslint-config-react-app/base')],
|
||||
rules: {
|
||||
...(!hasJsxRuntime && {
|
||||
'react/react-in-jsx-scope': 'error',
|
||||
}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
].filter(Boolean),
|
||||
// Some libraries import Node modules but don't use them in the browser.
|
||||
// Tell webpack to provide empty mocks for them so importing them works.
|
||||
node: {
|
||||
module: 'empty',
|
||||
dgram: 'empty',
|
||||
dns: 'mock',
|
||||
fs: 'empty',
|
||||
http2: 'empty',
|
||||
net: 'empty',
|
||||
tls: 'empty',
|
||||
child_process: 'empty',
|
||||
},
|
||||
// Turn off performance processing because we utilize
|
||||
// our own hints via the FileSizeReporter
|
||||
performance: false,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
|
||||
|
||||
const fs = require('fs');
|
||||
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
|
||||
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
|
||||
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
|
||||
const ignoredFiles = require('react-dev-utils/ignoredFiles');
|
||||
const redirectServedPath = require('react-dev-utils/redirectServedPathMiddleware');
|
||||
const paths = require('./paths');
|
||||
const getHttpsConfig = require('./getHttpsConfig');
|
||||
|
||||
const host = process.env.HOST || '0.0.0.0';
|
||||
const sockHost = process.env.WDS_SOCKET_HOST;
|
||||
const sockPath = process.env.WDS_SOCKET_PATH; // default: '/sockjs-node'
|
||||
const sockPort = process.env.WDS_SOCKET_PORT;
|
||||
|
||||
module.exports = function (proxy, allowedHost) {
|
||||
return {
|
||||
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
|
||||
// websites from potentially accessing local content through DNS rebinding:
|
||||
// https://github.com/webpack/webpack-dev-server/issues/887
|
||||
// https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
|
||||
// However, it made several existing use cases such as development in cloud
|
||||
// environment or subdomains in development significantly more complicated:
|
||||
// https://github.com/facebook/create-react-app/issues/2271
|
||||
// https://github.com/facebook/create-react-app/issues/2233
|
||||
// While we're investigating better solutions, for now we will take a
|
||||
// compromise. Since our WDS configuration only serves files in the `public`
|
||||
// folder we won't consider accessing them a vulnerability. However, if you
|
||||
// use the `proxy` feature, it gets more dangerous because it can expose
|
||||
// remote code execution vulnerabilities in backends like Django and Rails.
|
||||
// So we will disable the host check normally, but enable it if you have
|
||||
// specified the `proxy` setting. Finally, we let you override it if you
|
||||
// really know what you're doing with a special environment variable.
|
||||
disableHostCheck:
|
||||
!proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',
|
||||
// Enable gzip compression of generated files.
|
||||
compress: true,
|
||||
// Silence WebpackDevServer's own logs since they're generally not useful.
|
||||
// It will still show compile warnings and errors with this setting.
|
||||
clientLogLevel: 'none',
|
||||
// By default WebpackDevServer serves physical files from current directory
|
||||
// in addition to all the virtual build products that it serves from memory.
|
||||
// This is confusing because those files won’t automatically be available in
|
||||
// production build folder unless we copy them. However, copying the whole
|
||||
// project directory is dangerous because we may expose sensitive files.
|
||||
// Instead, we establish a convention that only files in `public` directory
|
||||
// get served. Our build script will copy `public` into the `build` folder.
|
||||
// In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
|
||||
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
|
||||
// Note that we only recommend to use `public` folder as an escape hatch
|
||||
// for files like `favicon.ico`, `manifest.json`, and libraries that are
|
||||
// for some reason broken when imported through webpack. If you just want to
|
||||
// use an image, put it in `src` and `import` it from JavaScript instead.
|
||||
contentBase: paths.appPublic,
|
||||
contentBasePublicPath: paths.publicUrlOrPath,
|
||||
// By default files from `contentBase` will not trigger a page reload.
|
||||
watchContentBase: true,
|
||||
// Enable hot reloading server. It will provide WDS_SOCKET_PATH endpoint
|
||||
// for the WebpackDevServer client so it can learn when the files were
|
||||
// updated. The WebpackDevServer client is included as an entry point
|
||||
// in the webpack development configuration. Note that only changes
|
||||
// to CSS are currently hot reloaded. JS changes will refresh the browser.
|
||||
hot: true,
|
||||
// Use 'ws' instead of 'sockjs-node' on server since we're using native
|
||||
// websockets in `webpackHotDevClient`.
|
||||
transportMode: 'ws',
|
||||
// Prevent a WS client from getting injected as we're already including
|
||||
// `webpackHotDevClient`.
|
||||
injectClient: false,
|
||||
// Enable custom sockjs pathname for websocket connection to hot reloading server.
|
||||
// Enable custom sockjs hostname, pathname and port for websocket connection
|
||||
// to hot reloading server.
|
||||
sockHost,
|
||||
sockPath,
|
||||
sockPort,
|
||||
// It is important to tell WebpackDevServer to use the same "publicPath" path as
|
||||
// we specified in the webpack config. When homepage is '.', default to serving
|
||||
// from the root.
|
||||
// remove last slash so user can land on `/test` instead of `/test/`
|
||||
publicPath: paths.publicUrlOrPath.slice(0, -1),
|
||||
// WebpackDevServer is noisy by default so we emit custom message instead
|
||||
// by listening to the compiler events with `compiler.hooks[...].tap` calls above.
|
||||
quiet: true,
|
||||
// Reportedly, this avoids CPU overload on some systems.
|
||||
// https://github.com/facebook/create-react-app/issues/293
|
||||
// src/node_modules is not ignored to support absolute imports
|
||||
// https://github.com/facebook/create-react-app/issues/1065
|
||||
watchOptions: {
|
||||
ignored: ignoredFiles(paths.appSrc),
|
||||
},
|
||||
https: getHttpsConfig(),
|
||||
host,
|
||||
overlay: false,
|
||||
historyApiFallback: {
|
||||
// Paths with dots should still use the history fallback.
|
||||
// See https://github.com/facebook/create-react-app/issues/387.
|
||||
disableDotRule: true,
|
||||
index: paths.publicUrlOrPath,
|
||||
},
|
||||
public: allowedHost,
|
||||
// `proxy` is run between `before` and `after` `webpack-dev-server` hooks
|
||||
proxy,
|
||||
before(app, server) {
|
||||
// Keep `evalSourceMapMiddleware` and `errorOverlayMiddleware`
|
||||
// middlewares before `redirectServedPath` otherwise will not have any effect
|
||||
// This lets us fetch source contents from webpack for the error overlay
|
||||
app.use(evalSourceMapMiddleware(server));
|
||||
// This lets us open files from the runtime error overlay.
|
||||
app.use(errorOverlayMiddleware());
|
||||
|
||||
if (fs.existsSync(paths.proxySetup)) {
|
||||
// This registers user provided middleware for proxy reasons
|
||||
require(paths.proxySetup)(app);
|
||||
}
|
||||
},
|
||||
after(app) {
|
||||
// Redirect to `PUBLIC_URL` or `homepage` from `package.json` if url not match
|
||||
app.use(redirectServedPath(paths.publicUrlOrPath));
|
||||
|
||||
// This service worker file is effectively a 'no-op' that will reset any
|
||||
// previous service worker registered for the same host:port combination.
|
||||
// We do this in development to avoid hitting the production cache if
|
||||
// it used the same host and port.
|
||||
// https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
|
||||
app.use(noopServiceWorkerMiddleware(paths.publicUrlOrPath));
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Superalgos Desktop</title>
|
||||
<meta name="description" content="Free and Open Source Crypto Social Trading">
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json"/>
|
||||
|
||||
<script type="text/javascript">
|
||||
function loadApp() {
|
||||
// Prevent click-jacking
|
||||
try {
|
||||
if (window === window.top || window.chrome && chrome.app && chrome.app.window) {
|
||||
document.documentElement.style.display = 'block'
|
||||
} else {
|
||||
top.location = self.location
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('CJ protection', e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body id="body" onload="loadApp()">
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -0,0 +1,212 @@
|
||||
|
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'production';
|
||||
process.env.NODE_ENV = 'production';
|
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
|
||||
|
||||
const path = require('path');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const fs = require('fs-extra');
|
||||
const bfj = require('bfj');
|
||||
const webpack = require('webpack');
|
||||
const configFactory = require('../config/webpack.config');
|
||||
const paths = require('../config/paths');
|
||||
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
|
||||
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
|
||||
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
|
||||
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
|
||||
const printBuildError = require('react-dev-utils/printBuildError');
|
||||
|
||||
const measureFileSizesBeforeBuild =
|
||||
FileSizeReporter.measureFileSizesBeforeBuild;
|
||||
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
|
||||
const useYarn = fs.existsSync(paths.yarnLockFile);
|
||||
|
||||
// These sizes are pretty large. We'll warn for bundles exceeding them.
|
||||
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
|
||||
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
|
||||
|
||||
const isInteractive = process.stdout.isTTY;
|
||||
|
||||
// Warn and crash if required files are missing
|
||||
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const argv = process.argv.slice(2);
|
||||
const writeStatsJson = argv.indexOf('--stats') !== -1;
|
||||
|
||||
// Generate configuration
|
||||
const config = configFactory('production');
|
||||
|
||||
// We require that you explicitly set browsers and do not fall back to
|
||||
// browserslist defaults.
|
||||
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
|
||||
checkBrowsers(paths.appPath, isInteractive)
|
||||
.then(() => {
|
||||
// First, read the current file sizes in build directory.
|
||||
// This lets us display how much they changed later.
|
||||
return measureFileSizesBeforeBuild(paths.appBuild);
|
||||
})
|
||||
.then(previousFileSizes => {
|
||||
// Remove all content but keep the directory so that
|
||||
// if you're in it, you don't end up in Trash
|
||||
fs.emptyDirSync(paths.appBuild);
|
||||
// Merge with the public folder
|
||||
copyPublicFolder();
|
||||
// Start the webpack build
|
||||
return build(previousFileSizes);
|
||||
})
|
||||
.then(
|
||||
({ stats, previousFileSizes, warnings }) => {
|
||||
if (warnings.length) {
|
||||
console.log(chalk.yellow('Compiled with warnings.\n'));
|
||||
console.log(warnings.join('\n\n'));
|
||||
console.log(
|
||||
'\nSearch for the ' +
|
||||
chalk.underline(chalk.yellow('keywords')) +
|
||||
' to learn more about each warning.'
|
||||
);
|
||||
console.log(
|
||||
'To ignore, add ' +
|
||||
chalk.cyan('// eslint-disable-next-line') +
|
||||
' to the line before.\n'
|
||||
);
|
||||
} else {
|
||||
console.log(chalk.green('Compiled successfully.\n'));
|
||||
}
|
||||
|
||||
console.log('File sizes after gzip:\n');
|
||||
printFileSizesAfterBuild(
|
||||
stats,
|
||||
previousFileSizes,
|
||||
paths.appBuild,
|
||||
WARN_AFTER_BUNDLE_GZIP_SIZE,
|
||||
WARN_AFTER_CHUNK_GZIP_SIZE
|
||||
);
|
||||
console.log();
|
||||
|
||||
const appPackage = require(paths.appPackageJson);
|
||||
const publicUrl = paths.publicUrlOrPath;
|
||||
const publicPath = config.output.publicPath;
|
||||
const buildFolder = path.relative(process.cwd(), paths.appBuild);
|
||||
printHostingInstructions(
|
||||
appPackage,
|
||||
publicUrl,
|
||||
publicPath,
|
||||
buildFolder,
|
||||
useYarn
|
||||
);
|
||||
},
|
||||
err => {
|
||||
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
|
||||
if (tscCompileOnError) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'Compiled with the following type errors (you may want to check these before deploying your app):\n'
|
||||
)
|
||||
);
|
||||
printBuildError(err);
|
||||
} else {
|
||||
console.log(chalk.red('Failed to compile.\n'));
|
||||
printBuildError(err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch(err => {
|
||||
if (err && err.message) {
|
||||
console.log(err.message);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// Create the production build and print the deployment instructions.
|
||||
function build(previousFileSizes) {
|
||||
console.log('Creating an optimized production build...');
|
||||
|
||||
const compiler = webpack(config);
|
||||
return new Promise((resolve, reject) => {
|
||||
compiler.run((err, stats) => {
|
||||
let messages;
|
||||
if (err) {
|
||||
if (!err.message) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
let errMessage = err.message;
|
||||
|
||||
// Add additional information for postcss errors
|
||||
if (Object.prototype.hasOwnProperty.call(err, 'postcssNode')) {
|
||||
errMessage +=
|
||||
'\nCompileError: Begins at CSS selector ' +
|
||||
err['postcssNode'].selector;
|
||||
}
|
||||
|
||||
messages = formatWebpackMessages({
|
||||
errors: [errMessage],
|
||||
warnings: [],
|
||||
});
|
||||
} else {
|
||||
messages = formatWebpackMessages(
|
||||
stats.toJson({ all: false, warnings: true, errors: true })
|
||||
);
|
||||
}
|
||||
if (messages.errors.length) {
|
||||
// Only keep the first error. Others are often indicative
|
||||
// of the same problem, but confuse the reader with noise.
|
||||
if (messages.errors.length > 1) {
|
||||
messages.errors.length = 1;
|
||||
}
|
||||
return reject(new Error(messages.errors.join('\n\n')));
|
||||
}
|
||||
if (
|
||||
process.env.CI &&
|
||||
(typeof process.env.CI !== 'string' ||
|
||||
process.env.CI.toLowerCase() !== 'false') &&
|
||||
messages.warnings.length
|
||||
) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'\nTreating warnings as errors because process.env.CI = true.\n' +
|
||||
'Most CI servers set it automatically.\n'
|
||||
)
|
||||
);
|
||||
return reject(new Error(messages.warnings.join('\n\n')));
|
||||
}
|
||||
|
||||
const resolveArgs = {
|
||||
stats,
|
||||
previousFileSizes,
|
||||
warnings: messages.warnings,
|
||||
};
|
||||
|
||||
if (writeStatsJson) {
|
||||
return bfj
|
||||
.write(paths.appBuild + '/bundle-stats.json', stats.toJson())
|
||||
.then(() => resolve(resolveArgs))
|
||||
.catch(error => reject(new Error(error)));
|
||||
}
|
||||
|
||||
return resolve(resolveArgs);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function copyPublicFolder() {
|
||||
fs.copySync(paths.appPublic, paths.appBuild, {
|
||||
dereference: true,
|
||||
filter: file => file !== paths.appHtml,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
exports.start = (port) => {
|
||||
|
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'development';
|
||||
process.env.NODE_ENV = 'development';
|
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
|
||||
|
||||
const fs = require('fs');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const webpack = require('webpack');
|
||||
const WebpackDevServer = require('webpack-dev-server');
|
||||
const clearConsole = require('react-dev-utils/clearConsole');
|
||||
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
|
||||
const {
|
||||
choosePort,
|
||||
createCompiler,
|
||||
prepareProxy,
|
||||
prepareUrls,
|
||||
} = require('react-dev-utils/WebpackDevServerUtils');
|
||||
const openBrowser = require('react-dev-utils/openBrowser');
|
||||
const semver = require('semver');
|
||||
const paths = require('../config/paths');
|
||||
const configFactory = require('../config/webpack.config');
|
||||
const createDevServerConfig = require('../config/webpackDevServer.config');
|
||||
const getClientEnvironment = require('../config/env');
|
||||
const react = require(require.resolve('react', {paths: [paths.appPath]}));
|
||||
|
||||
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
|
||||
const useYarn = fs.existsSync(paths.yarnLockFile);
|
||||
const isInteractive = process.stdout.isTTY;
|
||||
|
||||
// Warn and crash if required files are missing
|
||||
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Tools like Cloud9 rely on this.
|
||||
const DEFAULT_PORT = port;
|
||||
const HOST = '0.0.0.0';
|
||||
|
||||
if (process.env.HOST) {
|
||||
console.log(
|
||||
chalk.cyan(
|
||||
`Attempting to bind to HOST environment variable: ${chalk.yellow(
|
||||
chalk.bold(process.env.HOST)
|
||||
)}`
|
||||
)
|
||||
);
|
||||
console.log();
|
||||
}
|
||||
|
||||
// We require that you explicitly set browsers and do not fall back to
|
||||
// browserslist defaults.
|
||||
const {checkBrowsers} = require('react-dev-utils/browsersHelper');
|
||||
checkBrowsers(paths.appPath, isInteractive)
|
||||
.then(() => {
|
||||
// We attempt to use the default port but if it is busy, we offer the user to
|
||||
// run on a different port. `choosePort()` Promise resolves to the next free port.
|
||||
return choosePort(HOST, DEFAULT_PORT);
|
||||
})
|
||||
.then(port => {
|
||||
if (port == null) {
|
||||
// We have not found a port.
|
||||
return;
|
||||
}
|
||||
|
||||
const config = configFactory('development');
|
||||
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
|
||||
const appName = require(paths.appPackageJson).name;
|
||||
|
||||
const useTypeScript = fs.existsSync(paths.appTsConfig);
|
||||
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
|
||||
const urls = prepareUrls(
|
||||
protocol,
|
||||
HOST,
|
||||
port,
|
||||
paths.publicUrlOrPath.slice(0, -1)
|
||||
);
|
||||
const devSocket = {
|
||||
warnings: warnings =>
|
||||
devServer.sockWrite(devServer.sockets, 'warnings', warnings),
|
||||
errors: errors =>
|
||||
devServer.sockWrite(devServer.sockets, 'errors', errors),
|
||||
};
|
||||
// Create a webpack compiler that is configured with custom messages.
|
||||
const compiler = createCompiler({
|
||||
appName,
|
||||
config,
|
||||
devSocket,
|
||||
urls,
|
||||
useYarn,
|
||||
useTypeScript,
|
||||
tscCompileOnError,
|
||||
webpack,
|
||||
});
|
||||
// Load proxy config
|
||||
const proxySetting = require(paths.appPackageJson).proxy;
|
||||
const proxyConfig = prepareProxy(
|
||||
proxySetting,
|
||||
paths.appPublic,
|
||||
paths.publicUrlOrPath
|
||||
);
|
||||
// Serve webpack assets generated by the compiler over a web server.
|
||||
const serverConfig = createDevServerConfig(
|
||||
proxyConfig,
|
||||
urls.lanUrlForConfig
|
||||
);
|
||||
const devServer = new WebpackDevServer(compiler, serverConfig);
|
||||
// Launch WebpackDevServer.
|
||||
devServer.listen(port, HOST, err => {
|
||||
if (err) {
|
||||
return console.log(err);
|
||||
}
|
||||
if (isInteractive) {
|
||||
clearConsole();
|
||||
}
|
||||
|
||||
if (env.raw.FAST_REFRESH && semver.lt(react.version, '16.10.0')) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`Fast Refresh requires React 16.10 or higher. You are using React ${react.version}.`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
console.log(chalk.cyan('Starting the development server...\n'));
|
||||
openBrowser(urls.localUrlForBrowser);
|
||||
});
|
||||
|
||||
['SIGINT', 'SIGTERM'].forEach(function (sig) {
|
||||
process.on(sig, function () {
|
||||
devServer.close();
|
||||
process.exit();
|
||||
});
|
||||
});
|
||||
|
||||
if (process.env.CI !== 'true') {
|
||||
// Gracefully exit when stdin ends
|
||||
process.stdin.on('end', function () {
|
||||
devServer.close();
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
if (err && err.message) {
|
||||
console.log(err.message);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'test';
|
||||
process.env.NODE_ENV = 'test';
|
||||
process.env.PUBLIC_URL = '';
|
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
|
||||
|
||||
const jest = require('jest');
|
||||
const execSync = require('child_process').execSync;
|
||||
let argv = process.argv.slice(2);
|
||||
|
||||
function isInGitRepository() {
|
||||
try {
|
||||
execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isInMercurialRepository() {
|
||||
try {
|
||||
execSync('hg --cwd . root', { stdio: 'ignore' });
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Watch unless on CI or explicitly running all tests
|
||||
if (
|
||||
!process.env.CI &&
|
||||
argv.indexOf('--watchAll') === -1 &&
|
||||
argv.indexOf('--watchAll=false') === -1
|
||||
) {
|
||||
// https://github.com/facebook/create-react-app/issues/5210
|
||||
const hasSourceControl = isInGitRepository() || isInMercurialRepository();
|
||||
argv.push(hasSourceControl ? '--watch' : '--watchAll');
|
||||
}
|
||||
|
||||
|
||||
jest.run(argv);
|
||||
@@ -0,0 +1,28 @@
|
||||
const baseURL = 'http://localhost:33249'
|
||||
const GET = 'GET'
|
||||
const POST = 'POST'
|
||||
const DELETE = 'DELETE'
|
||||
const UPDATE = 'UPDATE'
|
||||
|
||||
function getProfiles(){
|
||||
return call('/profiles', GET);
|
||||
}
|
||||
|
||||
function getClientNode(){
|
||||
return call('/ClientNode', GET);
|
||||
}
|
||||
|
||||
function call(endpoint, method, body){
|
||||
return new Promise(async(resolve, reject) => {
|
||||
fetch(baseURL + endpoint, { method:method, body:body })
|
||||
.then(response => resolve(response))
|
||||
.catch(error => reject(error))
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
export {
|
||||
getProfiles,
|
||||
getClientNode
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
import {BrowserRouter, Route, Routes} from 'react-router-dom';
|
||||
import App from './home/App';
|
||||
import UserProfile from "./userProfile/UserProfile";
|
||||
import NotFound from "./notFound/NotFound";
|
||||
import Feed from "./feed/Feed";
|
||||
import PostPlaceholder from "./postPlaceholder/PostPlaceholder";
|
||||
|
||||
|
||||
const Root = () => {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path='/' element={<App/>}>
|
||||
<Route path='' element={<Feed/>}/>
|
||||
<Route path='Profile' element={<UserProfile/>}>
|
||||
{/*<Route path=':profileId' element={<UserProfile/>}/>*/}
|
||||
</Route>
|
||||
<Route path='Post' element={<PostPlaceholder/>}/>
|
||||
</Route>
|
||||
<Route
|
||||
path="*"
|
||||
element={
|
||||
<NotFound/>
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
</BrowserRouter>)
|
||||
}
|
||||
|
||||
export default Root;
|
||||
@@ -0,0 +1,17 @@
|
||||
.followButton {
|
||||
margin: 0.5rem;
|
||||
width: 3rem;
|
||||
max-height: 1.5rem;
|
||||
}
|
||||
|
||||
.userCard {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0.25rem 1rem;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import "./UserCard.css"
|
||||
import React, {useEffect} from 'react';
|
||||
import {Button, Card, CardContent, CardMedia, Typography} from "@mui/material";
|
||||
import pic from "../../images/superalgos.png"
|
||||
|
||||
const UserCard = ({name, id,followCallback}) => {
|
||||
|
||||
useEffect(() => {
|
||||
console.log(`created user with id ${id}`)
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<Card className="userCard" variant="outlined">
|
||||
<CardMedia className="avatar"
|
||||
component="img"
|
||||
image={pic}
|
||||
alt="PP"
|
||||
|
||||
/>
|
||||
<CardContent>
|
||||
<Typography variant="body2">{name}</Typography>
|
||||
</CardContent>
|
||||
<Button className="followButton" size="small" disableElevation variant="outlined" onClick={followCallback}>
|
||||
Follow
|
||||
</Button>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserCard;
|
||||
@@ -0,0 +1,4 @@
|
||||
.showMore {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import './UsersSidebar.css';
|
||||
import {Stack} from "@mui/material";
|
||||
import SearchBox from "../searchBox/SearchBox";
|
||||
import SuggestedUsers from "../suggestedUsers/SuggestedUsers";
|
||||
|
||||
|
||||
const UsersSidebar = () => {
|
||||
|
||||
const showMoreCallback = () => console.log("clicked show more");
|
||||
return (
|
||||
<Stack direction="column"
|
||||
justifyContent="flex-start"
|
||||
alignItems="center"
|
||||
spacing={2}
|
||||
sx={{minWidth: "max-content"}}>
|
||||
|
||||
<SearchBox/>
|
||||
<SuggestedUsers showMoreCallback={showMoreCallback}/>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default UsersSidebar;
|
||||
@@ -0,0 +1,11 @@
|
||||
.feed {
|
||||
width: 40rem;
|
||||
height: 95vh;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: none; /* Hide scrollbar for IE and Edge */
|
||||
scrollbar-width: none; /* Hide scrollbar for Firefox */
|
||||
}
|
||||
|
||||
.feed::-webkit-scrollbar { /* Hide scrollbar for Chrome, Safari and Opera */
|
||||
display: none;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import './Feed.css';
|
||||
import PostPlaceholder from "../postPlaceholder/PostPlaceholder";
|
||||
import PostsFeed from "../postsFeed/PostsFeed";
|
||||
|
||||
const Feed = () => {
|
||||
return (
|
||||
<div className="feed">
|
||||
<PostPlaceholder/>
|
||||
<PostsFeed/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Feed;
|
||||
@@ -0,0 +1,7 @@
|
||||
.app {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.middleSection{
|
||||
width: 40rem;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import './App.css';
|
||||
import Sidebar from "../sidebar/Sidebar";
|
||||
import UsersSidebar from "../UsersSidebar/UsersSidebar";
|
||||
import {Stack} from "@mui/material";
|
||||
import {Outlet} from "react-router-dom";
|
||||
import {getProfiles} from '../../api/service'
|
||||
|
||||
function App() {
|
||||
getProfiles().then( response => {
|
||||
console.log(response)
|
||||
});
|
||||
return (
|
||||
<div className="app">
|
||||
|
||||
<Stack className="app"
|
||||
direction="row"
|
||||
justifyContent="space-around">
|
||||
<Sidebar/>
|
||||
<Outlet className="middleSection"/>
|
||||
<UsersSidebar/>
|
||||
</Stack>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -0,0 +1,10 @@
|
||||
/*noinspection CssInvalidPropertyValue*/
|
||||
.logo {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 3rem;
|
||||
width: 100%;
|
||||
}
|
||||
.icon {
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import './Logo.css';
|
||||
import React from 'react';
|
||||
import {Avatar, Stack} from "@mui/material";
|
||||
import pic from "../../images/superalgos.png"
|
||||
|
||||
|
||||
const Logo = props => {
|
||||
/* TODO add on click redirect to / */
|
||||
return (
|
||||
<Stack direction="row" className="logo">
|
||||
<Avatar className="icon" alt="SuperAlgos" src={pic}/>
|
||||
<div>SuperAlgos</div>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default Logo;
|
||||
@@ -0,0 +1,10 @@
|
||||
import React, {useEffect} from 'react';
|
||||
import {useNavigate} from "react-router-dom";
|
||||
|
||||
const NotFound = () => {
|
||||
let navigate = useNavigate();
|
||||
useEffect(() => navigate("/"), [navigate])
|
||||
return null;
|
||||
};
|
||||
|
||||
export default NotFound;
|
||||
@@ -0,0 +1,49 @@
|
||||
.post {
|
||||
min-width: 5rem; /* todo needs to set a minimum width, but overriding from parent*/
|
||||
max-width: 100%;
|
||||
min-height: 5rem; /* todo needs tbd. needed actually? */
|
||||
max-height: 100%; /* todo need to be 8rem, but is collapsing without scroll. need fix*/
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.postAvatarContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
.postBody {
|
||||
max-width: 100%;
|
||||
min-width: 1.5rem;
|
||||
min-height: 1.5rem;
|
||||
padding-left: 2.8rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.postUserName {
|
||||
padding: 0.75rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.postFooterContainer {
|
||||
justify-content: flex-start;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding-top: 0.50rem;
|
||||
padding-bottom: 0.50rem;
|
||||
}
|
||||
|
||||
.postFooterLikeDislike {
|
||||
justify-content: space-evenly;
|
||||
display: flex;
|
||||
width: 30%;
|
||||
padding-left: 0.50rem;
|
||||
}
|
||||
|
||||
.postFooterRepost {
|
||||
justify-content: flex-end;
|
||||
display: flex;
|
||||
width: 65%;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import "./Post.css"
|
||||
import React, {useState} from 'react';
|
||||
import {Avatar, Card, IconButton, Stack, Typography} from "@mui/material";
|
||||
import {Autorenew, ThumbDown, ThumbUp} from "@mui/icons-material";
|
||||
import pic from "../../images/superalgos.png"
|
||||
|
||||
const Post = () => {
|
||||
|
||||
const userName = "User Name",
|
||||
postBody = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
|
||||
|
||||
const [count, setCount] = useState(0); /* todo define one more for dislike button */
|
||||
|
||||
function onLikeButtonClick() { {/* todo console log print need a fix */}
|
||||
console.log("Liked " + count + " times")
|
||||
}
|
||||
|
||||
function onButtonClick(){ /* todo dislike button function necessary? */
|
||||
console.log("Liked " + count + " times")
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="post">
|
||||
<Stack direction="row">
|
||||
<Stack className="postAvatarContainer">
|
||||
<Avatar src={pic}/>
|
||||
</Stack>
|
||||
<Stack className="postUserName">
|
||||
{userName}
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack className="postBody">
|
||||
{postBody}
|
||||
</Stack>
|
||||
<Stack className="postFooterContainer" direction="row">
|
||||
<Stack className="postFooterLikeDislike" direction="row">
|
||||
<IconButton
|
||||
onClick={() => setCount(count + 1)}
|
||||
>
|
||||
<ThumbUp/>
|
||||
</IconButton><Typography>{count}</Typography>
|
||||
<IconButton
|
||||
onClick={() => {/* TODO remove unnecessary arrow function*/
|
||||
onButtonClick()
|
||||
}}
|
||||
>
|
||||
<ThumbDown/>
|
||||
</IconButton>
|
||||
</Stack>
|
||||
<Stack className="postFooterRepost" direction="row">
|
||||
<IconButton
|
||||
onClick={() => {/* TODO remove unnecessary arrow function*/
|
||||
onButtonClick()
|
||||
}}
|
||||
>
|
||||
<Autorenew/>
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default Post;
|
||||
@@ -0,0 +1,47 @@
|
||||
.postPlaceholder {
|
||||
width: 39rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.postPlaceholderContainer {
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.postPlaceholderInputBoxContainer {
|
||||
|
||||
}
|
||||
|
||||
.postPlaceholderAvatar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
.postPlaceholderTextField {
|
||||
width: 90%;
|
||||
border: 5px;
|
||||
}
|
||||
|
||||
.postPlaceholderInputBox {
|
||||
width: auto;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.postFeedAccountCircle {
|
||||
|
||||
}
|
||||
|
||||
.postPlaceholderButtonStyle {
|
||||
padding: 0.75rem;
|
||||
display: flex;
|
||||
width: auto;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.postPlaceholderButton {
|
||||
height: 1.5rem;
|
||||
width: 5rem;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import "./PostPlaceholder.css"
|
||||
import React from 'react';
|
||||
import {Avatar, Button, Card, Stack, TextField, Typography} from "@mui/material";
|
||||
import pic from "../../images/superalgos.png"
|
||||
|
||||
const PostPlaceholder = () => {
|
||||
|
||||
function onButtonClick() {
|
||||
console.log("Clicked Post button");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="postPlaceholder">
|
||||
<h4>Posts</h4>
|
||||
<div className="postPlaceholderContainer">
|
||||
<Card>
|
||||
<Stack
|
||||
className="postPlaceholderInputBox"
|
||||
direction="row"
|
||||
justifyContent="flex-start"
|
||||
alignItems="center"
|
||||
spacing={1}
|
||||
justify="flex-end">
|
||||
<Stack className="postPlaceholderAvatar">
|
||||
<Avatar src={pic}/>
|
||||
</Stack>
|
||||
<Stack className="postPlaceholderTextField">
|
||||
<Typography gutterBottom variant="subtitle1" component="div">
|
||||
<TextField
|
||||
id="outlined-multiline-flexible"
|
||||
label="what's happening?"
|
||||
fullWidth
|
||||
multiline
|
||||
maxRows={6}
|
||||
/>
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack className="postPlaceholderButtonStyle" direction="row">
|
||||
<Button
|
||||
onClick={() => { /* TODO remove unnecessary arrow function call*/
|
||||
onButtonClick()
|
||||
}}
|
||||
className="postPlaceholderButton" variant="outlined">
|
||||
Post
|
||||
</Button>
|
||||
</Stack>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PostPlaceholder;
|
||||
@@ -0,0 +1,5 @@
|
||||
.postFeedContainer {
|
||||
max-width: 39rem;
|
||||
padding: 0.5rem ;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import './PostsFeed.css';
|
||||
import React from 'react';
|
||||
import {Stack} from "@mui/material";
|
||||
import Post from "../post/Post";
|
||||
|
||||
const PostsFeed = () => {
|
||||
const postFeed = [1, 2, 3, 4, 5];
|
||||
|
||||
const posts = postFeed.map(
|
||||
value => {
|
||||
return <Post key={value} id={value} name={`user${value}`}/>
|
||||
}
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="postFeedContainer">
|
||||
<Stack direction="column"
|
||||
spacing={2}>
|
||||
{posts}
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PostsFeed;
|
||||
@@ -0,0 +1,3 @@
|
||||
.searchIcon {
|
||||
margin: 1rem 0.5rem;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import {Box, TextField} from "@mui/material";
|
||||
import {Search} from "@mui/icons-material";
|
||||
|
||||
const SearchBox = () => {
|
||||
return (
|
||||
<div>
|
||||
<Box sx={{display: 'flex', alignItems: 'flex-end'}}>
|
||||
<Search className="searchIcon"/>
|
||||
<TextField id="users-search" label="Search" variant="standard" type="search"/>
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchBox;
|
||||
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import {Link} from "@mui/material";
|
||||
|
||||
const ShowMoreUsers = ({showMoreCallback}) => {
|
||||
return (
|
||||
<Link
|
||||
component="button"
|
||||
variant="body2"
|
||||
onClick={showMoreCallback}>
|
||||
Show more
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export default ShowMoreUsers;
|
||||
@@ -0,0 +1,3 @@
|
||||
.sidebar{
|
||||
height: 100%;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import './Sidebar.css';
|
||||
import {Divider, Stack} from "@mui/material";
|
||||
import SidebarItem from "../sidebarItem/SidebarItem";
|
||||
import Logo from "../logo/Logo";
|
||||
import HomeIcon from '@mui/icons-material/Home';
|
||||
import PostAddIcon from '@mui/icons-material/PostAdd';
|
||||
import PersonIcon from '@mui/icons-material/Person';
|
||||
|
||||
const Sidebar = props => {
|
||||
const menuOptions = [{name: "Home", icon: HomeIcon},
|
||||
{name: "Profile", icon: PersonIcon},
|
||||
{name: "Post", icon: PostAddIcon}
|
||||
/*, "Explore", "Notifications", "Messages", "Bookmarks", "Lists", "More"*/]
|
||||
|
||||
const menuItems = menuOptions.map(option => {
|
||||
return <SidebarItem key={option.name} Icon={option.icon} value={option.name}/>
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="sidebar">
|
||||
<Stack direction="column"
|
||||
justifyContent="flex-start"
|
||||
alignItems="center"
|
||||
spacing={5}
|
||||
sx={{minWidth: "max-content"}}>
|
||||
<Logo/>
|
||||
<Stack
|
||||
direction="column"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
spacing={2}
|
||||
divider={<Divider orientation="horizontal" flexItem/>}>
|
||||
{menuItems}
|
||||
</Stack>
|
||||
|
||||
</Stack>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Sidebar;
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
.sidebarItem {
|
||||
/*TODO add on hover*/
|
||||
/*TODO add breakpoints*/
|
||||
width: 15vw;
|
||||
}
|
||||
|
||||
|
||||
.sidebarText {
|
||||
/*TODO hide when too small*/
|
||||
|
||||
width: auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sidebarIcon {
|
||||
width: auto;
|
||||
padding: 0 1rem;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import './SidebarItem.css';
|
||||
import {Stack} from "@mui/material";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
|
||||
|
||||
const SidebarItem = ({value, Icon}) => {
|
||||
let navigate = useNavigate();
|
||||
const handleClick = () => {
|
||||
navigate(`/${value}`)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sidebarItem" onClick={handleClick}>
|
||||
<Stack direction="row">
|
||||
<Icon className="sidebarIcon"/>
|
||||
<div className="sidebarText"> {value} </div>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SidebarItem;
|
||||
@@ -0,0 +1,32 @@
|
||||
import "./SuggestedUsers.css"
|
||||
import React, {useEffect} from 'react';
|
||||
import UserCard from "../User/UserCard";
|
||||
import ShowMoreUsers from "../showMoreUsers/ShowMoreUsers";
|
||||
import {Stack} from "@mui/material";
|
||||
|
||||
|
||||
const SuggestedUsers = ({showMoreCallback}) => {
|
||||
const usersIds = [1, 2, 3, 4, 5];
|
||||
|
||||
|
||||
const users = usersIds.map(value => {
|
||||
let callBack = () => console.log(`Clicked follow on user${value}`);
|
||||
return <UserCard key={value} id={value} name={`user${value}`} followCallback={callBack}/>
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
console.log("SuggestedUsersConstructor called")
|
||||
}, []);
|
||||
|
||||
|
||||
return (
|
||||
<Stack direction="column"
|
||||
justifyContent="flex-start"
|
||||
alignItems="center"
|
||||
spacing={1}>
|
||||
{users}
|
||||
<ShowMoreUsers showMoreCallback={showMoreCallback}/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
export default SuggestedUsers;
|
||||
@@ -0,0 +1,41 @@
|
||||
.userSection {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.banner {
|
||||
height: 10rem;
|
||||
}
|
||||
|
||||
.profileAvatar {
|
||||
height: 7rem !important;
|
||||
width: 7rem !important;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.profilePicBG {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: none no-repeat scroll 0 0 #fff;
|
||||
position: relative;
|
||||
height: 8rem !important;
|
||||
width: 8rem !important;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.profileCard {
|
||||
margin-top: -50px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 8rem;
|
||||
}
|
||||
|
||||
.profileSection {
|
||||
width: 100%;
|
||||
overflow: initial !important; /* TODO check this is needed */
|
||||
height: 15rem;
|
||||
}
|
||||
|
||||
.middleSection {
|
||||
width: 39rem;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import "./UserProfile.css"
|
||||
import React from 'react';
|
||||
import UserProfileHeader from "../userProfileHeader/UserProfileHeader";
|
||||
import {Stack} from "@mui/material";
|
||||
import PostsFeed from "../postsFeed/PostsFeed";
|
||||
|
||||
|
||||
const UserProfile = () => {
|
||||
|
||||
/*TODO finish this shit*/
|
||||
|
||||
let actualUser = 'Loui Molina';
|
||||
|
||||
return (
|
||||
<Stack direction="column"
|
||||
justifyContent="flex-start"
|
||||
alignItems="center"
|
||||
spacing={1}
|
||||
className="middleSection">
|
||||
|
||||
<UserProfileHeader actualUser={actualUser}/>
|
||||
<PostsFeed/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export default UserProfile;
|
||||
@@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import {Card, CardContent, CardMedia, Typography} from "@mui/material";
|
||||
import banner from "../../images/banner.jpg";
|
||||
import pfp from "../../images/superalgos.png";
|
||||
|
||||
const UserProfileHeader = ({actualUser}) => {
|
||||
return (
|
||||
<Card className="profileSection">
|
||||
<CardMedia className="banner"
|
||||
component="img"
|
||||
image={banner}
|
||||
alt="PP"
|
||||
/>
|
||||
<div className="profileCard">
|
||||
<div className="profilePicBG">
|
||||
<CardMedia className="profileAvatar"
|
||||
component="img"
|
||||
image={pfp}
|
||||
alt="ProfilePic"
|
||||
/>
|
||||
</div>
|
||||
<CardContent className="userSection">
|
||||
<Typography variant="h5">{actualUser}</Typography>
|
||||
</CardContent>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserProfileHeader;
|
||||
|
After Width: | Height: | Size: 6.3 MiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,13 @@
|
||||
body {
|
||||
margin: 2.5vh 5vw;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
height: 95vh !important;
|
||||
}
|
||||
|
||||
#root {
|
||||
height: inherit;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import './index.css';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import {Provider} from "react-redux";
|
||||
import reducers from "./store";
|
||||
import Root from "./components/Root";
|
||||
|
||||
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<Provider store={reducers}>
|
||||
<Root/>
|
||||
</Provider>
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
@@ -0,0 +1,309 @@
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Saira';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Saira Light'), local('Saira-Light'), url(Fonts/Saira.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Saira Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Saira Condensed Light'), local('SairaCondensed-Light'), url(Fonts/Saira-Condensed.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: segoeui;
|
||||
src: url(Fonts/segoeui.woff) format('woff');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.body{
|
||||
overflow-y: hidden;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.scroll-div {
|
||||
position:absolute;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
top:0px;
|
||||
left:0px;
|
||||
vertical-align: top;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
color: #000;
|
||||
border-color: blue;
|
||||
background-color: transparent;
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.static-div {
|
||||
position: fixed;
|
||||
top:0px;
|
||||
left:18%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
background:transparent;
|
||||
border-width: 0px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.content-div {
|
||||
position:relative;
|
||||
vertical-align: top;
|
||||
height: 100%;
|
||||
width: 30%;
|
||||
left: 31.2%;
|
||||
color: #000;
|
||||
border-color: blue;
|
||||
background-color:transparent;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.post-placeholder {
|
||||
vertical-align: top;
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
color: #000;
|
||||
border-color: blue;
|
||||
background-color: transparent;
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.post-div {
|
||||
vertical-align: top;
|
||||
height: 300px;
|
||||
width: 100%;
|
||||
color: #000;
|
||||
border-color: blue;
|
||||
background-color: transparent;
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.new-post-div {
|
||||
vertical-align: top;
|
||||
height: 300px;
|
||||
width: 100%;
|
||||
color: #000;
|
||||
border-color: blue;
|
||||
background-color: transparent;
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.page-table {
|
||||
top:0px;
|
||||
left:18%;
|
||||
width: 64%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
background:transparent;
|
||||
border-width: 0px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.left-column {
|
||||
position:relative;
|
||||
top:0px;
|
||||
width: 20%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.middle-column {
|
||||
position:relative;
|
||||
top:0px;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.right-column {
|
||||
position:relative;
|
||||
top:0px;
|
||||
left:0px;
|
||||
width: 30%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.left-table {
|
||||
position:relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
border-width: 0px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.logo-cell {
|
||||
vertical-align: top;
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.menu-cell {
|
||||
vertical-align: top;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.profile-cell {
|
||||
vertical-align: bottom;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.middle-table {
|
||||
position:relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
border-width: 0px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.content-title-cell {
|
||||
vertical-align: top;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
border-width: 0px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.content-cell {
|
||||
vertical-align: top;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.right-table {
|
||||
position:relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
border-width: 0px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.search-cell {
|
||||
vertical-align: top;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.context-cell {
|
||||
vertical-align: top;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
border-width: 0px;
|
||||
background:transparent;
|
||||
}
|
||||
|
||||
.menu-table {
|
||||
position:relative;
|
||||
width: 100%;
|
||||
height: 65%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
border-width: 0px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.menu-item-cell {
|
||||
vertical-align: top;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.who-to-follow-table {
|
||||
position:relative;
|
||||
width: 100%;
|
||||
height: 30%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
border-width: 0px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.profile-to-follow-table {
|
||||
position:relative;
|
||||
width: 100%;
|
||||
height: 30%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
border-width: 0px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.profile-to-follow-span {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.profile-to-follow-button {
|
||||
float: right;
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.profile-to-unfollow-span {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.profile-to-unfollow-button {
|
||||
float: right;
|
||||
background-color: red;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
const reportWebVitals = onPerfEntry => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default reportWebVitals;
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import ExampleReducer from './slices/Globals'
|
||||
|
||||
export default configureStore({
|
||||
reducer:{
|
||||
example: ExampleReducer
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,27 @@
|
||||
import {createSlice} from '@reduxjs/toolkit'
|
||||
|
||||
export const Globals = createSlice({
|
||||
name: 'globals',
|
||||
initialState: {
|
||||
UI: {
|
||||
projects: {},
|
||||
schemas: {projectSchema: undefined},
|
||||
environment: undefined,
|
||||
clientNode: undefined,
|
||||
webApp: undefined
|
||||
},
|
||||
SA: {projects: {}}
|
||||
},
|
||||
reducers: {
|
||||
exampleAction: (state, action) => {
|
||||
state.userName = action.payload;
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Action creators are generated for each case reducer function
|
||||
export const {exampleAction} = Globals.actions
|
||||
|
||||
export default Globals.reducer
|
||||
|
||||
|
||||
@@ -58,13 +58,21 @@ exports.newDesktopRoot = function newDesktopRoot() {
|
||||
/*
|
||||
Setting up Secrets.
|
||||
*/
|
||||
SA.secrets = {
|
||||
array: require('./My-Secrets/Secrets.json').secrets,
|
||||
map: new Map()
|
||||
try {
|
||||
SA.secrets = {
|
||||
array: require('./My-Secrets/Secrets.json').secrets,
|
||||
map: new Map()
|
||||
}
|
||||
} catch (err) {
|
||||
SA.secrets = {
|
||||
array: [],
|
||||
map: new Map()
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < SA.secrets.array.length; i++) {
|
||||
let secret = SA.secrets.array[i]
|
||||
SA.secrets.map.set (secret.nodeCodeName, secret)
|
||||
SA.secrets.map.set(secret.nodeCodeName, secret)
|
||||
}
|
||||
|
||||
run()
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
exports.newDesktopRoot = function newDesktopRoot() {
|
||||
/*
|
||||
This module represents the execution root of the Desktop App.
|
||||
We use this module that is outside the Desktop folder to
|
||||
load all node dependencies and get them ready to the actual App.
|
||||
*/
|
||||
let thisObject = {
|
||||
run: run
|
||||
}
|
||||
|
||||
return thisObject
|
||||
|
||||
async function run(debugSettings) {
|
||||
/*
|
||||
The DK object is accessible everywhere at the Superalgos Desktop App.
|
||||
It provides access to all modules built for this App.
|
||||
*/
|
||||
global.DK = {}
|
||||
/*
|
||||
The SA object is accessible everywhere at the Superalgos Desktop App.
|
||||
It provides access to all modules built for Superalgos in general.
|
||||
*/
|
||||
global.SA = {}
|
||||
/* Load Environment Variables */
|
||||
let ENVIRONMENT = require('./Environment.js');
|
||||
let ENVIRONMENT_MODULE = ENVIRONMENT.newEnvironment()
|
||||
global.env = ENVIRONMENT_MODULE
|
||||
|
||||
if (debugSettings !== undefined && debugSettings.DESKTOP_APP_SIGNING_ACCOUNT !== undefined) {
|
||||
global.env.DESKTOP_APP_SIGNING_ACCOUNT = debugSettings.DESKTOP_APP_SIGNING_ACCOUNT
|
||||
}
|
||||
/*
|
||||
First thing is to load the project schema file.
|
||||
*/
|
||||
global.PROJECTS_SCHEMA = require(global.env.PATH_TO_PROJECT_SCHEMA)
|
||||
/*
|
||||
Setting up the modules that will be available, defined at the Project Schema file.
|
||||
*/
|
||||
let MULTI_PROJECT = require('./MultiProject.js');
|
||||
let MULTI_PROJECT_MODULE = MULTI_PROJECT.newMultiProject()
|
||||
MULTI_PROJECT_MODULE.initialize(DK, 'DK')
|
||||
MULTI_PROJECT_MODULE.initialize(SA, 'SA')
|
||||
/*
|
||||
Setting up external dependencies.
|
||||
*/
|
||||
SA.nodeModules = {
|
||||
fs: require('fs'),
|
||||
util: require('util'),
|
||||
path: require('path'),
|
||||
web3: require('web3'),
|
||||
ws: require('ws'),
|
||||
open: require('open'),
|
||||
http: require('http'),
|
||||
octokit: require("@octokit/rest"),
|
||||
simpleGit: require('simple-git'),
|
||||
nodeFetch: require('node-fetch')
|
||||
}
|
||||
/*
|
||||
Setting up Secrets.
|
||||
*/
|
||||
try {
|
||||
SA.secrets = {
|
||||
array: require('./My-Secrets/Secrets.json').secrets,
|
||||
map: new Map()
|
||||
}
|
||||
} catch (err) {
|
||||
SA.secrets = {
|
||||
array: [],
|
||||
map: new Map()
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < SA.secrets.array.length; i++) {
|
||||
let secret = SA.secrets.array[i]
|
||||
SA.secrets.map.set(secret.nodeCodeName, secret)
|
||||
}
|
||||
|
||||
run()
|
||||
|
||||
async function run() {
|
||||
DK.app = require('./DesktopReact/DesktopApp.js').newDesktopApp()
|
||||
await DK.app.run()
|
||||
console.log('Superalgos Desktop App is Running!')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,13 +33,14 @@ exports.newEnvironment = function () {
|
||||
DESKTOP_APP_SIGNING_ACCOUNT: 'Social-Trading-Desktop-App-1',
|
||||
DESKTOP_APP_MAX_OUTGOING_PEERS: 1,
|
||||
TASK_SERVER_APP_MAX_OUTGOING_PEERS: 1,
|
||||
TASK_SERVER_APP_MAX_OUTGOING_HEADS: 1,
|
||||
MOBILE_APP_SIGNING_ACCOUNT: 'Social-Trading-Mobile-App-1',
|
||||
SERVER_APP_SIGNING_ACCOUNT: 'Social-Trading-Server-App-1',
|
||||
PLATFORM_APP_SIGNING_ACCOUNT: 'Algo-Traders-Platform-1',
|
||||
P2P_NETWORK_NODE_SIGNING_ACCOUNT: 'P2P-Network-Node-1',
|
||||
P2P_NETWORK_NODE_MAX_INCOMING_CLIENTS: 1,
|
||||
P2P_NETWORK_NODE_MAX_INCOMING_PEERS: 2,
|
||||
P2P_NETWORK_NODE_MAX_OUTGOING_PEERS: 2
|
||||
P2P_NETWORK_NODE_MAX_INCOMING_PEERS: 0,
|
||||
P2P_NETWORK_NODE_MAX_OUTGOING_PEERS: 0
|
||||
}
|
||||
|
||||
if (process.env.DATA_PATH) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
exports.newMultiProject = function () {
|
||||
|
||||
/* TODO add init method to DK*/
|
||||
let thisObject = {
|
||||
initialize: initialize
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ exports.newNetworkNode = function newNetworkNode() {
|
||||
thisObject.socialGraphService = NT.projects.socialTrading.modules.socialGraph.newNetworkModulesSocialGraph()
|
||||
await thisObject.socialGraphService.initialize()
|
||||
/*
|
||||
The Sotrage deals with persisting the Social Graph.
|
||||
The Storage deals with persisting the Social Graph.
|
||||
*/
|
||||
thisObject.storage = NT.projects.socialTrading.modules.storage.newSocialTradingModulesStorage()
|
||||
thisObject.storage.initialize()
|
||||
|
||||
@@ -47,7 +47,7 @@ exports.newNetworkRoot = function newNetworkRoot() {
|
||||
fs: require('fs'),
|
||||
path: require('path'),
|
||||
util: require('util'),
|
||||
http: require('http'),
|
||||
http: require('http'),
|
||||
nodeFetch: require('node-fetch'),
|
||||
web3: require('web3'),
|
||||
ws: require('ws'),
|
||||
@@ -56,10 +56,18 @@ exports.newNetworkRoot = function newNetworkRoot() {
|
||||
/*
|
||||
Setting up Secrets.
|
||||
*/
|
||||
SA.secrets = {
|
||||
array: require('./My-Secrets/Secrets.json').secrets,
|
||||
map: new Map()
|
||||
try {
|
||||
SA.secrets = {
|
||||
array: require('./My-Secrets/Secrets.json').secrets,
|
||||
map: new Map()
|
||||
}
|
||||
} catch (err) {
|
||||
SA.secrets = {
|
||||
array: [],
|
||||
map: new Map()
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < SA.secrets.array.length; i++) {
|
||||
let secret = SA.secrets.array[i]
|
||||
SA.secrets.map.set(secret.nodeCodeName, secret)
|
||||
|
||||
@@ -210,7 +210,7 @@ exports.newGithubServer = function newGithubServer() {
|
||||
token = unescape(token)
|
||||
const repository = 'Superalgos'
|
||||
|
||||
/* Unsavping # */
|
||||
/* Unsaving # */
|
||||
for (let i = 0; i < 10; i++) {
|
||||
commitMessage = commitMessage.replace('_SLASH_', '/')
|
||||
commitMessage = commitMessage.replace('_HASHTAG_', '#')
|
||||
@@ -331,6 +331,7 @@ exports.newGithubServer = function newGithubServer() {
|
||||
let lastPage = false
|
||||
|
||||
while (lastPage === false) {
|
||||
page++
|
||||
await PL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
|
||||
let listResponse = await octokit.rest.pulls.listFiles({
|
||||
owner: owner,
|
||||
@@ -365,8 +366,8 @@ exports.newGithubServer = function newGithubServer() {
|
||||
if (await validateUserProfileSigningAccountsDoesNotBelongtoAnotherUserProfile() === false) { continue }
|
||||
|
||||
/*
|
||||
TODO: We need to check before merging a User Profile that none of id of the node of the profile hierearchy exists at any other
|
||||
User Profile already at the repository, to avoid atacts of users highjacking references of other user profiles.
|
||||
TODO: We need to check before merging a User Profile that none of id of the node of the profile hierarchy exists at any other
|
||||
User Profile already at the repository, to avoid attacks of users hijacking references of other user profiles.
|
||||
*/
|
||||
|
||||
if (await mergePullRequest() === false) {
|
||||
|
||||
@@ -1588,7 +1588,7 @@ exports.newHttpInterface = function newHttpInterface() {
|
||||
try {
|
||||
SA.projects.foundations.utilities.httpResponses.respondWithContent(JSON.stringify(error), httpResponse)
|
||||
} catch (err) {
|
||||
// we just try to reponnd to the web app, but maybe the response has already been sent.
|
||||
// we just try to respond to the web app, but maybe the response has already been sent.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2231,7 +2231,7 @@ exports.newHttpInterface = function newHttpInterface() {
|
||||
break
|
||||
case 'Storage': {
|
||||
let pathToFile = httpRequest.url.substring(9)
|
||||
/* Unsavping # */
|
||||
/* Unsaving # */
|
||||
for (let i = 0; i < 10; i++) {
|
||||
pathToFile = pathToFile.replace('_HASHTAG_', '#')
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ function newFrame() {
|
||||
|
||||
function isThisPointHere(point, outsideViewPort, dontTransform, padding) {
|
||||
// The second parameter is useful when you want to check a point that you already know is outside the viewport.
|
||||
// The padding is a distance to the borders of the container. Can be either negative (outside, to increase the container size) or positive (inside to decrease the cointainer size)
|
||||
// The padding is a distance to the borders of the container. Can be either negative (outside, to increase the container size) or positive (inside to decrease the container size)
|
||||
|
||||
if (padding === undefined) { padding = 0 }
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* FlexSearch.js v0.7.2 (Bundle)
|
||||
* Copyright 2018-2021 Nextapps GmbH
|
||||
* Author: Thomas Wilkerling
|
||||
* Licence: Apache-2.0
|
||||
* License: Apache-2.0
|
||||
* https://github.com/nextapps-de/flexsearch
|
||||
*/
|
||||
(function _f(self){'use strict';try{if(module)self=module}catch(e){}self._factory=_f;var t;function u(a){return"undefined"!==typeof a?a:!0}function aa(a){const b=Array(a);for(let c=0;c<a;c++)b[c]=v();return b}function v(){return Object.create(null)}function ba(a,b){return b.length-a.length}function x(a){return"string"===typeof a}function C(a){return"object"===typeof a}function D(a){return"function"===typeof a};function ca(a,b){var c=da;if(a&&(b&&(a=E(a,b)),this.H&&(a=E(a,this.H)),this.J&&1<a.length&&(a=E(a,this.J)),c||""===c)){a=a.split(c);if(this.filter){b=this.filter;c=a.length;const d=[];for(let e=0,f=0;e<c;e++){const g=a[e];g&&!b[g]&&(d[f++]=g)}a=d}return a}return a}const da=/[\p{Z}\p{S}\p{P}\p{C}]+/u,ea=/[\u0300-\u036f]/g;
|
||||
|
||||
@@ -76,9 +76,9 @@
|
||||
function drawSplashText() {
|
||||
const splashTextArray = [
|
||||
{
|
||||
"splashTextLine1Html": '<span class="credits-red">Every month, 10M SA tokens, with a market value of more than 500,000 USD are distributed between everyone contributing to the project during that month. </span>'
|
||||
"splashTextLine1Html": '<span class="credits-red">Every month, the project distributes 10M SA tokens with an approximate market value of $500,000 among contributors. </span>'
|
||||
,
|
||||
"splashTextLine2Html": 'In October 2021 we counted around 50 contributors, with an average mining reward of 10,000 USD for each one. Contributing to Superalgos now is like mining Bitcoin in 2010 where there were no more than 50 people mining it. Ask the community moderators how to join a team an participate in the next distribution.'
|
||||
"splashTextLine2Html": 'The October 2021 distribution counted around 50 contributors, with an average mining reward of $10,000 each. Contributing to Superalgos today is like mining Bitcoin in 2010! Ask Community Mods how to join a team to participate in the next distribution!'
|
||||
,
|
||||
"splashTextLine3Html": ''
|
||||
} /*,
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
"language": "ES",
|
||||
"text": "La jerarquia trading engine es la estructura de datos utilizada por el robot comercial para mantener la información en tiempo de ejecución altamente accesible y expuesta a otros.",
|
||||
"updated": 1624452474129
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Trading Engine - это структура данных, используемая торговым ботом для обеспечения высокого доступа к информации во время выполнения и ее доступности для других торговых ботов.",
|
||||
"updated": 1638338951505
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -17,50 +22,127 @@
|
||||
},
|
||||
{
|
||||
"style": "Text",
|
||||
"text": "You will use this hierarchy for two main purposes:"
|
||||
"text": "You will use this hierarchy for two main purposes:",
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Вы будете использовать эту иерархию для двух основных целей:",
|
||||
"updated": 1638338966721
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "List",
|
||||
"text": "To let your trading systems access the information processed by the trading bot. This allows strategies to keep track of and react to current and past events—including those involving the exchange, such as orders placed or filled—as the bot is running."
|
||||
"text": "To let your trading systems access the information processed by the trading bot. This allows strategies to keep track of and react to current and past events—including those involving the exchange, such as orders placed or filled—as the bot is running.",
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Чтобы позволить вашим торговым системам получить доступ к информации, обрабатываемой торговым ботом. Это позволяет стратегиям отслеживать и реагировать на текущие и прошлые события, в том числе связанные с обменом, такие как размещенные или исполненные заявки, во время работы бота.",
|
||||
"updated": 1638339042173
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "List",
|
||||
"text": "To keep track of the actions of the trading bot via the design space visual environment and panels over the charts. By analyzing runtime information, you may gain a detailed understanding of what happens, when, and why, throughout a trading session."
|
||||
"text": "To keep track of the actions of the trading bot via the design space visual environment and panels over the charts. By analyzing runtime information, you may gain a detailed understanding of what happens, when, and why, throughout a trading session.",
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Для отслеживания действий торгового бота через визуальную среду Design Space и панели над графиками. Анализируя информацию о времени выполнения, вы можете получить подробное представление о том, что происходит, когда и почему в течение торговой сессии.",
|
||||
"updated": 1638339093835
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Title",
|
||||
"text": "Frequently Asked Questions"
|
||||
"text": "Frequently Asked Questions",
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Часто задаваемые вопросы",
|
||||
"updated": 1638339158409
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Subtitle",
|
||||
"text": "Can a Trading Engine be shared between two Trading Bots?"
|
||||
"text": "Can a Trading Engine be shared between two Trading Bots?",
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Может ли Trading Engine использоваться совместно двумя торговыми ботами?",
|
||||
"updated": 1638339236191
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Text",
|
||||
"text": "The same trading engine can be used from multiple trading sessions, because it is just a node-based representation of a data structure.",
|
||||
"updated": 1621987910896
|
||||
"updated": 1621987910896,
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Один и тот же торговый движок можно использовать в нескольких торговых сессиях, потому что это просто представление структуры данных на основе узлов.",
|
||||
"updated": 1638339266880
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Text",
|
||||
"text": "Every time a Task is ran, a snapshot of that data structure, together with a snapshot of the Network and the Bots definitions at their Data Mine is sent from the UI to the Task Server, and from there is consumed by the running Bot. That means that a second Bot can receive the same Trading Engine snapshot and it will never know if other bots were fed with the same information or not.",
|
||||
"updated": 1621987965915
|
||||
"updated": 1621987965915,
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Каждый раз, когда запускается задача Task, снимок этой структуры данных вместе со снимком сети и определениями ботов в их Data Mine отправляется из UI на сервер задач Task Server, а оттуда используется запущенным ботом. Это означает, что второй бот может получить тот же снимок состояния Trading Engine, и он никогда не узнает, были ли другие боты загружены той же информацией или нет.",
|
||||
"updated": 1638339437677
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Subtitle",
|
||||
"text": "Do I need to change something at the Trading Engine?"
|
||||
"text": "Do I need to change something at the Trading Engine?",
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Нужно ли мне что-то менять в Trading Engine?",
|
||||
"updated": 1638339586902
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Text",
|
||||
"text": "You don't need to physically change anything inside trading engine via the UI. The trading engine is a visual representation of the data structure that the trading system uses to store its values, so to speak."
|
||||
"text": "You don't need to physically change anything inside trading engine via the UI. The trading engine is a visual representation of the data structure that the trading system uses to store its values, so to speak.",
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Вам не нужно ничего физически менять внутри Trading Engine через UI. Trading Engine - это визуальное представление структуры данных, которую торговая система использует, так сказать, для хранения своих значений.",
|
||||
"updated": 1638339666496
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Subtitle",
|
||||
"text": "Can I change the values inside the Trading Engine?"
|
||||
"text": "Can I change the values inside the Trading Engine?",
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Могу ли я изменить значения в Trading Engine?",
|
||||
"updated": 1638339692515
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Text",
|
||||
"text": "You access the Trading Engine from within your trading system if there is something in there you would like to change. This is the whole purpose of the User Defined Code node.",
|
||||
"updated": 1621988418300
|
||||
"updated": 1621988418300,
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Вы получаете доступ к Trading Engine из своей торговой системы, если в ней есть что-то, что вы хотели бы изменить. В этом вся цель узла User Defined Code.",
|
||||
"updated": 1638339838848
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7,6 +7,11 @@
|
||||
"language": "ES",
|
||||
"text": "Un trading mine es una jerarquía que contiene definiciones de trading bots y sus conspiradores. Estas definiciones constituyen los programas reales de dichos bots: el código fuente.",
|
||||
"updated": 1628906153772
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Trading Mine - это иерархия, которая содержит определения торговых ботов и их плоттеры. Эти определения составляют фактические программы таких ботов - исходный код.",
|
||||
"updated": 1638338598326
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -17,16 +22,37 @@
|
||||
},
|
||||
{
|
||||
"style": "Text",
|
||||
"text": "Altough Superalgos is starting out with a single trading bot, the system is prepared to feature as many trading bots as developers are willing to create. As such, a trading mine is a catalog of trading bots."
|
||||
"text": "Altough Superalgos is starting out with a single trading bot, the system is prepared to feature as many trading bots as developers are willing to create. As such, a trading mine is a catalog of trading bots.",
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Хотя Superalgos начинает с одного торгового бота, система готова включать в себя столько торговых ботов, сколько разработчики готовы создать. Таким образом, Trading Mine представляет собой каталог торговых ботов.",
|
||||
"updated": 1638338508685
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Text",
|
||||
"text": "Notice that, in the context of Superalgos, a trading bot has nothing to do with the trading logic. Think of trading logic as the business rules which—in Superalgos—are defined in trading systems. Anyone may build a Trading System, meaning that creating trading systems and the trading strategies within does not require coding.",
|
||||
"updated": 1609861342619
|
||||
"updated": 1609861342619,
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Обратите внимание, что в контексте Superalgos торговый бот не имеет ничего общего с торговой логикой. Думайте о торговой логике как о бизнес-правилах, которые - в Superalgos - определены в торговых системах. Любой может создать торговую систему, а это означает, что создание торговых систем и торговых стратегий внутри нее не требует программирования.",
|
||||
"updated": 1638338537661
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Text",
|
||||
"text": "Trading mines work similarly to data mines in the sense that they provide the same kind of tools to structure the definitions of bots, including process definitions, product definitions, and plotters."
|
||||
"text": "Trading mines work similarly to data mines in the sense that they provide the same kind of tools to structure the definitions of bots, including process definitions, product definitions, and plotters.",
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Trading Mine работает аналогично Data Mine в том смысле, что они предоставляют те же инструменты для структурирования определений ботов, включая определения процессов, определения продуктов и плоттеры.",
|
||||
"updated": 1638338689173
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Block",
|
||||
@@ -35,7 +61,14 @@
|
||||
{
|
||||
"style": "Title",
|
||||
"text": "Trading Mine Configuration",
|
||||
"updated": 1609861240385
|
||||
"updated": 1609861240385,
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Настройка Trading Mine",
|
||||
"updated": 1638338789550
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -10,8 +10,8 @@
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Торговая система - это структура, управляющая логикой низкого уровня, которая служит для структурирования процессов и методов, используемых для реализации и развертывания торговых стратегий.",
|
||||
"updated": 1636194401995
|
||||
"text": "Trading System - это структура, управляющая логикой низкого уровня, которая служит для структурирования процессов и методов, используемых для реализации и развертывания торговых стратегий.",
|
||||
"updated": 1638338041020
|
||||
},
|
||||
{
|
||||
"language": "DE",
|
||||
@@ -33,6 +33,11 @@
|
||||
"language": "ES",
|
||||
"text": "En términos prácticos, un Trading System es una disposición jerárquica que organiza los aspectos procesables de su plan de inversión. Esta jerarquía contiene definiciones respecto a un sinnúmero de estrategias de trading, todas operando en el mismo mercado y compartiendo la misma asignación de capital inicial.",
|
||||
"updated": 1637880963165
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "С практической точки зрения торговая система - это иерархическая структура, организующая практические аспекты вашего инвестиционного плана. Иерархия содержит определения относительно любого количества торговых стратегий, работающих на одном рынке и имеющих одинаковое начальное распределение капитала.",
|
||||
"updated": 1638338066276
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -44,6 +49,11 @@
|
||||
"language": "ES",
|
||||
"text": "Se utiliza un Trading System para definir estrategias acorde al protocolo de Superalgos, dividiendo a éstas en cuatro etapas: activación, apertura, gestión y cierre.",
|
||||
"updated": 1637880976135
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Вы используете торговую систему для определения стратегий в соответствии с протоколом Superalgos, разделяя стратегии на четыре этапа: запуск, открытие, управление и закрытие.",
|
||||
"updated": 1638338085462
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -55,6 +65,11 @@
|
||||
"language": "ES",
|
||||
"text": "El concepto de describir estrategias en etapas es fundamental para el aspecto metódico del Trading System, ya que provee la infraestructura necesaria para ejecutar cualquier estrategia con la misma infraestructura. Esto contribuye a desarrollar Trading Systems escalables, que pueden alcanzar innumerable cantidad de estrategias.",
|
||||
"updated": 1637880986625
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Концепция поэтапного описания стратегий является фундаментальной для методического аспекта торговой системы, поскольку она обеспечивает основу для запуска каждой стратегии с одной и той же структурой, что способствует разработке масштабируемых торговых систем, которые могут расширяться до любого количества стратегий.",
|
||||
"updated": 1638338107057
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -66,6 +81,11 @@
|
||||
"language": "ES",
|
||||
"text": "Cuando un Trading System ofrece más de una estrategia, la primera estrategia tiene prioridad sobre la segunda, la segunda sobre la tercera, y así sucesivamente. Esto significa que las estrategias son analizadas de manera secuencial. Cuando una estrategia dada es activada, las estrategias restantes no serán analizadas hasta que ésta se desactive. En otras palabras, cuando se implementan múltiples estrategias bajo un único Trading System, solo una estrategia puede operar en un momento dado, y la prioridad está dada por el orden especificado en el Nodo del Trading System.",
|
||||
"updated": 1637881003253
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Когда торговая система использует более одной стратегии, первая стратегия имеет приоритет над второй, вторая над третьей и так далее. Это означает, что стратегии оцениваются последовательно. Когда данная стратегия срабатывает, оставшиеся в очереди стратегии больше не оцениваются, пока стратегия не сработает. Другими словами, когда несколько стратегий развернуты в одной торговой системе, только одна стратегия может торговать в любой момент времени, а приоритет отдается порядку, установленному вокруг узла торговой системы.",
|
||||
"updated": 1638338139127
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -77,6 +97,11 @@
|
||||
"language": "ES",
|
||||
"text": "Como complemento a lo anterior, si se desea configurar estrategias que operen sobre diferentes mercados, o estrategias que tengan la posibilidad de abrir posiciones de forma simultánea, entonces se las deberá configurar bajo Trading Systems distintos.",
|
||||
"updated": 1637881023208
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Как следствие вышесказанного, если вы хотите, чтобы стратегии работали на разных рынках, или вы хотите, чтобы стратегии могли открывать позиции одновременно, вы должны настроить эти стратегии в разных торговых системах.",
|
||||
"updated": 1638338156830
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ exports.newAlgorithmicTradingBotModulesOrdersSimulations = function (processInde
|
||||
case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': {
|
||||
if (tradingEngineOrder.orderStatistics.actualRate.value > tradingEngine.tradingCurrent.tradingEpisode.candle.max.value) {
|
||||
|
||||
const message = 'Simulating - Actual Rate Too Hight'
|
||||
const message = 'Simulating - Actual Rate Too High'
|
||||
let docs = {
|
||||
project: 'Foundations',
|
||||
category: 'Topic',
|
||||
|
||||
@@ -25,6 +25,8 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
let tradingPositionModuleObject = TS.projects.algorithmicTrading.botModules.tradingPosition.newAlgorithmicTradingBotModulesTradingPosition(processIndex)
|
||||
let tradingExecutionModuleObject = TS.projects.algorithmicTrading.botModules.tradingExecution.newAlgorithmicTradingBotModulesTradingExecution(processIndex)
|
||||
let announcementsModuleObject = TS.projects.socialBots.botModules.announcements.newSocialBotsBotModulesAnnouncements(processIndex)
|
||||
let outgoingTradingSignalsModuleObject = TS.projects.tradingSignals.modules.outgoingTradingSignals.newTradingSignalsModulesOutgoingTradingSignals(processIndex)
|
||||
let portfolioManagerClient = TS.projects.portfolioManagement.modules.portfolioManagerClient.newPortfolioManagementModulesPortfolioManagerClient(processIndex)
|
||||
let snapshotsModuleObject = TS.projects.algorithmicTrading.botModules.snapshots.newAlgorithmicTradingBotModulesSnapshots(processIndex)
|
||||
let tradingEpisodeModuleObject = TS.projects.algorithmicTrading.botModules.tradingEpisode.newAlgorithmicTradingBotModulesTradingEpisode(processIndex)
|
||||
|
||||
@@ -97,15 +99,15 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
tradingPositionModuleObject.cycleBasedStatistics()
|
||||
}
|
||||
|
||||
function runTriggerStage() {
|
||||
async function runTriggerStage() {
|
||||
/*
|
||||
We check if we will be triggering on, off or taking position.
|
||||
*/
|
||||
checkTriggerOn()
|
||||
checkTriggerOff()
|
||||
checkTakePosition()
|
||||
await checkTriggerOn()
|
||||
await checkTriggerOff()
|
||||
await checkTakePosition()
|
||||
|
||||
function checkTriggerOn() {
|
||||
async function checkTriggerOn() {
|
||||
if (
|
||||
tradingEngine.tradingCurrent.strategy.index.value === tradingEngine.tradingCurrent.strategy.index.config.initialValue
|
||||
) {
|
||||
@@ -137,7 +139,12 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
passed = tradingSystem.checkConditions(situation, passed)
|
||||
|
||||
tradingSystem.values.push([situation.id, passed])
|
||||
|
||||
let response = await portfolioManagerClient.askPortfolioEventsManager(triggerStage.triggerOn, passed)
|
||||
passed = response.raiseEvent
|
||||
|
||||
if (passed) {
|
||||
|
||||
tradingSystem.highlights.push(situation.id)
|
||||
tradingSystem.highlights.push(triggerStage.triggerOn.id)
|
||||
tradingSystem.highlights.push(triggerStage.id)
|
||||
@@ -147,6 +154,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
/* Initialize this */
|
||||
tradingEngine.tradingCurrent.tradingEpisode.distanceToTradingEvent.triggerOn.value = 1
|
||||
|
||||
await outgoingTradingSignalsModuleObject.broadcastSignal(triggerStage.triggerOn)
|
||||
announcementsModuleObject.makeAnnouncements(triggerStage.triggerOn)
|
||||
|
||||
if (TS.projects.foundations.globals.processConstants.CONSTANTS_BY_PROCESS_INDEX_MAP.get(processIndex).SESSION_NODE.type === 'Backtesting Session') {
|
||||
@@ -156,9 +164,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
changeStageStatus('Trigger Stage', 'Open')
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,7 +173,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
}
|
||||
}
|
||||
|
||||
function checkTriggerOff() {
|
||||
async function checkTriggerOff() {
|
||||
if (tradingEngine.tradingCurrent.strategyTriggerStage.status.value === 'Open') {
|
||||
checkUserDefinedCode('Trigger Stage', 'Running', 'first');
|
||||
|
||||
@@ -188,12 +194,18 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
passed = tradingSystem.checkConditions(situation, passed)
|
||||
|
||||
tradingSystem.values.push([situation.id, passed])
|
||||
|
||||
let response = await portfolioManagerClient.askPortfolioEventsManager(triggerStage.triggerOff, passed)
|
||||
passed = response.raiseEvent
|
||||
|
||||
if (passed) {
|
||||
tradingSystem.highlights.push(situation.id)
|
||||
tradingSystem.highlights.push(triggerStage.triggerOff.id)
|
||||
tradingSystem.highlights.push(triggerStage.id)
|
||||
|
||||
tradingEngine.tradingCurrent.tradingEpisode.distanceToTradingEvent.triggerOff.value = 1
|
||||
|
||||
outgoingTradingSignalsModuleObject.broadcastSignal(triggerStage.triggerOff)
|
||||
announcementsModuleObject.makeAnnouncements(triggerStage.triggerOff)
|
||||
changeStageStatus('Trigger Stage', 'Closed', 'Trigger Off Event')
|
||||
tradingStrategyModuleObject.closeStrategy('Trigger Off')
|
||||
@@ -204,7 +216,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
}
|
||||
}
|
||||
|
||||
function checkTakePosition() {
|
||||
async function checkTakePosition() {
|
||||
if (
|
||||
tradingEngine.tradingCurrent.strategyTriggerStage.status.value === 'Open'
|
||||
) {
|
||||
@@ -212,7 +224,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
let triggerStage = strategy.triggerStage
|
||||
|
||||
tradingSystem.evalConditions(strategy, 'Take Position Event')
|
||||
tradingSystem.evalFormulas(strategy, 'Take Position Event')
|
||||
await tradingSystem.evalFormulas(strategy, 'Take Position Event')
|
||||
|
||||
if (triggerStage !== undefined) {
|
||||
if (triggerStage.takePosition !== undefined) {
|
||||
@@ -226,6 +238,10 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
passed = tradingSystem.checkConditions(situation, passed)
|
||||
|
||||
tradingSystem.values.push([situation.id, passed])
|
||||
|
||||
let response = await portfolioManagerClient.askPortfolioEventsManager(triggerStage.takePosition, passed)
|
||||
passed = response.raiseEvent
|
||||
|
||||
if (passed) {
|
||||
tradingSystem.highlights.push(situation.id)
|
||||
tradingSystem.highlights.push(triggerStage.takePosition.id)
|
||||
@@ -233,6 +249,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
|
||||
tradingPositionModuleObject.openPosition(situation.name)
|
||||
|
||||
outgoingTradingSignalsModuleObject.broadcastSignal(triggerStage.takePosition)
|
||||
announcementsModuleObject.makeAnnouncements(triggerStage.takePosition)
|
||||
|
||||
if (TS.projects.foundations.globals.processConstants.CONSTANTS_BY_PROCESS_INDEX_MAP.get(processIndex).SESSION_NODE.type === 'Backtesting Session') {
|
||||
@@ -259,14 +276,14 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
async function runOpenStage() {
|
||||
|
||||
checkIfWeNeedToAbortTheStage()
|
||||
runWhenStatusIsOpening()
|
||||
await runWhenStatusIsOpening()
|
||||
await runWhenStatusIsOpen()
|
||||
await runWhenStatusIsClosing()
|
||||
|
||||
function checkIfWeNeedToAbortTheStage() {
|
||||
/* Abort Open Stage Check */
|
||||
if (tradingEngine.tradingCurrent.strategyOpenStage.status.value === 'Open' &&
|
||||
(tradingEngine.tradingCurrent.strategyCloseStage.status.value === 'Opening' || tradingEngine.tradingCurrent.strategyCloseStage.status.value === 'Open') ){
|
||||
(tradingEngine.tradingCurrent.strategyCloseStage.status.value === 'Opening' || tradingEngine.tradingCurrent.strategyCloseStage.status.value === 'Open')) {
|
||||
/*
|
||||
if the Close stage is opened while the open stage is still open that means that
|
||||
we need to stop placing orders, check what happened to the orders already placed,
|
||||
@@ -276,7 +293,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
}
|
||||
}
|
||||
|
||||
function runWhenStatusIsOpening() {
|
||||
async function runWhenStatusIsOpening() {
|
||||
/* Opening Status Procedure */
|
||||
if (tradingEngine.tradingCurrent.strategyOpenStage.status.value === 'Opening') {
|
||||
/*
|
||||
@@ -298,7 +315,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
TS.projects.foundations.globals.processModuleObjects.MODULE_OBJECTS_BY_PROCESS_INDEX_MAP.get(processIndex).TRADING_ENGINE_MODULE_OBJECT.initializeNode(tradingEngine.exchangeOrders)
|
||||
|
||||
/* Entry Position size and rate */
|
||||
tradingSystem.evalFormulas(tradingSystemStage, 'Initial Targets')
|
||||
await tradingSystem.evalFormulas(tradingSystemStage, 'Initial Targets')
|
||||
tradingPositionModuleObject.initialTargets(tradingSystemStage, tradingEngineStage)
|
||||
initializeStageTargetSize()
|
||||
|
||||
@@ -321,7 +338,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
|
||||
/* Evaluate conditions and formulas so they are ready during the execution run */
|
||||
tradingSystem.evalConditions(executionNode, 'Open Execution')
|
||||
tradingSystem.evalFormulas(executionNode, 'Open Execution')
|
||||
await tradingSystem.evalFormulas(executionNode, 'Open Execution')
|
||||
|
||||
await tradingExecutionModuleObject.runExecution(
|
||||
executionNode,
|
||||
@@ -331,7 +348,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
|
||||
/* User Defined Code if runWhileAtStage==true */
|
||||
if (tradingEngine.tradingCurrent.strategyOpenStage.status.value === 'Open') {
|
||||
checkUserDefinedCode('Open Stage', 'Running', 'last');
|
||||
checkUserDefinedCode('Open Stage', 'Running', 'last');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -351,7 +368,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
and cancel the ones that were not.
|
||||
*/
|
||||
tradingSystem.evalConditions(tradingSystemStage, 'Open Execution')
|
||||
tradingSystem.evalFormulas(tradingSystemStage, 'Open Execution')
|
||||
await tradingSystem.evalFormulas(tradingSystemStage, 'Open Execution')
|
||||
|
||||
await tradingExecutionModuleObject.runExecution(
|
||||
executionNode,
|
||||
@@ -407,12 +424,12 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
}
|
||||
}
|
||||
|
||||
function runManageStage() {
|
||||
async function runManageStage() {
|
||||
|
||||
if (tradingEngine.tradingCurrent.tradingEpisode.cycle.value !== 'First') { return }
|
||||
|
||||
runWhenStatusIsOpening()
|
||||
runWhenStatusIsOpen()
|
||||
await runWhenStatusIsOpen()
|
||||
|
||||
function runWhenStatusIsOpening() {
|
||||
/* Opening Status Procedure */
|
||||
@@ -438,7 +455,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
}
|
||||
}
|
||||
|
||||
function runWhenStatusIsOpen() {
|
||||
async function runWhenStatusIsOpen() {
|
||||
/* Open Status Procedure */
|
||||
if (tradingEngine.tradingCurrent.strategyManageStage.status.value === 'Open') {
|
||||
checkUserDefinedCode('Manage Stage', 'Running', 'first');
|
||||
@@ -447,7 +464,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
|
||||
/* Evaluate all the stage conditions and formulas to have them ready */
|
||||
tradingSystem.evalConditions(manageStage, 'Manage Stage')
|
||||
tradingSystem.evalFormulas(manageStage, 'Manage Stage')
|
||||
await tradingSystem.evalFormulas(manageStage, 'Manage Stage')
|
||||
|
||||
/* Stop Loss Management */
|
||||
checkStopPhasesEvents()
|
||||
@@ -464,7 +481,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
|
||||
/* If User Defined Code exists check for runWhileAtStage */
|
||||
if (tradingEngine.tradingCurrent.strategyManageStage.status.value === 'Open') {
|
||||
checkUserDefinedCode('Manage Stage', 'Running', 'last');
|
||||
checkUserDefinedCode('Manage Stage', 'Running', 'last');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -696,6 +713,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
|
||||
tradingPositionModuleObject.updateTakeProfit(tradingEngine.tradingCurrent.position.takeProfit.takeProfitPhase.value + 1)
|
||||
|
||||
outgoingTradingSignalsModuleObject.broadcastSignal(nextPhaseEvent)
|
||||
announcementsModuleObject.makeAnnouncements(nextPhaseEvent)
|
||||
|
||||
/* Reset this counter */
|
||||
@@ -747,6 +765,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
continue
|
||||
}
|
||||
|
||||
outgoingTradingSignalsModuleObject.broadcastSignal(moveToPhaseEvent)
|
||||
announcementsModuleObject.makeAnnouncements(moveToPhaseEvent)
|
||||
|
||||
/* Reset this counter */
|
||||
@@ -810,11 +829,11 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
|
||||
async function runCloseStage() {
|
||||
|
||||
runWhenStatusIsOpening()
|
||||
await runWhenStatusIsOpening()
|
||||
await runWhenStatusIsOpen()
|
||||
await runWhenStatusIsClosing()
|
||||
|
||||
function runWhenStatusIsOpening() {
|
||||
async function runWhenStatusIsOpening() {
|
||||
|
||||
/* Opening Status Procedure */
|
||||
if (tradingEngine.tradingCurrent.strategyCloseStage.status.value === 'Opening') {
|
||||
@@ -834,7 +853,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
let tradingSystemStage = tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value].closeStage
|
||||
|
||||
/* Exit Position size and rate */
|
||||
tradingSystem.evalFormulas(tradingSystemStage, 'Initial Targets')
|
||||
await tradingSystem.evalFormulas(tradingSystemStage, 'Initial Targets')
|
||||
if (tradingPositionModuleObject.initialTargets(tradingSystemStage, tradingEngine.tradingCurrent.strategyCloseStage, 'Close Stage') === false) {
|
||||
//console.log("No Close-Stage Target Asset to Trade: Strategy Closing.");
|
||||
}
|
||||
@@ -866,7 +885,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
let executionNode = tradingSystemStage.closeExecution
|
||||
|
||||
tradingSystem.evalConditions(tradingSystemStage, 'Close Execution')
|
||||
tradingSystem.evalFormulas(tradingSystemStage, 'Close Execution')
|
||||
await tradingSystem.evalFormulas(tradingSystemStage, 'Close Execution')
|
||||
|
||||
await tradingExecutionModuleObject.runExecution(
|
||||
executionNode,
|
||||
@@ -876,7 +895,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
checkIfStageNeedsToBeClosed(tradingEngineStage, tradingSystemStage, 'Close Stage')
|
||||
/* If User Defined Code exists check for runWhileAtStage */
|
||||
if (tradingEngine.tradingCurrent.strategyCloseStage.status.value === 'Open') {
|
||||
checkUserDefinedCode('Close Stage', 'Running', 'last');
|
||||
checkUserDefinedCode('Close Stage', 'Running', 'last');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -896,7 +915,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
and cancel the ones that were not.
|
||||
*/
|
||||
tradingSystem.evalConditions(tradingSystemStage, 'Close Execution')
|
||||
tradingSystem.evalFormulas(tradingSystemStage, 'Close Execution')
|
||||
await tradingSystem.evalFormulas(tradingSystemStage, 'Close Execution')
|
||||
|
||||
await tradingExecutionModuleObject.runExecution(
|
||||
executionNode,
|
||||
@@ -1108,6 +1127,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
tradingSystem.highlights.push(closeStageEvent.id)
|
||||
tradingSystem.highlights.push(stage.id)
|
||||
|
||||
outgoingTradingSignalsModuleObject.broadcastSignal(closeStageEvent)
|
||||
announcementsModuleObject.makeAnnouncements(closeStageEvent)
|
||||
return true
|
||||
}
|
||||
@@ -1172,7 +1192,7 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
/* Recording the closing at the Trading Engine Data Structure */
|
||||
stage.end.value = tradingEngine.tradingCurrent.tradingEpisode.cycle.lastEnd.value
|
||||
stage.endRate.value = tradingEngine.tradingCurrent.tradingEpisode.candle.close.value
|
||||
|
||||
|
||||
if (stageName === 'Open Stage') {
|
||||
if (tradingEngine.tradingCurrent.strategyCloseStage.status.value !== 'Opening' && tradingEngine.tradingCurrent.strategyCloseStage.status.value !== 'Open' &&
|
||||
tradingEngine.tradingCurrent.strategyManageStage.status.value !== 'Opening' && tradingEngine.tradingCurrent.strategyManageStage.status.value !== 'Open') {
|
||||
@@ -1272,20 +1292,20 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
tradingSystemStage.userDefinedCode !== undefined) {
|
||||
if (status === 'Running' && when !== tradingSystemStage.userDefinedCode.config.whileAtStageWhenToRun) { return; }
|
||||
|
||||
switch(status) {
|
||||
case 'Open' : {
|
||||
switch (status) {
|
||||
case 'Open': {
|
||||
if (tradingSystemStage.userDefinedCode.config.runWhenEnteringStage) {
|
||||
tradingSystem.evalUserCode(tradingSystemStage, 'User Defined Code');
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'Running' : {
|
||||
case 'Running': {
|
||||
if (tradingSystemStage.userDefinedCode.config.runWhileAtStage) {
|
||||
tradingSystem.evalUserCode(tradingSystemStage, 'User Defined Code');
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'Closed' : {
|
||||
case 'Closed': {
|
||||
if (tradingSystemStage.userDefinedCode.config.runWhenExitingStage) {
|
||||
tradingSystem.evalUserCode(tradingSystemStage, 'User Defined Code');
|
||||
}
|
||||
@@ -1305,23 +1325,23 @@ exports.newAlgorithmicTradingBotModulesTradingStages = function (processIndex) {
|
||||
|
||||
/* getTradingSystemStage(): takes stage name returns stage object. */
|
||||
function getTradingSystemStage(stage) {
|
||||
switch(stage) {
|
||||
case 'Trigger Stage' : {
|
||||
return tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value].triggerStage;
|
||||
break;
|
||||
switch (stage) {
|
||||
case 'Trigger Stage': {
|
||||
return tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value].triggerStage;
|
||||
break;
|
||||
}
|
||||
case 'Open Stage': {
|
||||
return tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value].openStage;
|
||||
break;
|
||||
}
|
||||
case 'Manage Stage': {
|
||||
return tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value].manageStage;
|
||||
break
|
||||
}
|
||||
case 'Close Stage': {
|
||||
return tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value].closeStage;
|
||||
break;
|
||||
}
|
||||
}
|
||||
case 'Open Stage' : {
|
||||
return tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value].openStage;
|
||||
break;
|
||||
}
|
||||
case 'Manage Stage' : {
|
||||
return tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value].manageStage;
|
||||
break
|
||||
}
|
||||
case 'Close Stage' : {
|
||||
return tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value].closeStage;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ exports.newAlgorithmicTradingBotModulesTradingSystem = function (processIndex) {
|
||||
let dynamicIndicators
|
||||
|
||||
let tradingStagesModuleObject = TS.projects.algorithmicTrading.botModules.tradingStages.newAlgorithmicTradingBotModulesTradingStages(processIndex)
|
||||
let portfolioManagerClient = TS.projects.portfolioManagement.modules.portfolioManagerClient.newPortfolioManagementModulesPortfolioManagerClient(processIndex)
|
||||
|
||||
let taskParameters = {
|
||||
market: TS.projects.foundations.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName +
|
||||
@@ -70,12 +71,12 @@ exports.newAlgorithmicTradingBotModulesTradingSystem = function (processIndex) {
|
||||
evalNode(startingNode, 'Conditions', descendentOfNodeType)
|
||||
}
|
||||
|
||||
tradingSystem.evalFormulas = function (startingNode, descendentOfNodeType) {
|
||||
evalNode(startingNode, 'Formulas', descendentOfNodeType)
|
||||
tradingSystem.evalFormulas = async function (startingNode, descendentOfNodeType) {
|
||||
await evalNode(startingNode, 'Formulas', descendentOfNodeType)
|
||||
}
|
||||
|
||||
tradingSystem.evalUserCode = function (startingNode, descendentOfNodeType) {
|
||||
evalNode(startingNode, 'User Codes', descendentOfNodeType);
|
||||
evalNode(startingNode, 'User Codes', descendentOfNodeType);
|
||||
}
|
||||
|
||||
tradingSystem.addError = function (errorDataArray) {
|
||||
@@ -203,13 +204,13 @@ exports.newAlgorithmicTradingBotModulesTradingSystem = function (processIndex) {
|
||||
buildDynamicIndicators()
|
||||
|
||||
/* Run the Trigger Stage */
|
||||
tradingStagesModuleObject.runTriggerStage()
|
||||
await tradingStagesModuleObject.runTriggerStage()
|
||||
|
||||
/* Run the Open Stage */
|
||||
await tradingStagesModuleObject.runOpenStage()
|
||||
|
||||
/* Run the Manage Stage */
|
||||
tradingStagesModuleObject.runManageStage()
|
||||
await tradingStagesModuleObject.runManageStage()
|
||||
|
||||
/* Run the Close Stage */
|
||||
await tradingStagesModuleObject.runCloseStage()
|
||||
@@ -234,7 +235,7 @@ exports.newAlgorithmicTradingBotModulesTradingSystem = function (processIndex) {
|
||||
}
|
||||
}
|
||||
|
||||
function evalNode(node, evaluating, descendentOfNodeType, isDescendent) {
|
||||
async function evalNode(node, evaluating, descendentOfNodeType, isDescendent, parentNode) {
|
||||
if (node === undefined) { return }
|
||||
|
||||
/* Verify if this node is descendent of the specified node type */
|
||||
@@ -257,18 +258,18 @@ exports.newAlgorithmicTradingBotModulesTradingSystem = function (processIndex) {
|
||||
if (node.code !== undefined) {
|
||||
/* We will eval this formula */
|
||||
if (isDescendent === true) {
|
||||
evalFormula(node)
|
||||
await evalFormula(node, parentNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Here we check if there is a User Defined-Javascript Code to be evaluated: */
|
||||
if (node.type === 'Javascript Code' && evaluating === 'User Codes') {
|
||||
if (node.code !== undefined) {
|
||||
if (isDescendent === true) {
|
||||
evalJSCode(node);
|
||||
if (node.code !== undefined) {
|
||||
if (isDescendent === true) {
|
||||
evalJSCode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now we go down through all this node children */
|
||||
@@ -284,7 +285,7 @@ exports.newAlgorithmicTradingBotModulesTradingSystem = function (processIndex) {
|
||||
case 'node': {
|
||||
if (property.name !== previousPropertyName) {
|
||||
if (node[property.name] !== undefined) {
|
||||
evalNode(node[property.name], evaluating, descendentOfNodeType, isDescendent)
|
||||
await evalNode(node[property.name], evaluating, descendentOfNodeType, isDescendent, node)
|
||||
}
|
||||
previousPropertyName = property.name
|
||||
}
|
||||
@@ -294,7 +295,7 @@ exports.newAlgorithmicTradingBotModulesTradingSystem = function (processIndex) {
|
||||
if (node[property.name] !== undefined) {
|
||||
let nodePropertyArray = node[property.name]
|
||||
for (let m = 0; m < nodePropertyArray.length; m++) {
|
||||
evalNode(nodePropertyArray[m], evaluating, descendentOfNodeType, isDescendent)
|
||||
await evalNode(nodePropertyArray[m], evaluating, descendentOfNodeType, isDescendent, node)
|
||||
}
|
||||
}
|
||||
break
|
||||
@@ -357,7 +358,7 @@ exports.newAlgorithmicTradingBotModulesTradingSystem = function (processIndex) {
|
||||
TS.projects.foundations.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME, '[INFO] evalCondition -> value = ' + value)
|
||||
}
|
||||
|
||||
function evalFormula(node) {
|
||||
async function evalFormula(node, parentNode) {
|
||||
let value
|
||||
let errorMessage
|
||||
let docs
|
||||
@@ -365,6 +366,10 @@ exports.newAlgorithmicTradingBotModulesTradingSystem = function (processIndex) {
|
||||
try {
|
||||
TS.projects.foundations.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME, '[INFO] evalFormula -> ' + node.name + ' -> code = ' + node.code)
|
||||
value = eval(node.code)
|
||||
|
||||
let response = await portfolioManagerClient.askPortfolioFormulaManager(parentNode, value)
|
||||
value = response.value
|
||||
|
||||
} catch (err) {
|
||||
/*
|
||||
One possible error is that the formula references a .previous that is undefined. This
|
||||
@@ -423,29 +428,29 @@ exports.newAlgorithmicTradingBotModulesTradingSystem = function (processIndex) {
|
||||
}
|
||||
|
||||
function evalJSCode(node) {
|
||||
let value
|
||||
let errorMessage
|
||||
let docs
|
||||
let value
|
||||
let errorMessage
|
||||
let docs
|
||||
|
||||
try {
|
||||
value = eval(node.code);
|
||||
try {
|
||||
value = eval(node.code);
|
||||
|
||||
} catch (err) {
|
||||
value = 0
|
||||
errorMessage = err.message
|
||||
docs = {
|
||||
project: 'Foundations',
|
||||
category: 'Topic',
|
||||
type: 'TS LF Trading Bot Error - Evaluating User Code Error',
|
||||
placeholder: {}
|
||||
} catch (err) {
|
||||
value = 0
|
||||
errorMessage = err.message
|
||||
docs = {
|
||||
project: 'Foundations',
|
||||
category: 'Topic',
|
||||
type: 'TS LF Trading Bot Error - Evaluating User Code Error',
|
||||
placeholder: {}
|
||||
}
|
||||
TS.projects.education.utilities.docsFunctions.buildPlaceholder(docs, err, node.name, node.code, undefined)
|
||||
}
|
||||
TS.projects.education.utilities.docsFunctions.buildPlaceholder(docs, err, node.name, node.code, undefined)
|
||||
}
|
||||
|
||||
if (errorMessage !== undefined) {
|
||||
tradingSystem.addError([node.id, errorMessage, docs])
|
||||
TS.projects.foundations.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME, '[INFO] evalFormula -> errorMessage = ' + errorMessage)
|
||||
return
|
||||
}
|
||||
if (errorMessage !== undefined) {
|
||||
tradingSystem.addError([node.id, errorMessage, docs])
|
||||
TS.projects.foundations.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME, '[INFO] evalFormula -> errorMessage = ' + errorMessage)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -584,9 +584,10 @@
|
||||
}
|
||||
|
||||
function startLiveTrading() {
|
||||
// Testing:
|
||||
TS.projects.foundations.globals.processVariables.PM_COMMUNICATION_MODULE.beginListening();
|
||||
TS.projects.foundations.globals.processVariables.PM_COMMUNICATION_MODULE.getQuoteAssetBalance();
|
||||
/* Testing to be removed @PLUV: */
|
||||
/*TS.projects.foundations.globals.processVariables.PM_COMMUNICATION_MODULE.beginListening();
|
||||
TS.projects.foundations.globals.processVariables.PM_COMMUNICATION_MODULE.getQuoteAssetBalance();*/
|
||||
/* End Testing */
|
||||
return checkKey()
|
||||
}
|
||||
|
||||
|
||||
@@ -36,9 +36,9 @@
|
||||
"relatedUiObjectProject": "Community-Plugins"
|
||||
},
|
||||
{
|
||||
"action": "Save Plugin",
|
||||
"action": "Save Plugin File",
|
||||
"actionFunction": "payload.executeAction",
|
||||
"label": "Save Plugin",
|
||||
"label": "Save Plugin File",
|
||||
"relatedUiObject": "Plugin File",
|
||||
"actionProject": "Community-Plugins",
|
||||
"relatedUiObjectProject": "Community-Plugins"
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Иерархия плагинов определяет, какие плагины должны быть включены в текущую рабочую область. В контексте этой иерархии плагины представляют собой файлы JSON, содержащие описание иерархии, такой как шахта данных, торговая шахта, торговый механизм или торговая система.",
|
||||
"updated": 1636194488881
|
||||
"text": "Иерархия Plugins определяет, какие плагины должны быть включены в текущую рабочую область Workspace. В контексте этой иерархии плагины - это файлы JSON, содержащие описание иерархии, такой как Data Mine, Trading Mine, Trading Engine или Trading System.",
|
||||
"updated": 1638340280833
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -22,12 +22,26 @@
|
||||
},
|
||||
{
|
||||
"style": "Text",
|
||||
"text": "In the context of a community-driven project, a plugin is a device that serves to distribute community-generated intelligence. The project distributes several plugins contributed by the community."
|
||||
"text": "In the context of a community-driven project, a plugin is a device that serves to distribute community-generated intelligence. The project distributes several plugins contributed by the community.",
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "В контексте проекта, управляемого сообществом, плагин - это устройство, которое служит для распространения информации, созданной сообществом. Проект распространяет несколько плагинов, предоставленных сообществом.",
|
||||
"updated": 1638340321953
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Important",
|
||||
"text": "Hierarchies included in the workspace as plugins are not saved at the workspace level. As such, changes made in those hierarchies do not persist. If you wish to modify a plugin and keep working with the modified version in the future, you must either clone or backup the plugin and work on the resulting copy instead.",
|
||||
"updated": 1609866745275
|
||||
"updated": 1609866745275,
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Иерархии (Hierarchies), включенные в рабочую область как плагины, не сохраняются на уровне рабочей области. Таким образом, изменения, внесенные в эти иерархии, не сохраняются. Если вы хотите изменить плагин и продолжить работу с измененной версией в будущем, вы должны либо клонировать, либо сделать резервную копию плагина и вместо этого работать с полученной копией.",
|
||||
"updated": 1638340382678
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -39,9 +39,9 @@ function newCommunityPluginsActionSwitch() {
|
||||
}
|
||||
break
|
||||
case 'Add Specified Plugin Data Mine':
|
||||
{
|
||||
UI.projects.communityPlugins.functionLibraries.pluginsFunctions.addSpecifiedPluginDataMine(action.node, action.rootNodes)
|
||||
}
|
||||
{
|
||||
UI.projects.communityPlugins.functionLibraries.pluginsFunctions.addSpecifiedPluginDataMine(action.node, action.rootNodes)
|
||||
}
|
||||
break
|
||||
case 'Add Missing Plugin Trading Mines':
|
||||
{
|
||||
@@ -108,9 +108,14 @@ function newCommunityPluginsActionSwitch() {
|
||||
UI.projects.communityPlugins.functionLibraries.pluginsFunctions.disableSavingWithWorkspace(action.node, action.rootNodes, action.callBackFunction)
|
||||
}
|
||||
break
|
||||
case 'Save Plugin File':
|
||||
{
|
||||
UI.projects.communityPlugins.functionLibraries.pluginsFunctions.savePluginFile(action.node, action.rootNodes)
|
||||
}
|
||||
break
|
||||
case 'Save Plugin':
|
||||
{
|
||||
UI.projects.communityPlugins.functionLibraries.pluginsFunctions.savePlugin(action.node, action.rootNodes)
|
||||
UI.projects.communityPlugins.functionLibraries.pluginsFunctions.savePluginHierarchy(action.node, action.rootNodes)
|
||||
}
|
||||
break
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@ function newPluginsFunctionLibraryPluginsFunctions() {
|
||||
addMissingPluginApiMaps: addMissingPluginApiMaps,
|
||||
enableSavingWithWorkspace: enableSavingWithWorkspace,
|
||||
disableSavingWithWorkspace: disableSavingWithWorkspace,
|
||||
savePlugin: savePlugin,
|
||||
savePluginFile: savePluginFile,
|
||||
savePluginHierarchy: savePluginHierarchy,
|
||||
installAsPlugin: installAsPlugin
|
||||
}
|
||||
return thisObject
|
||||
@@ -283,8 +284,35 @@ function newPluginsFunctionLibraryPluginsFunctions() {
|
||||
callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE)
|
||||
}
|
||||
|
||||
function savePlugin(node, rootNodes) {
|
||||
UI.projects.communityPlugins.utilities.plugins.savePluginFile(node)
|
||||
function savePluginFile(pluginFile, rootNodes) {
|
||||
UI.projects.communityPlugins.utilities.plugins.savePluginFileAtClient(pluginFile)
|
||||
}
|
||||
|
||||
function savePluginHierarchy(node, rootNodes) {
|
||||
if (node.isPlugin !== true) { return }
|
||||
|
||||
let plugins = UI.projects.foundations.spaces.designSpace.workspace.getHierarchyHeadByNodeType('Plugins')
|
||||
let pluginProject = UI.projects.visualScripting.utilities.nodeChildren.findChildByCodeName(plugins, node.project)
|
||||
let pluginFolderName = UI.projects.communityPlugins.utilities.plugins.getPluginFolderNamesByNodeType(node.type)
|
||||
let pluginForlderNodeType = 'Plugin ' + pluginFolderName.replaceAll('-', ' ')
|
||||
let pluginFolderNode = UI.projects.visualScripting.utilities.nodeChildren.findChildByType(pluginProject, pluginForlderNodeType)
|
||||
|
||||
for (let i = 0; i < pluginFolderNode.pluginFiles.length; i++) {
|
||||
let pluginFile = pluginFolderNode.pluginFiles[i]
|
||||
let fileName = UI.projects.visualScripting.utilities.nodeConfig.loadConfigProperty(pluginFile.payload, 'fileName')
|
||||
let codeName = UI.projects.visualScripting.utilities.nodeConfig.loadConfigProperty(node.payload, 'codeName')
|
||||
if (codeName === fileName) {
|
||||
UI.projects.communityPlugins.utilities.plugins.savePluginFileAtClient(pluginFile)
|
||||
/*
|
||||
Show nice message.
|
||||
*/
|
||||
node.payload.uiObject.setInfoMessage(
|
||||
"Plugin Saved.",
|
||||
UI.projects.governance.globals.designer.SET_INFO_COUNTER_FACTOR
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function installAsPlugin(node, rootNodes) {
|
||||
@@ -301,6 +329,8 @@ function newPluginsFunctionLibraryPluginsFunctions() {
|
||||
return
|
||||
}
|
||||
|
||||
UI.projects.communityPlugins.utilities.plugins.savePluginFile(pluginFile)
|
||||
UI.projects.communityPlugins.utilities.plugins.savePluginFileAtClient(pluginFile)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@ function newPluginsUtilitiesPlugins() {
|
||||
addMissingPluginFiles: addMissingPluginFiles,
|
||||
addMissingPluginFile: addMissingPluginFile,
|
||||
getProjectName: getProjectName,
|
||||
savePluginFile: savePluginFile
|
||||
savePluginFileAtClient: savePluginFileAtClient
|
||||
}
|
||||
|
||||
return thisObject
|
||||
@@ -119,7 +119,7 @@ function newPluginsUtilitiesPlugins() {
|
||||
return config.codeName
|
||||
}
|
||||
|
||||
function savePluginFile(pluginFile) {
|
||||
function savePluginFileAtClient(pluginFile) {
|
||||
let project = UI.projects.visualScripting.utilities.nodeConfig.loadConfigProperty(pluginFile.payload, 'project')
|
||||
let fileName = UI.projects.visualScripting.utilities.nodeConfig.loadConfigProperty(pluginFile.payload, 'fileName')
|
||||
let folderName = UI.projects.visualScripting.utilities.nodeConfig.loadConfigProperty(pluginFile.payload, 'folderName')
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Шахта данных - это иерархия, содержащая определения датчиков и ботов-индикаторов, а также их плоттеров. Эти определения взяты из фактического кода таких ботов.\n",
|
||||
"updated": 1636192911774
|
||||
"text": "Data Mine - это иерархия, содержащая определения датчиков и ботов-индикаторов, а также их плоттеров. Эти определения взяты из фактического кода таких ботов.",
|
||||
"updated": 1638275063658
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -23,11 +23,35 @@
|
||||
},
|
||||
{
|
||||
"style": "Text",
|
||||
"text": "In a way, data mines are catalogs of bots that anyone may create. This hierarchy provides all the tools required to create sensors, indicators, and plotters, with minimal coding required."
|
||||
"text": "In a way, data mines are catalogs of bots that anyone may create. This hierarchy provides all the tools required to create sensors, indicators, and plotters, with minimal coding required.",
|
||||
"translations": [
|
||||
{
|
||||
"language": "ES",
|
||||
"text": "En cierto modo, los Data Mines son listas de bots que pueden ser creados por cualquier usuario. Esta jerarquía provee todas las herramientas necesarias para crear sensores, indicadores y gráficos, con un mínimo requerimiento de código.",
|
||||
"updated": 1638150280351
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "В каком-то смысле Data Mine - это каталоги ботов, которые может создать каждый. Эта иерархия предоставляет все инструменты, необходимые для создания датчиков, индикаторов и плоттеров с минимальным кодированием.",
|
||||
"updated": 1638339932741
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Text",
|
||||
"text": "Because the hierarchy provides definitions, it does not intervene in the execution of bots. Instead, the definitions in data mines are instantiated from the network hierarchy by bot instances. Bot instances and process instances are the ones running the code according to the definitions in the corresponding data mine."
|
||||
"text": "Because the hierarchy provides definitions, it does not intervene in the execution of bots. Instead, the definitions in data mines are instantiated from the network hierarchy by bot instances. Bot instances and process instances are the ones running the code according to the definitions in the corresponding data mine.",
|
||||
"translations": [
|
||||
{
|
||||
"language": "ES",
|
||||
"text": "Debido a que la jerarquía solo proporciona definiciones, no interfiere en la ejecución de Bots. Sino que las definiciones en los Data Mines son instanciados desde la jerarquía Network por las instancias de los Bots. Las instancias de los Bots y las instancias de procesos son las encargadas de ejecutar el código acorde a las definiciones del Data Mine correspondiente.",
|
||||
"updated": 1638150294294
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Поскольку иерархия предоставляет определения, она не вмешивается в выполнение ботов. Вместо этого определения в Data Mine создаются экземплярами ботов из сетевой иерархии. Экземпляры ботов и экземпляры процессов - это те, которые запускают код в соответствии с определениями в соответствующем хранилище данных.",
|
||||
"updated": 1638339974635
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Block",
|
||||
@@ -36,16 +60,37 @@
|
||||
{
|
||||
"style": "Title",
|
||||
"text": "Data Mine Configuration",
|
||||
"updated": 1609789108763
|
||||
"updated": 1609789108763,
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Конфигурирование Data Mine",
|
||||
"updated": 1638340008131
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Subtitle",
|
||||
"text": "Properties",
|
||||
"updated": 1609789116514
|
||||
"updated": 1609789116514,
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Свойства",
|
||||
"updated": 1638340048023
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "List",
|
||||
"text": "codeName is the name of the data mine as used within the code of the system."
|
||||
"text": "codeName is the name of the data mine as used within the code of the system.",
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "codeName - это имя хранилища данных, используемое в коде системы.",
|
||||
"updated": 1638340067608
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -65,7 +65,7 @@
|
||||
let exchangeClass
|
||||
/*
|
||||
This is what we are going to do hereL
|
||||
|
||||
|
||||
1. Parameters set by SA user at the Crypto Exchange node are extracted. There might be parameters for each supported method at the CCXT library.
|
||||
2. The CCXT class for the configured exchange is instantiated, with whatever options where configured.
|
||||
|
||||
@@ -75,6 +75,12 @@
|
||||
statusDependencies = pStatusDependencies;
|
||||
|
||||
exchangeId = TS.projects.foundations.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.codeName
|
||||
/*
|
||||
maxRate - sets the maximum number of OHCLV that is pulled before the data is saved.
|
||||
This is only to be used when the exchange is kicking out the data-mine randomly and alows the user to
|
||||
save the data more often allowing for the data mining to move forward.
|
||||
*/
|
||||
maxRate = TS.projects.foundations.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.maxRate
|
||||
|
||||
/* Applying the parameters defined by the user at the Exchange Node Config */
|
||||
if (TS.projects.foundations.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.API !== undefined) {
|
||||
@@ -578,6 +584,16 @@
|
||||
|
||||
let currentDate = new Date(since)
|
||||
|
||||
/*
|
||||
maxRate - sets the maximum number of OHCLV that is pulled before the data is saved.
|
||||
maxRate should only be used when the exchange is kicking out the data-mine randomly and alows the user to
|
||||
save the data more often allowing for the data mining to move forward.
|
||||
Check if we don't have a maxRate parameter and use global parameter instead
|
||||
*/
|
||||
if (!maxRate) {
|
||||
maxRate = MAX_OHLCVs_PER_EXECUTION
|
||||
}
|
||||
|
||||
/*
|
||||
If we've pulled less OHLCVs than the limit, this either means:
|
||||
(1) we reached the current date and are now pulling the latest
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"type": "Current projects",
|
||||
"definition": {
|
||||
"text": "The table below contains all current projects to which you can add new docs-articles. Feel free to add projects if they are missing.",
|
||||
"updated": 1638112235073
|
||||
},
|
||||
"paragraphs": [
|
||||
{
|
||||
"style": "Table",
|
||||
"text": "|Project|Project Explanation|\n|Algorithmic-Trading|Project Explanation|\n|Community-Plugins|Project Explanation|\n|Data-Mining|Project Explanation|\n|Education|This Project is about building up and maintaining the Docs (-Articles)|\n|Ethereum|Project Explanation|\n|Foundations|Project Explanation|\n|Governance|Manages and organizes the Superalgos Project (All of us, the community)|\n|Machine-Learning|Project Explanation|\n|Network|Project Explanation|\n|Social-Bots|Project Explanation|\n|Social-Trading|Project Explanation|\n|TensorFlow|Project Explanation|\n|Visual-Scripting|Project Explanation|",
|
||||
"updated": 1638116155258,
|
||||
"translations": [
|
||||
{
|
||||
"language": "DE",
|
||||
"text": "|Project|Project Explanation|\n|Algorithmic-Trading|Project Explanation|\n|Community-Plugins|Project Explanation|\n|Data-Mining|Project Explanation|\n|Education|This Project is about building up and maintaining the Docs (-Articles)|\n|Ethereum|Project Explanation|\n|Foundations|Project Explanation|\n|Governance|Manages and organizes the Superalgos Project (All of us, the community)|\n|Machine-Learning|Project Explanation|\n|Network|Project Explanation|\n|Social-Bots|Project Explanation|\n|Social-Trading|Project Explanation|\n|TensorFlow|Project Explanation|\n|Visual-Scripting|Project Explanation|",
|
||||
"updated": 1638115499301
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Text",
|
||||
"text": "Created and maintained by gitforkgit.",
|
||||
"updated": 1638112659687
|
||||
},
|
||||
{
|
||||
"style": "Text",
|
||||
"text": ""
|
||||
},
|
||||
{
|
||||
"style": "Text",
|
||||
"text": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"type": "Technical term overview",
|
||||
"definition": {
|
||||
"text": "This article was created to show you which terms you should not translate because they are a fixed term. An example is the word Node. Created and maintained by gitforkgit.",
|
||||
"updated": 1638052921801,
|
||||
"translations": [
|
||||
{
|
||||
"language": "DE",
|
||||
"text": "This article was created to show you which terms you should not translate because they are a fixed term. An example is the word Node. Created and maintained by gitforkgit ",
|
||||
"updated": 1638052964966
|
||||
}
|
||||
]
|
||||
},
|
||||
"paragraphs": [
|
||||
{
|
||||
"style": "Text",
|
||||
"text": "One of our goals is to make Superalgos available for everyone for free and open source forever. For this, translations are extremely important and essential. However, there are some technical terms that should not be translated. Because otherwise it could be difficult when describing problems to others, start contributing, understanding superalgos...",
|
||||
"updated": 1638052177612
|
||||
},
|
||||
{
|
||||
"style": "Text",
|
||||
"text": "\"Below here is a table in which the terms are listed. Here the translators can orient themselves and each Superalgos user add terms of which he thinks that they should not be translated. If you translate articles please look here from time to time to see which terms you should leave as they are in the original. Translations in brackets behind the terms themselves are of course legitimate and welcome by readers of the articles.",
|
||||
"updated": 1638052195441
|
||||
},
|
||||
{
|
||||
"style": "Success",
|
||||
"text": "Feel free to add Terms in the table!",
|
||||
"updated": 1638052209974
|
||||
},
|
||||
{
|
||||
"style": "Note",
|
||||
"text": "Terms that should remain original in the translations of articles:",
|
||||
"updated": 1638052228369
|
||||
},
|
||||
{
|
||||
"style": "Table",
|
||||
"text": "|Workspace Terms|Node Terms|Other Technical Terms|Workspace Terms translation|Node Terms translation|Other Technical Terms translation|\n|Pin/Unpin|Node|add terms here|add Term translation here|add Term translation here|add Term translation here|\n|add terms here|Parent Node|add terms here|add Term translation here|add Term translation here|add Term translation here|\n|add terms here|Child Node|add terms here|add Term translation here|add Term translation here|add Term translation here|\n|add terms here|add terms here|add terms here|add Term translation here|add Term translation here|add Term translation here|",
|
||||
"updated": 1638052333924
|
||||
},
|
||||
{
|
||||
"style": "List",
|
||||
"text": "Workspace terms -> all Technical Terms related to the Workspace",
|
||||
"updated": 1638487168731
|
||||
},
|
||||
{
|
||||
"style": "List",
|
||||
"text": "node Terms -> all Technical Terms related to Nodes "
|
||||
},
|
||||
{
|
||||
"style": "List",
|
||||
"text": "Other Technical Terms -> all Technical Terms that dont fit the other categories"
|
||||
},
|
||||
{
|
||||
"style": "Note",
|
||||
"text": "In the columns with translation as their header you can add the translation into your language, if your language is selected in the footer, otherwise the translations would be globally in the table what isn`t the Goal.",
|
||||
"updated": 1638486873653
|
||||
},
|
||||
{
|
||||
"style": "Text",
|
||||
"text": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -18,8 +18,8 @@
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Иерархия данного пособия содержит свободное расположение Tutorial Topic и узлов Tutorial Step, которые вместе составляют это учебное пособие. Каждый узел в иерархии, включая родительский узел пособия, представляет собой HTML-страницу, которая может быть наложена поверх Design Space и Charting Space.",
|
||||
"updated": 1635509527381
|
||||
"text": "Иерархия Tutorial содержит свободное расположение Tutorial Topic и узлов Tutorial Step, которые вместе составляют это учебное пособие. Каждый узел в иерархии, включая родительский узел пособия, представляет собой HTML-страницу, которая может быть наложена поверх Design Space и Charting Space.",
|
||||
"updated": 1638340460215
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -38,6 +38,11 @@
|
||||
"text": "Die Nodes in der Hierarchie können frei angeordnet werden, das heißt es können beliebig viele Tutorial-Themen (topic) und Tutorial-Steps vorhanden sein. Tutorial Themen können Tutorial Schritte aber auch andere Tutorial Themen enthalten. Dies erlaubt es verschachtelte Themen als Unterthemen verwendet werden.",
|
||||
"updated": 1633975991952,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Узлы в иерархии могут быть расположены произвольно, что означает, что может быть столько обучающих тем и обучающих шагов, сколько необходимо. Темы учебного пособия могут содержать этапы обучения, а также другие разделы учебного пособия. Это позволяет использовать вложенные темы в качестве подтем.",
|
||||
"updated": 1638340558172
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -51,6 +56,11 @@
|
||||
"text": "Das System wertet die Hierarchie - wie üblich - im Uhrzeigersinn aus, beginnend mit dem Knoten rechts vom übergeordneten Tutorial-Knoten, zum tiefsten Knoten jedes Zweigs geht, bevor es zum nächsten geht.",
|
||||
"updated": 1633976627129,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Система, как обычно, оценивает иерархию по часовой стрелке, начиная с узла справа от родительского узла Tutorial, и переходит к самому глубокому узлу каждой ветви перед переходом к следующему.",
|
||||
"updated": 1638340581444
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -64,6 +74,11 @@
|
||||
"text": "Im Allgemeinen verhalten sich alle nodes ähnlich in dem Sinne, dass jede node, unabhängig vom genauen Typ, für sich genommen einen Lernschritt darstellt. Das heißt, alle nodes, einschließlich der obersten Tutorial-node, der Tutorial Topic node und der Tutorial Step node können eine Inhaltsseite als Schritt im Tutorial anzeigen. Feinere Unterschiede sind gegebenenfalls hervorzuheben.",
|
||||
"updated": 1633976586338,
|
||||
"style": "Callout"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "В общих чертах, все типы узлов ведут себя одинаково в том смысле, что каждый узел, независимо от конкретного типа, представляет собой учебный шаг сам по себе. То есть все узлы, включая узел верхнего уровня Tutorial, узел Tutorial Topic и узел Tutorial Step, могут отображать страницу содержимого в качестве шага в руководстве. При необходимости следует указать на незначительные различия.",
|
||||
"updated": 1638340613514
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -77,6 +92,11 @@
|
||||
"text": "Die wichtigste Gemeinsamkeit aller Knotentypen in der Hierarchie ist, dass sie alle Inhalte aus der Sammlung der Dokumentseiten des Tutorials beziehen können. Das heißt, jeder Knoten in der Hierarchie zeigt auf eine Docs Page (Dokumentationsseite), und es ist diese Seite in den Dokumenten, die den Inhalt für den gegebenen Schritt anzeigt. Der Zeiger wird in der Konfiguration jedes Knotens angegeben (siehe die Eigenschaft docs).",
|
||||
"updated": 1633976778899,
|
||||
"style": "Note"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Ключевое сходство между всеми типами узлов в иерархии заключается в том, что все они могут рисовать контент из коллекции страниц в Документах. То есть каждый узел в иерархии указывает на страницу Docs, и именно эта страница Docs Page содержит контент, отображаемый для данного шага. Указатель указывается в конфигурации каждого узла (см. Свойство docs).",
|
||||
"updated": 1638340776119
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -94,6 +114,11 @@
|
||||
"text": "Tutorial Konfiguration",
|
||||
"updated": 1633976794872,
|
||||
"style": "Title"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Конфигурация Tutorial",
|
||||
"updated": 1638340822415
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -107,6 +132,11 @@
|
||||
"text": "Die Konfiguration jedes Knotens in der Tutorial-Hierarchie bestimmt den Inhalt und das Verhalten der einzelnen Schritte. Alle node-typen haben die gleiche Grundkonfiguration. Tutorial Step nodes haben jedoch zusätzliche Eigenschaften, die auf der Seite \" Tutorial Step nodes \" beschrieben werden.",
|
||||
"updated": 1633977045116,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Конфигурация каждого узла в иерархии учебника определяет содержание и поведение каждого шага. Все типы узлов имеют одинаковую базовую конфигурацию. Однако узлы Tutorial Step имеют дополнительные свойства, описанные на странице узла Tutorial Step.",
|
||||
"updated": 1638340869518
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -120,6 +150,11 @@
|
||||
"text": "Die auf dieser Seite beschriebenen Konfigurationseigenschaften gelten für alle Arten von Nodes in der Hierarchie.",
|
||||
"updated": 1633977077545,
|
||||
"style": "Success"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Свойства конфигурации, обсуждаемые на этой странице, применяются ко всем типам узлов в иерархии.",
|
||||
"updated": 1638340885116
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -133,6 +168,11 @@
|
||||
"text": "Das Layout der HTML-Seite folgt dem Layout der zugehörigen Seite in den Docs, wird aber innerhalb der Grenzen des Tutorial-Fensters angezeigt.",
|
||||
"updated": 1633977110132,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Макет HTML-страницы соответствует макету связанной страницы в Docs, хотя отображается в пределах окна учебника.",
|
||||
"updated": 1638340917482
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -146,6 +186,11 @@
|
||||
"text": "Mit anderen Worten beeinflussen Sie das Layout des jeweiligen Schritts, indem Sie Inhalte schreiben und Stile auf einzelne Absätze in der zugehörigen Docs-Page anwenden.",
|
||||
"updated": 1633977352460,
|
||||
"style": "Callout"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Другими словами, вы влияете на макет конкретного шага, записывая содержимое и применяя стили к отдельным абзацам на связанной странице Docs Page.",
|
||||
"updated": 1638340975740
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -159,6 +204,11 @@
|
||||
"text": "Die HTML / Tutorial -Seite verfügt über einen begrenzten Platz und unterstützt kein Scrollen. Wenn zu viele Informationen in der zugehörigen Docs Page definiert werden, übersteigt der resultierende Inhalt den verfügbaren Platz im Tutorial-Fenster. Es gibt keine systemseitige Kontrolle, um mit solchen Text-Überläufen (in den Tutorial fenstern) umzugehen, so dass es an Ihnen liegt, die Informationen auf mehrere Schritte zu verteilen und jede Seite innerhalb ihrer Grenzen zu halten.",
|
||||
"updated": 1633977528479,
|
||||
"style": "Note"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "HTML-страница имеет ограниченное пространство и не поддерживает прокрутку. Если на связанной странице Docs Page определено слишком много информации, результирующий контент будет переполнять доступное пространство в окне руководства. Нет никакого системного контроля, чтобы справиться с такими переполнениями, так что вам решать, распределять информацию по нескольким шагам и держать каждую страницу в своих пределах.",
|
||||
"updated": 1638341066695
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -172,6 +222,11 @@
|
||||
"text": "Eigenschaften der Konfiguration",
|
||||
"updated": 1633977545673,
|
||||
"style": "Title"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Свойства конфигурации",
|
||||
"updated": 1638341097060
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -190,6 +245,11 @@
|
||||
"text": "Das Eigenschaftssymbol benötigt zwei Definitionen:",
|
||||
"updated": 1633977572877,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Свойство \"icon\" требует двух определений:",
|
||||
"updated": 1638341508736
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -202,6 +262,11 @@
|
||||
"text": "Der \"name\" des Symbols ohne die Erweiterung;",
|
||||
"updated": 1633977609627,
|
||||
"style": "List"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Название \"name\" значка без расширения;",
|
||||
"updated": 1638341578178
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -214,6 +279,11 @@
|
||||
"text": "Das \"project\", zu dem das Symbol gehört.",
|
||||
"updated": 1633977635502,
|
||||
"style": "List"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Название пректа \"Project\", которому принадлежит значок.",
|
||||
"updated": 1638341642470
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -226,6 +296,11 @@
|
||||
"text": "Zu Ihrer Information: Die Icons (Symbole) werden unter folgendem Pfad gespeichert:",
|
||||
"updated": 1633977674577,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Для справки, значки хранятся по следующему пути:",
|
||||
"updated": 1638341659063
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -244,6 +319,11 @@
|
||||
"text": "Verwenden Sie die vorhandene Sammlung von Icons (Symbolen), um jeden Schritt des Tutorials zu illustrieren. Diese Illustrationen tragen sehr viel zu einer überzeugenderen Kommunikation bei.",
|
||||
"updated": 1633977745001,
|
||||
"style": "Success"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Используйте существующую коллекцию значков, чтобы проиллюстрировать каждый шаг учебного пособия. Эти иллюстрации помогают сделать общение более убедительным.",
|
||||
"updated": 1638341684555
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -257,6 +337,11 @@
|
||||
"text": "Wenn die Konfiguration vorhanden ist, wird das Symbol in einer Tabelle neben dem Textblock platziert, der das Definitionsfeld der zugehörigen Docs Page bildet. Wenn kein Symbol vorhanden ist, nimmt die Definition die gesamte Breite der Seite ein.",
|
||||
"updated": 1633977797138,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Когда конфигурация существует, значок помещается в таблицу рядом с текстовым блоком, который составляет поле определения связанной страницы Docs Page. Когда нет значка, определение занимает всю ширину страницы.",
|
||||
"updated": 1638341729479
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -275,6 +360,11 @@
|
||||
"text": "Die obige Konfiguration platziert das Symbol balance.png des Superalgos-Projekts in der Tabelle der entsprechenden Docs-Seite (siehe die docs Eigenschaft unten).",
|
||||
"updated": 1633977851249,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Приведенная выше конфигурация помещает значок balance.png проекта Superalgos в таблицу на соответствующей странице Docs (см. Свойство docs ниже).",
|
||||
"updated": 1638341767468
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -293,6 +383,11 @@
|
||||
"text": "Die Eigenschaft \"position\" bestimmt die Position des fließenden HTML-Dokuments (Tutorial-Fensters). Es gibt drei Alternativen... \"Links\", \"Rechts\", oder lassen Sie das Feld leer, um die Seite in der Mitte des Bildschirms zu positionieren.",
|
||||
"updated": 1633977968939,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Свойство \"position\" определяет положение плавающего HTML. Есть три варианта: «Left», «Right» или оставьте поле пустым, чтобы разместить страницу в центре экрана.",
|
||||
"updated": 1638341861917
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -311,6 +406,11 @@
|
||||
"text": "Bei der obigen Konfiguration befindet sich das Tutorial-Fenster auf der linken Seite des Bildschirms.",
|
||||
"updated": 1633978012441,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Приведенная выше конфигурация помещает шаг обучения в левой части экрана.",
|
||||
"updated": 1638341886907
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -328,13 +428,25 @@
|
||||
"text": "Die Eigenschaft slider bestimmt die Position des horizontalen Schiebereglers. Es gibt drei Alternativen... \"toTop\", \"toBottom\", \"toMiddle\".",
|
||||
"updated": 1633978051324,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Свойство \"slider\" определяет положение горизонтального ползунка. Есть три альтернативы: toTop, toBottom, toMiddle.",
|
||||
"updated": 1638342086859
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Json",
|
||||
"text": "\"slider\": \"toBottom\" // Opens the Charting Space",
|
||||
"updated": 1617624084984
|
||||
"updated": 1617624084984,
|
||||
"translations": [
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "\"slider\": \"toBottom\" // Открывает область графиков",
|
||||
"updated": 1638342112119
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"style": "Json",
|
||||
@@ -360,6 +472,11 @@
|
||||
"text": "Mit der Eigenschaft pressButton können Sie das Tutorial zwangsweise \"anhalten\", zum \"nächsten\" Schritt gehen, zum \"vorherigen\" Schritt gehen oder einen Schritt \"überspringen\". Wenn ein Schritt geladen wird und eine dieser Optionen in dieser Eigenschaft konfiguriert ist, wird die Aktion sofort ausgeführt.",
|
||||
"updated": 1633978127988,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Свойство pressButton позволяет принудительно остановить учебник «Stop», перейти к следующему шагу «Next», перейти к предыдущему шагу «Previous» или пропустить шаг «Skip». Когда шаг загружен и в этом свойстве настроен один из этих параметров, действие применяется немедленно.",
|
||||
"updated": 1638342503229
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -373,6 +490,11 @@
|
||||
"text": "Eine häufige Verwendung dieser Funktion ist das Überspringen eines Schrittes, z.B. einer Tutorial-topic-node, die ausschließlich für organisatorische Zwecke innerhalb der Hierarchie verwendet wird und daher keinen Inhalt hat. Lassen Sie das Feld leer, damit der Schritt auf die übliche Weise abgespielt wird.",
|
||||
"updated": 1633978359504,
|
||||
"style": "Note"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Обычно эта функция используется для пропуска шага, например, узла Tutorial Topic, который используется исключительно для организационных целей в иерархии и поэтому не содержит содержимого. Оставьте поле пустым, чтобы шаг играл обычным образом.",
|
||||
"updated": 1638342559154
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -391,6 +513,11 @@
|
||||
"text": "Die obige Konfiguration führt den Benutzer zum nächsten Schritt des Tutorials, ohne dass der Inhalt der betreffenden node angezeigt wird.",
|
||||
"updated": 1633978461463,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Приведенная выше конфигурация переводит пользователя на следующий шаг в руководстве без отображения какого-либо содержимого для рассматриваемого узла.",
|
||||
"updated": 1638342577730
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -404,6 +531,11 @@
|
||||
"text": "Sie können Tutorial Topic nodes nur zu organisatorischen Zwecken verwenden, z.B. um die Hierarchie so anzuordnen, dass sie einfach zu lesen und zu pflegen ist. Themen mit zu vielen Schritten sind später schwieriger zu handhaben. Mit anderen Worten: Sie können einen Tutorial Topic node verwenden, um mehrere Schritte zu gruppieren, auch wenn Sie nicht beabsichtigen, dass die Tutorial Topic node irgendeinen Inhalt anzeigt. In solchen Fällen können Sie diese Eigenschaft verwenden, um den Schritt zu überspringen, der nicht für die Anzeige von Inhalten vorgesehen ist.",
|
||||
"updated": 1633979074457,
|
||||
"style": "Success"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Вы можете использовать узлы Tutorial Topic только для организационных целей, например, чтобы упорядочить иерархию таким образом, чтобы ее было легко читать и поддерживать. С темами, в которых слишком много шагов, потом будет труднее разбираться. Другими словами, вы можете использовать узел Tutorial Topic, чтобы сгруппировать несколько шагов, даже если вы не собираетесь в узле Tutorial Topic отображать какой-либо контент. В таких случаях вы можете использовать это свойство, чтобы пропустить шаг, не предназначенный для отображения содержимого.",
|
||||
"updated": 1638343008771
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -422,6 +554,11 @@
|
||||
"text": "Die docs-Eigenschaft wird verwendet, um auf die Docs Tutorial Page zu verweisen, die den Inhalt für den jeweiligen Tutorial-Schritt liefert. Die Definition erfordert den Namen des Project (Projekts), die Docs Page Category (Kategorie) welche immer \"Tutorial\" ist, und den vollständigen Namen der Docs Page, das heißt den \"Typ\", wie in der JSON-Datei angegeben.",
|
||||
"updated": 1633979212224,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Свойство \"docs\" используется для указания на страницу Docs Tutorial Page, которая передает контент на конкретный шаг учебного пособия. Для определения требуется имя проекта, категория страницы документов, которая всегда является «Tutorial», и полное имя страницы документов, то есть типа «type», как указано в файле JSON.",
|
||||
"updated": 1638343213692
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -444,6 +581,11 @@
|
||||
"text": "Die Eigenschaft wird verwendet, um das Verhalten des Docs-Panels zu steuern. Innerhalb von controlDocs sind mehrere Eigenschaften verfügbar, die alle optional sind. Sehen wir uns jede im Detail an.",
|
||||
"updated": 1633979296433,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Свойство используется для управления поведением панели Docs Panel. В controlDocs доступно несколько свойств, и все они являются необязательными. Рассмотрим подробно каждый.",
|
||||
"updated": 1638343486932
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -457,6 +599,11 @@
|
||||
"text": "Die Panel-Eigenschaft kann auf \"Öffnen\" oder \"Schließen\" eingestellt werden. Diese Eigenschaft steuert die Registerkarte, mit der Sie das Doku-Panel in einem Schritt öffnen und in einem anderen Schritt wieder schließen können, wenn es nicht mehr benötigt wird.",
|
||||
"updated": 1633979356912,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Свойство панели может быть установлено на «Open» или «Close». Это свойство управляет вкладкой, которая позволяет вам открыть панель документов за один шаг и закрыть ее на другом шаге, когда она больше не нужна.",
|
||||
"updated": 1638343378776
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -479,6 +626,11 @@
|
||||
"text": "Wenn das Docs Panel geschlossen ist, sind die restlichen Eigenschaften irgendwie irrelevant. Wenn das Docs-Panel jedoch geöffnet ist, steuern die restlichen Eigenschaften, was im Docs-Panel angezeigt wird.",
|
||||
"updated": 1633979402232,
|
||||
"style": "Success"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Если панель Docs Panel закрыта, остальные свойства не имеют значения. Однако, когда панель Docs Panel открыта, остальные свойства управляют тем, что отображается на панели Docs Panel.",
|
||||
"updated": 1638343471175
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -492,6 +644,11 @@
|
||||
"text": "Die Eigenschaft closeTutorialEditor kann den Docs-Editor ein- und ausschalten. Wenn die Eigenschaft auf false gesetzt ist oder wenn es keine Definition gibt und wenn keine spezifische Docs-Seite aufgerufen wird (siehe controlDocs unten), zeigt das Docs-Panel die Seite, die mit dem aktuellen Tutorial-Schritt im Bearbeitungsmodus verbunden ist.",
|
||||
"updated": 1633979594120,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Свойство closeTutorialEditor может включать и выключать редактор документов. Если установлено значение false или нет определения и при условии, что не вызывается конкретная страница Docs Page (см. ControlDocs ниже), на панели Docs Panel отображается страница, связанная с текущим этапом обучения в режиме редактирования.",
|
||||
"updated": 1638343628965
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -510,6 +667,11 @@
|
||||
"text": "Da diese Definition standardmäßig auf false gesetzt ist, ist dies das standardmäßige Verhalten. Das heißt, wenn das Docs-Bedienfeld geöffnet wird, während ein Tutorial-Schritt abgespielt wird, zeigt das Docs-Bedienfeld standardmäßig die Docs-Seite an, die mit dem jeweiligen Schritt verknüpft ist, so dass die Seite direkt an Ort und Stelle bearbeitet werden kann.",
|
||||
"updated": 1633979921238,
|
||||
"style": "Success"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Поскольку для этого определения по умолчанию установлено значение false, это обычное поведение. То есть по умолчанию, когда панель Docs Panel открывается во время выполнения шага обучения, на панели Docs Panel отображается страница Docs Page, связанная с конкретным шагом, так что страницу можно редактировать прямо сейчас.",
|
||||
"updated": 1638343747105
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -523,6 +685,11 @@
|
||||
"text": "Wenn die Eigenschaft closeTutorialEditor auf true gesetzt ist und keine Anforderung zum Öffnen einer Dokumentseite vorliegt (siehe Eigenschaft page weiter unten), dann zeigt das Dokumentseiten-Panel die Dokumentseite an, die im vorherigen Schritt angezeigt wurde.",
|
||||
"updated": 1633979700958,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Если для свойства closeTutorialEditor установлено значение true и нет запроса на открытие страницы Docs (см. Свойство страницы ниже), тогда на панели Docs отображается любая страница Docs Page, отображаемая на предыдущем шаге.",
|
||||
"updated": 1638343921788
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -541,6 +708,11 @@
|
||||
"text": "Es kann sinnvoll sein, den Tutorial Editor zu schließen, wenn der Benutzer von einem Schritt zum nächsten mit den Docs interagieren soll. Nehmen wir zum Beispiel an, das Tutorial weist den Benutzer an, eine Suche durchzuführen und dieses Ergebnis im nächsten Schritt zu besprechen. In diesem Fall möchten Sie, dass der Editor des Tutorials geschlossen bleibt, da sonst die vom Benutzer durchgeführte Suche verloren ginge und stattdessen die Seite geladen würde, die dem neuen Schritt des Tutorials entspricht.",
|
||||
"updated": 1633979902886,
|
||||
"style": "Success"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Вы можете закрыть редактор учебника, когда пользователь должен взаимодействовать с Docs от одного шага к другому. Например, предположим, что учебник инструктирует пользователя выполнить поиск и обсуждает результаты поиска на следующем шаге. В этом случае вы захотите, чтобы редактор учебников оставался закрытым, иначе поиск, выполненный пользователем, будет потерян, и вместо этого будет загружена страница, соответствующая новому шагу руководства.",
|
||||
"updated": 1638344026667
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -554,6 +726,11 @@
|
||||
"text": "Die Eigenschaft searchPage überschreibt die Einstellungen des Tutorial-Editors und lädt die Hauptsuchseite mit dem Suchfeld in der Mitte.",
|
||||
"updated": 1633979953175,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Свойство searchPage выходит за пределы настроек редактора учебника и загружает домашнюю страницу поиска с полем поиска в центре.",
|
||||
"updated": 1638344044257
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -572,6 +749,11 @@
|
||||
"text": "Verwenden Sie die obige Einstellung, wenn Sie möchten, dass der Benutzer das saubere Suchfeld erhält, um eine Suche durchzuführen oder einen Befehl auszuführen.",
|
||||
"updated": 1633980006636,
|
||||
"style": "Success"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Используйте указанную выше настройку, если хотите, чтобы пользователь получил чистое поле поиска для выполнения поиска или выполнения команды.",
|
||||
"updated": 1638344074943
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -585,6 +767,11 @@
|
||||
"text": "Mit der page property legen Sie fest, welche Docs Page geladen werden soll. Sie müssen das Project (Projekt), die Docs Page Category (Kategorie) und den \"type\" (Typ), also den vollständigen Namen der Seite, angeben. Die optionale Eigenschaft anchor ermöglicht es Ihnen, den Benutzer mit dem Docs Anchor Style auf einen bestimmten Absatz der Seite zu verweisen.",
|
||||
"updated": 1633980112889,
|
||||
"style": "Text"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Свойство page - это то место, где вы определяете, какая страница Docs Page должна быть загружена. Вы должны указать проект Project, категорию страницы Docs Page Category и «type», который является полным именем страницы. Необязательная привязка свойства позволяет указать пользователю на определенный абзац на странице с помощью привязки Docs Anchor Style.",
|
||||
"updated": 1638344326671
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -603,6 +790,11 @@
|
||||
"text": "Verwenden Sie die obige Konfiguration, wenn Sie möchten, dass der Benutzer eine bestimmte Seite in den Docs liest.",
|
||||
"updated": 1633980693215,
|
||||
"style": "Success"
|
||||
},
|
||||
{
|
||||
"language": "RU",
|
||||
"text": "Используйте указанную выше конфигурацию, если хотите, чтобы пользователь читал определенную страницу в Docs.",
|
||||
"updated": 1638344372113
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
@@ -11,12 +11,12 @@
|
||||
},
|
||||
{
|
||||
"action": "Add UI Object",
|
||||
"label": "Add Cancel Order Portfolio Event",
|
||||
"label": "Add Ask Portfolio Events Manager",
|
||||
"disableIfPropertyIsDefined": true,
|
||||
"propertyToCheckFor": "cancelOrderPMEvent",
|
||||
"relatedUiObject": "Cancel Order PM Event",
|
||||
"propertyToCheckFor": "askPortfolioEventsManager",
|
||||
"relatedUiObject": "Ask Portfolio Events Manager",
|
||||
"actionFunction": "payload.executeAction",
|
||||
"relatedUiObjectProject": "Foundations",
|
||||
"relatedUiObjectProject": "Portfolio-Management",
|
||||
"actionProject": "Visual-Scripting"
|
||||
},
|
||||
{
|
||||
@@ -51,9 +51,9 @@
|
||||
"autoAdd": true
|
||||
},
|
||||
{
|
||||
"name": "cancelOrderPMEvent",
|
||||
"name": "askPortfolioEventsManager",
|
||||
"type": "node",
|
||||
"childType": "Cancel Order PM Event"
|
||||
"childType": "Ask Portfolio Events Manager"
|
||||
},
|
||||
{
|
||||
"name": "announcements",
|
||||
|
||||