From 577fb7cfab97d3d24379d5f1cdba0784c2844b03 Mon Sep 17 00:00:00 2001 From: Axel Date: Sun, 28 Nov 2021 19:55:43 -0300 Subject: [PATCH 01/15] Integrate React frontend for desktop app * Added component files * Added dependencies and settings on package.json * Updated package-lock.json to match new dependencies * Added react scripts and config files --- Desktop/DesktopApp.js | 8 +- Desktop/UI/WebApp.js | 33 +- Desktop/WebServer/index.html | 134 +- Desktop/components/ReactBase.js | 16 + Desktop/components/Root.js | 31 + Desktop/components/User/UserCard.css | 17 + Desktop/components/User/UserCard.js | 30 + .../components/UsersSidebar/UsersSidebar.css | 4 + .../components/UsersSidebar/UsersSidebar.js | 23 + Desktop/components/feed/Feed.css | 11 + Desktop/components/feed/Feed.js | 14 + Desktop/components/home/App.css | 7 + Desktop/components/home/App.js | 23 + Desktop/components/logo/Logo.css | 10 + Desktop/components/logo/Logo.js | 17 + Desktop/components/notFound/NotFound.js | 10 + Desktop/components/post/Post.css | 32 + Desktop/components/post/Post.js | 62 + .../postPlaceholder/PostPlaceholder.css | 47 + .../postPlaceholder/PostPlaceholder.js | 54 + Desktop/components/postsFeed/PostsFeed.css | 4 + Desktop/components/postsFeed/PostsFeed.js | 26 + Desktop/components/searchBox/SearchBox.css | 3 + Desktop/components/searchBox/SearchBox.js | 16 + .../components/showMoreUsers/ShowMoreUsers.js | 16 + Desktop/components/sidebar/Sidebar.css | 3 + Desktop/components/sidebar/Sidebar.js | 42 + .../components/sidebarItem/SidebarItem.css | 21 + Desktop/components/sidebarItem/SidebarItem.js | 22 + .../suggestedUsers/SuggestedUsers.css | 0 .../suggestedUsers/SuggestedUsers.js | 32 + .../components/userProfile/UserProfile.css | 41 + Desktop/components/userProfile/UserProfile.js | 28 + .../userProfileHeader/UserProfileHeader.css | 0 .../userProfileHeader/UserProfileHeader.js | 30 + Desktop/config/env.js | 106 + Desktop/config/getHttpsConfig.js | 66 + Desktop/config/modules.js | 134 + Desktop/config/paths.js | 76 + Desktop/config/pnpTs.js | 35 + Desktop/config/webpack.config.js | 757 + Desktop/config/webpackDevServer.config.js | 130 + Desktop/images/banner.jpg | Bin 0 -> 6595126 bytes Desktop/images/pfp.png | Bin 0 -> 17477 bytes Desktop/images/superalgos.png | Bin 0 -> 17477 bytes Desktop/public/favicon.ico | Bin 0 -> 15086 bytes Desktop/public/index.html | 43 + Desktop/public/logo192.png | Bin 0 -> 5347 bytes Desktop/public/logo512.png | Bin 0 -> 9664 bytes Desktop/public/manifest.json | 25 + Desktop/public/robots.txt | 3 + Desktop/scripts/build.js | 212 + Desktop/scripts/start.js | 162 + Desktop/scripts/test.js | 53 + Desktop/src/components/ReactBase.js | 16 + Desktop/src/components/Root.js | 31 + Desktop/src/components/User/UserCard.css | 17 + Desktop/src/components/User/UserCard.js | 30 + .../components/UsersSidebar/UsersSidebar.css | 4 + .../components/UsersSidebar/UsersSidebar.js | 23 + Desktop/src/components/feed/Feed.css | 11 + Desktop/src/components/feed/Feed.js | 14 + Desktop/src/components/home/App.css | 7 + Desktop/src/components/home/App.js | 23 + Desktop/src/components/logo/Logo.css | 10 + Desktop/src/components/logo/Logo.js | 17 + Desktop/src/components/notFound/NotFound.js | 10 + Desktop/src/components/post/Post.css | 49 + Desktop/src/components/post/Post.js | 64 + .../postPlaceholder/PostPlaceholder.css | 47 + .../postPlaceholder/PostPlaceholder.js | 54 + .../src/components/postsFeed/PostsFeed.css | 5 + Desktop/src/components/postsFeed/PostsFeed.js | 25 + .../src/components/searchBox/SearchBox.css | 3 + Desktop/src/components/searchBox/SearchBox.js | 16 + .../components/showMoreUsers/ShowMoreUsers.js | 16 + Desktop/src/components/sidebar/Sidebar.css | 3 + Desktop/src/components/sidebar/Sidebar.js | 42 + .../components/sidebarItem/SidebarItem.css | 21 + .../src/components/sidebarItem/SidebarItem.js | 22 + .../suggestedUsers/SuggestedUsers.css | 0 .../suggestedUsers/SuggestedUsers.js | 32 + .../components/userProfile/UserProfile.css | 41 + .../src/components/userProfile/UserProfile.js | 28 + .../userProfileHeader/UserProfileHeader.css | 0 .../userProfileHeader/UserProfileHeader.js | 30 + Desktop/src/images/banner.jpg | Bin 0 -> 6595126 bytes Desktop/src/images/pfp.jpg | Bin 0 -> 24092 bytes Desktop/src/images/superalgos.png | Bin 0 -> 17477 bytes Desktop/src/index.css | 13 + Desktop/src/index.js | 18 + Desktop/src/reportWebVitals.js | 13 + Desktop/src/store/index.js | 9 + Desktop/src/store/slices/Example.slice.js | 21 + Desktop/store/index.js | 9 + Desktop/store/slices/Example.slice.js | 21 + MultiProject.js | 2 +- .../Plugins/User-Profiles/Loui-Molina.json | 339 +- package-lock.json | 41541 +++++++++++++++- package.json | 86 +- 100 files changed, 45030 insertions(+), 422 deletions(-) create mode 100644 Desktop/components/ReactBase.js create mode 100644 Desktop/components/Root.js create mode 100644 Desktop/components/User/UserCard.css create mode 100644 Desktop/components/User/UserCard.js create mode 100644 Desktop/components/UsersSidebar/UsersSidebar.css create mode 100644 Desktop/components/UsersSidebar/UsersSidebar.js create mode 100644 Desktop/components/feed/Feed.css create mode 100644 Desktop/components/feed/Feed.js create mode 100644 Desktop/components/home/App.css create mode 100644 Desktop/components/home/App.js create mode 100644 Desktop/components/logo/Logo.css create mode 100644 Desktop/components/logo/Logo.js create mode 100644 Desktop/components/notFound/NotFound.js create mode 100644 Desktop/components/post/Post.css create mode 100644 Desktop/components/post/Post.js create mode 100644 Desktop/components/postPlaceholder/PostPlaceholder.css create mode 100644 Desktop/components/postPlaceholder/PostPlaceholder.js create mode 100644 Desktop/components/postsFeed/PostsFeed.css create mode 100644 Desktop/components/postsFeed/PostsFeed.js create mode 100644 Desktop/components/searchBox/SearchBox.css create mode 100644 Desktop/components/searchBox/SearchBox.js create mode 100644 Desktop/components/showMoreUsers/ShowMoreUsers.js create mode 100644 Desktop/components/sidebar/Sidebar.css create mode 100644 Desktop/components/sidebar/Sidebar.js create mode 100644 Desktop/components/sidebarItem/SidebarItem.css create mode 100644 Desktop/components/sidebarItem/SidebarItem.js create mode 100644 Desktop/components/suggestedUsers/SuggestedUsers.css create mode 100644 Desktop/components/suggestedUsers/SuggestedUsers.js create mode 100644 Desktop/components/userProfile/UserProfile.css create mode 100644 Desktop/components/userProfile/UserProfile.js create mode 100644 Desktop/components/userProfileHeader/UserProfileHeader.css create mode 100644 Desktop/components/userProfileHeader/UserProfileHeader.js create mode 100644 Desktop/config/env.js create mode 100644 Desktop/config/getHttpsConfig.js create mode 100644 Desktop/config/modules.js create mode 100644 Desktop/config/paths.js create mode 100644 Desktop/config/pnpTs.js create mode 100644 Desktop/config/webpack.config.js create mode 100644 Desktop/config/webpackDevServer.config.js create mode 100644 Desktop/images/banner.jpg create mode 100644 Desktop/images/pfp.png create mode 100644 Desktop/images/superalgos.png create mode 100644 Desktop/public/favicon.ico create mode 100644 Desktop/public/index.html create mode 100644 Desktop/public/logo192.png create mode 100644 Desktop/public/logo512.png create mode 100644 Desktop/public/manifest.json create mode 100644 Desktop/public/robots.txt create mode 100644 Desktop/scripts/build.js create mode 100644 Desktop/scripts/start.js create mode 100644 Desktop/scripts/test.js create mode 100644 Desktop/src/components/ReactBase.js create mode 100644 Desktop/src/components/Root.js create mode 100644 Desktop/src/components/User/UserCard.css create mode 100644 Desktop/src/components/User/UserCard.js create mode 100644 Desktop/src/components/UsersSidebar/UsersSidebar.css create mode 100644 Desktop/src/components/UsersSidebar/UsersSidebar.js create mode 100644 Desktop/src/components/feed/Feed.css create mode 100644 Desktop/src/components/feed/Feed.js create mode 100644 Desktop/src/components/home/App.css create mode 100644 Desktop/src/components/home/App.js create mode 100644 Desktop/src/components/logo/Logo.css create mode 100644 Desktop/src/components/logo/Logo.js create mode 100644 Desktop/src/components/notFound/NotFound.js create mode 100644 Desktop/src/components/post/Post.css create mode 100644 Desktop/src/components/post/Post.js create mode 100644 Desktop/src/components/postPlaceholder/PostPlaceholder.css create mode 100644 Desktop/src/components/postPlaceholder/PostPlaceholder.js create mode 100644 Desktop/src/components/postsFeed/PostsFeed.css create mode 100644 Desktop/src/components/postsFeed/PostsFeed.js create mode 100644 Desktop/src/components/searchBox/SearchBox.css create mode 100644 Desktop/src/components/searchBox/SearchBox.js create mode 100644 Desktop/src/components/showMoreUsers/ShowMoreUsers.js create mode 100644 Desktop/src/components/sidebar/Sidebar.css create mode 100644 Desktop/src/components/sidebar/Sidebar.js create mode 100644 Desktop/src/components/sidebarItem/SidebarItem.css create mode 100644 Desktop/src/components/sidebarItem/SidebarItem.js create mode 100644 Desktop/src/components/suggestedUsers/SuggestedUsers.css create mode 100644 Desktop/src/components/suggestedUsers/SuggestedUsers.js create mode 100644 Desktop/src/components/userProfile/UserProfile.css create mode 100644 Desktop/src/components/userProfile/UserProfile.js create mode 100644 Desktop/src/components/userProfileHeader/UserProfileHeader.css create mode 100644 Desktop/src/components/userProfileHeader/UserProfileHeader.js create mode 100644 Desktop/src/images/banner.jpg create mode 100644 Desktop/src/images/pfp.jpg create mode 100644 Desktop/src/images/superalgos.png create mode 100644 Desktop/src/index.css create mode 100644 Desktop/src/index.js create mode 100644 Desktop/src/reportWebVitals.js create mode 100644 Desktop/src/store/index.js create mode 100644 Desktop/src/store/slices/Example.slice.js create mode 100644 Desktop/store/index.js create mode 100644 Desktop/store/slices/Example.slice.js diff --git a/Desktop/DesktopApp.js b/Desktop/DesktopApp.js index 3a8cc7c6f..13795715d 100644 --- a/Desktop/DesktopApp.js +++ b/Desktop/DesktopApp.js @@ -70,8 +70,12 @@ exports.newDesktopApp = function newDesktopApp() { thisObject.webSocketsInterface.initialize() console.log('Desktop Client Web Sockets Interface ......................................... Listening at port ' + JSON.parse(DK.desktopApp.p2pNetworkClient.node.config).webSocketsPort) - thisObject.httpInterface = DK.projects.socialTrading.modules.httpInterface.newDesktopModulesHttpInterface() - thisObject.httpInterface.initialize() + + /* TODO refactor this into something better */ + let react = require('./scripts/start'); + react.start(JSON.parse(DK.desktopApp.p2pNetworkClient.node.config).webPort); +/* 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) } } diff --git a/Desktop/UI/WebApp.js b/Desktop/UI/WebApp.js index cb4b4da43..f0d6afd9d 100644 --- a/Desktop/UI/WebApp.js +++ b/Desktop/UI/WebApp.js @@ -1,3 +1,12 @@ +const reactReqs = { + + a: REQUIREJS([], () => {}), + React: require('react'), + ReactDOM: require('react-dom'), + ReactBase: require("../components/ReactBase") +} + + function newWebApp() { /* In it's current state of development, the Web App only has one module. @@ -5,7 +14,7 @@ function newWebApp() { Everything is being coded here until some structure emerges. */ let thisObject = { - messageReceived: messageReceived, + messageReceived: messageReceived, initialize: initialize, finalize: finalize } @@ -21,9 +30,10 @@ function newWebApp() { setupRootObject(UI, 'UI') setupRootObject(SA, 'SA') await UI.projects.socialTrading.modules.webSocketsWebAppClient.initialize() - loadWUserProfileTimeline() - loadWhoToFollow() - setupEventHandlers() + loadReactDom(); + // loadWUserProfileTimeline() + // loadWhoToFollow() + // setupEventHandlers() } catch (err) { console.log('[ERROR] initialize -> err.stack = ' + err.stack) } @@ -100,6 +110,15 @@ function newWebApp() { } } + async function loadReactDom() { + + reactReqs.ReactDOM.render( + reactReqs.ReactBase, + document.getElementById('root') + ); + + } + async function loadWUserProfileTimeline() { let queryMessage = { queryType: SA.projects.socialTrading.globals.queryTypes.EVENTS, @@ -163,8 +182,7 @@ function newWebApp() { postDiv.appendChild(textNode) contentDiv.appendChild(postDiv) } - } - catch (err) { + } catch (err) { console.log('[ERROR] err.stack = ' + err.stack) } } @@ -285,6 +303,7 @@ function newWebApp() { function updateTextArea() { textArea.value = "" } + break } case 'Follow Profile': { @@ -302,6 +321,7 @@ function newWebApp() { button.setAttribute("class", "profile-to-unfollow-button") button.name = 'Unfollow Profile' } + break } case 'Unfollow Profile': { @@ -319,6 +339,7 @@ function newWebApp() { button.setAttribute("class", "profile-to-follow-button") button.name = 'Follow Profile' } + break } } diff --git a/Desktop/WebServer/index.html b/Desktop/WebServer/index.html index 9b57e56e0..ab04c6658 100644 --- a/Desktop/WebServer/index.html +++ b/Desktop/WebServer/index.html @@ -4,8 +4,8 @@ Superalgos Desktop - - + + @@ -18,7 +18,9 @@ } else { top.location = self.location } - } catch (e) { console.error('CJ protection', e) } + } catch (e) { + console.error('CJ protection', e) + } try { loadSuperalgos() @@ -34,131 +36,7 @@ - -
-
- - - - - - -
- - - - - - - - - - -
LOGO
PROFILE
-
- - - - - - - -
CONTENT TITLE
-
-
- - - - - - - -
SEARCH
- - - - - - - - - - -
Who to follow
-
Show more
-
-
-
> -
-
- Placeholder -
-
- - - - - -
Profile Pic - - - - - - - - - - -
- -
Permissions
Tool Bar
-
-
-
- POST 2 -
-
- POST 3 -
-
- POST 4 -
-
- POST 5 -
-
-
+
\ No newline at end of file diff --git a/Desktop/components/ReactBase.js b/Desktop/components/ReactBase.js new file mode 100644 index 000000000..da695b918 --- /dev/null +++ b/Desktop/components/ReactBase.js @@ -0,0 +1,16 @@ +import React from 'react'; +import {Provider} from "react-redux"; +import reducers from "../store"; +import Root from "./Root"; + +const ReactBase = () => { + return ( + + + + + + ); +}; + +export default ReactBase; diff --git a/Desktop/components/Root.js b/Desktop/components/Root.js new file mode 100644 index 000000000..dd45f133c --- /dev/null +++ b/Desktop/components/Root.js @@ -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 ( + + + }> + }/> + }> + {/*}/>*/} + + }/> + + + } + /> + + ) +} + +export default Root; \ No newline at end of file diff --git a/Desktop/components/User/UserCard.css b/Desktop/components/User/UserCard.css new file mode 100644 index 000000000..2b98ba940 --- /dev/null +++ b/Desktop/components/User/UserCard.css @@ -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; +} \ No newline at end of file diff --git a/Desktop/components/User/UserCard.js b/Desktop/components/User/UserCard.js new file mode 100644 index 000000000..ed3657c2e --- /dev/null +++ b/Desktop/components/User/UserCard.js @@ -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 ( + + + + {name} + + + + ); +}; + +export default UserCard; diff --git a/Desktop/components/UsersSidebar/UsersSidebar.css b/Desktop/components/UsersSidebar/UsersSidebar.css new file mode 100644 index 000000000..5cd540978 --- /dev/null +++ b/Desktop/components/UsersSidebar/UsersSidebar.css @@ -0,0 +1,4 @@ +.showMore { + width: 100%; + text-align: center; +} diff --git a/Desktop/components/UsersSidebar/UsersSidebar.js b/Desktop/components/UsersSidebar/UsersSidebar.js new file mode 100644 index 000000000..afd23ed51 --- /dev/null +++ b/Desktop/components/UsersSidebar/UsersSidebar.js @@ -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 ( + + + + + + ); +} + +export default UsersSidebar; diff --git a/Desktop/components/feed/Feed.css b/Desktop/components/feed/Feed.css new file mode 100644 index 000000000..7724e2fb7 --- /dev/null +++ b/Desktop/components/feed/Feed.css @@ -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; +} \ No newline at end of file diff --git a/Desktop/components/feed/Feed.js b/Desktop/components/feed/Feed.js new file mode 100644 index 000000000..e731d6d2f --- /dev/null +++ b/Desktop/components/feed/Feed.js @@ -0,0 +1,14 @@ +import './Feed.css'; +import PostPlaceholder from "../postPlaceholder/PostPlaceholder"; +import PostsFeed from "../postsFeed/PostsFeed"; + +const Feed = () => { + return ( +
+ + +
+ ); +} + +export default Feed; diff --git a/Desktop/components/home/App.css b/Desktop/components/home/App.css new file mode 100644 index 000000000..a255b898a --- /dev/null +++ b/Desktop/components/home/App.css @@ -0,0 +1,7 @@ +.app { + height: 100%; +} + +.middleSection{ + width: 40rem; +} \ No newline at end of file diff --git a/Desktop/components/home/App.js b/Desktop/components/home/App.js new file mode 100644 index 000000000..156b4ae3d --- /dev/null +++ b/Desktop/components/home/App.js @@ -0,0 +1,23 @@ +import './App.css'; +import Sidebar from "../sidebar/Sidebar"; +import UsersSidebar from "../UsersSidebar/UsersSidebar"; +import {Stack} from "@mui/material"; +import {Outlet} from "react-router-dom"; + +function App() { + return ( +
+ + + + + + + +
+ ); +} + +export default App; diff --git a/Desktop/components/logo/Logo.css b/Desktop/components/logo/Logo.css new file mode 100644 index 000000000..f35b42bc0 --- /dev/null +++ b/Desktop/components/logo/Logo.css @@ -0,0 +1,10 @@ +/*noinspection CssInvalidPropertyValue*/ +.logo { + display: inline-flex; + align-items: center; + height: 3rem; + width: 100%; +} +.icon { + margin: 0 0.5rem; +} \ No newline at end of file diff --git a/Desktop/components/logo/Logo.js b/Desktop/components/logo/Logo.js new file mode 100644 index 000000000..dcf4ba457 --- /dev/null +++ b/Desktop/components/logo/Logo.js @@ -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 ( + + +
SuperAlgos
+
+ ); +}; + +export default Logo; diff --git a/Desktop/components/notFound/NotFound.js b/Desktop/components/notFound/NotFound.js new file mode 100644 index 000000000..fc3b64a78 --- /dev/null +++ b/Desktop/components/notFound/NotFound.js @@ -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; diff --git a/Desktop/components/post/Post.css b/Desktop/components/post/Post.css new file mode 100644 index 000000000..cc9b51675 --- /dev/null +++ b/Desktop/components/post/Post.css @@ -0,0 +1,32 @@ +.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 { + height: 12.5rem; + padding-left: 2.8rem; +} + +.postUserName { + padding: 0.75rem; + justify-content: center; + align-items: center; +} + +.postFooter { + justify-content: space-evenly; + padding: 0.15rem; + width: 100%; + display: flex; +} diff --git a/Desktop/components/post/Post.js b/Desktop/components/post/Post.js new file mode 100644 index 000000000..15096d30b --- /dev/null +++ b/Desktop/components/post/Post.js @@ -0,0 +1,62 @@ +import "./Post.css" +import React from 'react'; +import {Avatar, Card, IconButton, Stack} 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."; + + function onButtonClick() { + console.log("Clicked the button"); + } + + return ( + + + + + + + {/*use props or local variables*/} + {userName} + + + + + + {/*use props or local variables*/} + {postBody} + + + {/* footer (buttons: like/dislike, repost) */} + { /* TODO remove unnecessary arrow function*/ + onButtonClick() + }} + > + + + {/* TODO remove unnecessary arrow function*/ + + onButtonClick() + }} + > + + + {/* TODO remove unnecessary arrow function*/ + onButtonClick() + }} + > + + + + + ); +}; + +export default Post; diff --git a/Desktop/components/postPlaceholder/PostPlaceholder.css b/Desktop/components/postPlaceholder/PostPlaceholder.css new file mode 100644 index 000000000..eab70d064 --- /dev/null +++ b/Desktop/components/postPlaceholder/PostPlaceholder.css @@ -0,0 +1,47 @@ +.postPlaceholder { + width: 40rem; + 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; +} diff --git a/Desktop/components/postPlaceholder/PostPlaceholder.js b/Desktop/components/postPlaceholder/PostPlaceholder.js new file mode 100644 index 000000000..181ec65e9 --- /dev/null +++ b/Desktop/components/postPlaceholder/PostPlaceholder.js @@ -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 ( +
+

Posts

+
+ + + + + + + + + + + + + + + +
+
+ ); +}; + +export default PostPlaceholder; diff --git a/Desktop/components/postsFeed/PostsFeed.css b/Desktop/components/postsFeed/PostsFeed.css new file mode 100644 index 000000000..87203bb43 --- /dev/null +++ b/Desktop/components/postsFeed/PostsFeed.css @@ -0,0 +1,4 @@ +.postFeedContainer { + padding: 0.5rem ; + width: 100%; +} diff --git a/Desktop/components/postsFeed/PostsFeed.js b/Desktop/components/postsFeed/PostsFeed.js new file mode 100644 index 000000000..5eb3bcf30 --- /dev/null +++ b/Desktop/components/postsFeed/PostsFeed.js @@ -0,0 +1,26 @@ +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 + } + ) + + return ( +
{/* todo margin appearing to the right of the div */} + + + {posts} + +
+ ); +}; + +export default PostsFeed; \ No newline at end of file diff --git a/Desktop/components/searchBox/SearchBox.css b/Desktop/components/searchBox/SearchBox.css new file mode 100644 index 000000000..21b5110d5 --- /dev/null +++ b/Desktop/components/searchBox/SearchBox.css @@ -0,0 +1,3 @@ +.searchIcon { + margin: 1rem 0.5rem; +} \ No newline at end of file diff --git a/Desktop/components/searchBox/SearchBox.js b/Desktop/components/searchBox/SearchBox.js new file mode 100644 index 000000000..2d64998f8 --- /dev/null +++ b/Desktop/components/searchBox/SearchBox.js @@ -0,0 +1,16 @@ +import React from 'react'; +import {Box, TextField} from "@mui/material"; +import {Search} from "@mui/icons-material"; + +const SearchBox = () => { + return ( +
+ + + + +
+ ); +}; + +export default SearchBox; diff --git a/Desktop/components/showMoreUsers/ShowMoreUsers.js b/Desktop/components/showMoreUsers/ShowMoreUsers.js new file mode 100644 index 000000000..ccf4128a5 --- /dev/null +++ b/Desktop/components/showMoreUsers/ShowMoreUsers.js @@ -0,0 +1,16 @@ +import React from 'react'; +import {Link} from "@mui/material"; + +const ShowMoreUsers = ({showMoreCallback}) => { + return ( + + Show more + + ); +}; + + +export default ShowMoreUsers; \ No newline at end of file diff --git a/Desktop/components/sidebar/Sidebar.css b/Desktop/components/sidebar/Sidebar.css new file mode 100644 index 000000000..0f6fac6c2 --- /dev/null +++ b/Desktop/components/sidebar/Sidebar.css @@ -0,0 +1,3 @@ +.sidebar{ + height: 100%; +} \ No newline at end of file diff --git a/Desktop/components/sidebar/Sidebar.js b/Desktop/components/sidebar/Sidebar.js new file mode 100644 index 000000000..63a9bcbe9 --- /dev/null +++ b/Desktop/components/sidebar/Sidebar.js @@ -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 + }) + + return ( +
+ + + }> + {menuItems} + + + + +
+ ); +} + +export default Sidebar; diff --git a/Desktop/components/sidebarItem/SidebarItem.css b/Desktop/components/sidebarItem/SidebarItem.css new file mode 100644 index 000000000..66d219931 --- /dev/null +++ b/Desktop/components/sidebarItem/SidebarItem.css @@ -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; + +} + diff --git a/Desktop/components/sidebarItem/SidebarItem.js b/Desktop/components/sidebarItem/SidebarItem.js new file mode 100644 index 000000000..40cf0bc6e --- /dev/null +++ b/Desktop/components/sidebarItem/SidebarItem.js @@ -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 ( +
+ + +
{value}
+
+
+ ); +} + +export default SidebarItem; diff --git a/Desktop/components/suggestedUsers/SuggestedUsers.css b/Desktop/components/suggestedUsers/SuggestedUsers.css new file mode 100644 index 000000000..e69de29bb diff --git a/Desktop/components/suggestedUsers/SuggestedUsers.js b/Desktop/components/suggestedUsers/SuggestedUsers.js new file mode 100644 index 000000000..e2cde9d4b --- /dev/null +++ b/Desktop/components/suggestedUsers/SuggestedUsers.js @@ -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 + }) + + useEffect(() => { + console.log("SuggestedUsersConstructor called") + }, []); + + + return ( + + {users} + + + ); +}; +export default SuggestedUsers; diff --git a/Desktop/components/userProfile/UserProfile.css b/Desktop/components/userProfile/UserProfile.css new file mode 100644 index 000000000..1ff38e5f2 --- /dev/null +++ b/Desktop/components/userProfile/UserProfile.css @@ -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: 40rem; +} \ No newline at end of file diff --git a/Desktop/components/userProfile/UserProfile.js b/Desktop/components/userProfile/UserProfile.js new file mode 100644 index 000000000..d6ba38d64 --- /dev/null +++ b/Desktop/components/userProfile/UserProfile.js @@ -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 ( + + + + + + ); +}; + + +export default UserProfile; diff --git a/Desktop/components/userProfileHeader/UserProfileHeader.css b/Desktop/components/userProfileHeader/UserProfileHeader.css new file mode 100644 index 000000000..e69de29bb diff --git a/Desktop/components/userProfileHeader/UserProfileHeader.js b/Desktop/components/userProfileHeader/UserProfileHeader.js new file mode 100644 index 000000000..96a06df18 --- /dev/null +++ b/Desktop/components/userProfileHeader/UserProfileHeader.js @@ -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/pfp.png"; + +const UserProfileHeader = ({actualUser}) => { + return ( + + +
+
+ +
+ + {actualUser} + +
+
+ ); +}; + +export default UserProfileHeader; diff --git a/Desktop/config/env.js b/Desktop/config/env.js new file mode 100644 index 000000000..cecdd1e46 --- /dev/null +++ b/Desktop/config/env.js @@ -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, . + // 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; diff --git a/Desktop/config/getHttpsConfig.js b/Desktop/config/getHttpsConfig.js new file mode 100644 index 000000000..831426f3e --- /dev/null +++ b/Desktop/config/getHttpsConfig.js @@ -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; diff --git a/Desktop/config/modules.js b/Desktop/config/modules.js new file mode 100644 index 000000000..52da19ac2 --- /dev/null +++ b/Desktop/config/modules.js @@ -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/(.*)$': '/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(); diff --git a/Desktop/config/paths.js b/Desktop/config/paths.js new file mode 100644 index 000000000..fa9e0bf67 --- /dev/null +++ b/Desktop/config/paths.js @@ -0,0 +1,76 @@ + + +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 - - - - - - -
- - - \ No newline at end of file diff --git a/Desktop/components/ReactBase.js b/Desktop/components/ReactBase.js deleted file mode 100644 index da695b918..000000000 --- a/Desktop/components/ReactBase.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import {Provider} from "react-redux"; -import reducers from "../store"; -import Root from "./Root"; - -const ReactBase = () => { - return ( - - - - - - ); -}; - -export default ReactBase; diff --git a/Desktop/components/Root.js b/Desktop/components/Root.js deleted file mode 100644 index dd45f133c..000000000 --- a/Desktop/components/Root.js +++ /dev/null @@ -1,31 +0,0 @@ -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 ( - - - }> - }/> - }> - {/*}/>*/} - - }/> - - - } - /> - - ) -} - -export default Root; \ No newline at end of file diff --git a/Desktop/components/User/UserCard.css b/Desktop/components/User/UserCard.css deleted file mode 100644 index 2b98ba940..000000000 --- a/Desktop/components/User/UserCard.css +++ /dev/null @@ -1,17 +0,0 @@ -.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; -} \ No newline at end of file diff --git a/Desktop/components/User/UserCard.js b/Desktop/components/User/UserCard.js deleted file mode 100644 index ed3657c2e..000000000 --- a/Desktop/components/User/UserCard.js +++ /dev/null @@ -1,30 +0,0 @@ -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 ( - - - - {name} - - - - ); -}; - -export default UserCard; diff --git a/Desktop/components/UsersSidebar/UsersSidebar.css b/Desktop/components/UsersSidebar/UsersSidebar.css deleted file mode 100644 index 5cd540978..000000000 --- a/Desktop/components/UsersSidebar/UsersSidebar.css +++ /dev/null @@ -1,4 +0,0 @@ -.showMore { - width: 100%; - text-align: center; -} diff --git a/Desktop/components/UsersSidebar/UsersSidebar.js b/Desktop/components/UsersSidebar/UsersSidebar.js deleted file mode 100644 index afd23ed51..000000000 --- a/Desktop/components/UsersSidebar/UsersSidebar.js +++ /dev/null @@ -1,23 +0,0 @@ -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 ( - - - - - - ); -} - -export default UsersSidebar; diff --git a/Desktop/components/feed/Feed.css b/Desktop/components/feed/Feed.css deleted file mode 100644 index 7724e2fb7..000000000 --- a/Desktop/components/feed/Feed.css +++ /dev/null @@ -1,11 +0,0 @@ -.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; -} \ No newline at end of file diff --git a/Desktop/components/feed/Feed.js b/Desktop/components/feed/Feed.js deleted file mode 100644 index e731d6d2f..000000000 --- a/Desktop/components/feed/Feed.js +++ /dev/null @@ -1,14 +0,0 @@ -import './Feed.css'; -import PostPlaceholder from "../postPlaceholder/PostPlaceholder"; -import PostsFeed from "../postsFeed/PostsFeed"; - -const Feed = () => { - return ( -
- - -
- ); -} - -export default Feed; diff --git a/Desktop/components/home/App.css b/Desktop/components/home/App.css deleted file mode 100644 index a255b898a..000000000 --- a/Desktop/components/home/App.css +++ /dev/null @@ -1,7 +0,0 @@ -.app { - height: 100%; -} - -.middleSection{ - width: 40rem; -} \ No newline at end of file diff --git a/Desktop/components/home/App.js b/Desktop/components/home/App.js deleted file mode 100644 index 156b4ae3d..000000000 --- a/Desktop/components/home/App.js +++ /dev/null @@ -1,23 +0,0 @@ -import './App.css'; -import Sidebar from "../sidebar/Sidebar"; -import UsersSidebar from "../UsersSidebar/UsersSidebar"; -import {Stack} from "@mui/material"; -import {Outlet} from "react-router-dom"; - -function App() { - return ( -
- - - - - - - -
- ); -} - -export default App; diff --git a/Desktop/components/logo/Logo.css b/Desktop/components/logo/Logo.css deleted file mode 100644 index f35b42bc0..000000000 --- a/Desktop/components/logo/Logo.css +++ /dev/null @@ -1,10 +0,0 @@ -/*noinspection CssInvalidPropertyValue*/ -.logo { - display: inline-flex; - align-items: center; - height: 3rem; - width: 100%; -} -.icon { - margin: 0 0.5rem; -} \ No newline at end of file diff --git a/Desktop/components/logo/Logo.js b/Desktop/components/logo/Logo.js deleted file mode 100644 index dcf4ba457..000000000 --- a/Desktop/components/logo/Logo.js +++ /dev/null @@ -1,17 +0,0 @@ -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 ( - - -
SuperAlgos
-
- ); -}; - -export default Logo; diff --git a/Desktop/components/notFound/NotFound.js b/Desktop/components/notFound/NotFound.js deleted file mode 100644 index fc3b64a78..000000000 --- a/Desktop/components/notFound/NotFound.js +++ /dev/null @@ -1,10 +0,0 @@ -import React, {useEffect} from 'react'; -import {useNavigate} from "react-router-dom"; - -const NotFound = () => { - let navigate = useNavigate(); - useEffect(() => navigate("/"), [navigate]) - return null; -}; - -export default NotFound; diff --git a/Desktop/components/post/Post.css b/Desktop/components/post/Post.css deleted file mode 100644 index cc9b51675..000000000 --- a/Desktop/components/post/Post.css +++ /dev/null @@ -1,32 +0,0 @@ -.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 { - height: 12.5rem; - padding-left: 2.8rem; -} - -.postUserName { - padding: 0.75rem; - justify-content: center; - align-items: center; -} - -.postFooter { - justify-content: space-evenly; - padding: 0.15rem; - width: 100%; - display: flex; -} diff --git a/Desktop/components/post/Post.js b/Desktop/components/post/Post.js deleted file mode 100644 index 15096d30b..000000000 --- a/Desktop/components/post/Post.js +++ /dev/null @@ -1,62 +0,0 @@ -import "./Post.css" -import React from 'react'; -import {Avatar, Card, IconButton, Stack} 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."; - - function onButtonClick() { - console.log("Clicked the button"); - } - - return ( - - - - - - - {/*use props or local variables*/} - {userName} - - - - - - {/*use props or local variables*/} - {postBody} - - - {/* footer (buttons: like/dislike, repost) */} - { /* TODO remove unnecessary arrow function*/ - onButtonClick() - }} - > - - - {/* TODO remove unnecessary arrow function*/ - - onButtonClick() - }} - > - - - {/* TODO remove unnecessary arrow function*/ - onButtonClick() - }} - > - - - - - ); -}; - -export default Post; diff --git a/Desktop/components/postPlaceholder/PostPlaceholder.css b/Desktop/components/postPlaceholder/PostPlaceholder.css deleted file mode 100644 index eab70d064..000000000 --- a/Desktop/components/postPlaceholder/PostPlaceholder.css +++ /dev/null @@ -1,47 +0,0 @@ -.postPlaceholder { - width: 40rem; - 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; -} diff --git a/Desktop/components/postPlaceholder/PostPlaceholder.js b/Desktop/components/postPlaceholder/PostPlaceholder.js deleted file mode 100644 index 181ec65e9..000000000 --- a/Desktop/components/postPlaceholder/PostPlaceholder.js +++ /dev/null @@ -1,54 +0,0 @@ -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 ( -
-

Posts

-
- - - - - - - - - - - - - - - -
-
- ); -}; - -export default PostPlaceholder; diff --git a/Desktop/components/postsFeed/PostsFeed.css b/Desktop/components/postsFeed/PostsFeed.css deleted file mode 100644 index 87203bb43..000000000 --- a/Desktop/components/postsFeed/PostsFeed.css +++ /dev/null @@ -1,4 +0,0 @@ -.postFeedContainer { - padding: 0.5rem ; - width: 100%; -} diff --git a/Desktop/components/postsFeed/PostsFeed.js b/Desktop/components/postsFeed/PostsFeed.js deleted file mode 100644 index 5eb3bcf30..000000000 --- a/Desktop/components/postsFeed/PostsFeed.js +++ /dev/null @@ -1,26 +0,0 @@ -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 - } - ) - - return ( -
{/* todo margin appearing to the right of the div */} - - - {posts} - -
- ); -}; - -export default PostsFeed; \ No newline at end of file diff --git a/Desktop/components/searchBox/SearchBox.css b/Desktop/components/searchBox/SearchBox.css deleted file mode 100644 index 21b5110d5..000000000 --- a/Desktop/components/searchBox/SearchBox.css +++ /dev/null @@ -1,3 +0,0 @@ -.searchIcon { - margin: 1rem 0.5rem; -} \ No newline at end of file diff --git a/Desktop/components/searchBox/SearchBox.js b/Desktop/components/searchBox/SearchBox.js deleted file mode 100644 index 2d64998f8..000000000 --- a/Desktop/components/searchBox/SearchBox.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import {Box, TextField} from "@mui/material"; -import {Search} from "@mui/icons-material"; - -const SearchBox = () => { - return ( -
- - - - -
- ); -}; - -export default SearchBox; diff --git a/Desktop/components/showMoreUsers/ShowMoreUsers.js b/Desktop/components/showMoreUsers/ShowMoreUsers.js deleted file mode 100644 index ccf4128a5..000000000 --- a/Desktop/components/showMoreUsers/ShowMoreUsers.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import {Link} from "@mui/material"; - -const ShowMoreUsers = ({showMoreCallback}) => { - return ( - - Show more - - ); -}; - - -export default ShowMoreUsers; \ No newline at end of file diff --git a/Desktop/components/sidebar/Sidebar.css b/Desktop/components/sidebar/Sidebar.css deleted file mode 100644 index 0f6fac6c2..000000000 --- a/Desktop/components/sidebar/Sidebar.css +++ /dev/null @@ -1,3 +0,0 @@ -.sidebar{ - height: 100%; -} \ No newline at end of file diff --git a/Desktop/components/sidebar/Sidebar.js b/Desktop/components/sidebar/Sidebar.js deleted file mode 100644 index 63a9bcbe9..000000000 --- a/Desktop/components/sidebar/Sidebar.js +++ /dev/null @@ -1,42 +0,0 @@ -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 - }) - - return ( -
- - - }> - {menuItems} - - - - -
- ); -} - -export default Sidebar; diff --git a/Desktop/components/sidebarItem/SidebarItem.css b/Desktop/components/sidebarItem/SidebarItem.css deleted file mode 100644 index 66d219931..000000000 --- a/Desktop/components/sidebarItem/SidebarItem.css +++ /dev/null @@ -1,21 +0,0 @@ - -.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; - -} - diff --git a/Desktop/components/sidebarItem/SidebarItem.js b/Desktop/components/sidebarItem/SidebarItem.js deleted file mode 100644 index 40cf0bc6e..000000000 --- a/Desktop/components/sidebarItem/SidebarItem.js +++ /dev/null @@ -1,22 +0,0 @@ -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 ( -
- - -
{value}
-
-
- ); -} - -export default SidebarItem; diff --git a/Desktop/components/suggestedUsers/SuggestedUsers.css b/Desktop/components/suggestedUsers/SuggestedUsers.css deleted file mode 100644 index e69de29bb..000000000 diff --git a/Desktop/components/suggestedUsers/SuggestedUsers.js b/Desktop/components/suggestedUsers/SuggestedUsers.js deleted file mode 100644 index e2cde9d4b..000000000 --- a/Desktop/components/suggestedUsers/SuggestedUsers.js +++ /dev/null @@ -1,32 +0,0 @@ -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 - }) - - useEffect(() => { - console.log("SuggestedUsersConstructor called") - }, []); - - - return ( - - {users} - - - ); -}; -export default SuggestedUsers; diff --git a/Desktop/components/userProfile/UserProfile.css b/Desktop/components/userProfile/UserProfile.css deleted file mode 100644 index 1ff38e5f2..000000000 --- a/Desktop/components/userProfile/UserProfile.css +++ /dev/null @@ -1,41 +0,0 @@ -.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: 40rem; -} \ No newline at end of file diff --git a/Desktop/components/userProfile/UserProfile.js b/Desktop/components/userProfile/UserProfile.js deleted file mode 100644 index d6ba38d64..000000000 --- a/Desktop/components/userProfile/UserProfile.js +++ /dev/null @@ -1,28 +0,0 @@ -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 ( - - - - - - ); -}; - - -export default UserProfile; diff --git a/Desktop/components/userProfileHeader/UserProfileHeader.css b/Desktop/components/userProfileHeader/UserProfileHeader.css deleted file mode 100644 index e69de29bb..000000000 diff --git a/Desktop/components/userProfileHeader/UserProfileHeader.js b/Desktop/components/userProfileHeader/UserProfileHeader.js deleted file mode 100644 index 96a06df18..000000000 --- a/Desktop/components/userProfileHeader/UserProfileHeader.js +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import {Card, CardContent, CardMedia, Typography} from "@mui/material"; -import banner from "../../images/banner.jpg"; -import pfp from "../../images/pfp.png"; - -const UserProfileHeader = ({actualUser}) => { - return ( - - -
-
- -
- - {actualUser} - -
-
- ); -}; - -export default UserProfileHeader; diff --git a/Desktop/public/favicon.ico b/Desktop/public/favicon.ico deleted file mode 100644 index cd2c5d6afbe08d2ee060535f131cc21ad521575c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 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 diff --git a/Desktop/WebServer/Fonts/Saira-Condensed.woff2 b/Desktop/public/fonts/Saira-Condensed.woff2 similarity index 100% rename from Desktop/WebServer/Fonts/Saira-Condensed.woff2 rename to Desktop/public/fonts/Saira-Condensed.woff2 diff --git a/Desktop/WebServer/Fonts/Saira.woff2 b/Desktop/public/fonts/Saira.woff2 similarity index 100% rename from Desktop/WebServer/Fonts/Saira.woff2 rename to Desktop/public/fonts/Saira.woff2 diff --git a/Desktop/WebServer/Fonts/segoeui.woff b/Desktop/public/fonts/segoeui.woff similarity index 100% rename from Desktop/WebServer/Fonts/segoeui.woff rename to Desktop/public/fonts/segoeui.woff diff --git a/Desktop/WebServer/Images/favicon.ico b/Desktop/public/images/favicon.ico similarity index 100% rename from Desktop/WebServer/Images/favicon.ico rename to Desktop/public/images/favicon.ico diff --git a/Desktop/public/index.html b/Desktop/public/index.html index ecddaab42..81a214993 100644 --- a/Desktop/public/index.html +++ b/Desktop/public/index.html @@ -1,43 +1,32 @@ - - - - - - - - - - React App + + + + - - -
- + + +
diff --git a/Desktop/public/logo192.png b/Desktop/public/logo192.png deleted file mode 100644 index fc44b0a3796c0e0a64c3d858ca038bd4570465d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5347 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 b{|{}U^kRn2wo}j7|0ATu<;8xA7zX}7|B6mN diff --git a/Desktop/src/components/ReactBase.js b/Desktop/src/components/ReactBase.js deleted file mode 100644 index da695b918..000000000 --- a/Desktop/src/components/ReactBase.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import {Provider} from "react-redux"; -import reducers from "../store"; -import Root from "./Root"; - -const ReactBase = () => { - return ( - - - - - - ); -}; - -export default ReactBase; diff --git a/Desktop/images/banner.jpg b/Desktop/src/images/images/banner.jpg similarity index 100% rename from Desktop/images/banner.jpg rename to Desktop/src/images/images/banner.jpg diff --git a/Desktop/images/pfp.png b/Desktop/src/images/images/pfp.png similarity index 100% rename from Desktop/images/pfp.png rename to Desktop/src/images/images/pfp.png diff --git a/Desktop/images/superalgos.png b/Desktop/src/images/images/superalgos.png similarity index 100% rename from Desktop/images/superalgos.png rename to Desktop/src/images/images/superalgos.png diff --git a/Desktop/src/index.js b/Desktop/src/index.js index 5e714912e..841a2a655 100644 --- a/Desktop/src/index.js +++ b/Desktop/src/index.js @@ -2,13 +2,25 @@ import './index.css'; import React from 'react'; import ReactDOM from 'react-dom'; import reportWebVitals from './reportWebVitals'; -import ReactBase from "./components/ReactBase"; +// import {SA, UI} from '../UI/Globals'; +// import {loadSuperalgos} from '../UI/WebAppPreLoader' +import {Provider} from "react-redux"; +import reducers from "./store"; +import Root from "./components/Root"; // const store = createStore(reducers); +// loadSuperalgos(); +// console.log(UI); +// console.log(SA); + ReactDOM.render( - , + + + + + , document.getElementById('root') ); diff --git a/Desktop/WebServer/css/main.css b/Desktop/src/main.css similarity index 98% rename from Desktop/WebServer/css/main.css rename to Desktop/src/main.css index 3dadc5d11..3434da2da 100644 --- a/Desktop/WebServer/css/main.css +++ b/Desktop/src/main.css @@ -1,3 +1,5 @@ +/* TODO migrate to react components*/ + /* latin */ @font-face { font-family: 'Saira'; @@ -21,11 +23,6 @@ font-weight: normal; font-style: normal; } - - .body{ - overflow-y: hidden; - overflow-x: hidden; - } .scroll-div { position:absolute; diff --git a/Desktop/store/index.js b/Desktop/store/index.js deleted file mode 100644 index 9874c25de..000000000 --- a/Desktop/store/index.js +++ /dev/null @@ -1,9 +0,0 @@ - -import { configureStore } from '@reduxjs/toolkit'; -import ExampleReducer from './slices/Example.slice' - -export default configureStore({ - reducer:{ - example: ExampleReducer - } - }) diff --git a/Desktop/store/slices/Example.slice.js b/Desktop/store/slices/Example.slice.js deleted file mode 100644 index 7e1d9b0c7..000000000 --- a/Desktop/store/slices/Example.slice.js +++ /dev/null @@ -1,21 +0,0 @@ -import { createSlice } from '@reduxjs/toolkit' - -export const ExampleSlice = createSlice({ - name: 'example', - initialState: { - exampleName:'', - token:'' - }, - reducers: { - exampleAction: (state, action) => { - state.userName = action.payload; - }, - }, - }) - - // Action creators are generated for each case reducer function - export const { exampleAction } = ExampleSlice.actions - - export default ExampleSlice.reducer - - From 7ed3876d1933ec7aaa717b62a30a3f66d26860c5 Mon Sep 17 00:00:00 2001 From: Axel Date: Mon, 29 Nov 2021 18:35:22 -0300 Subject: [PATCH 03/15] add validation to ProfileConstructor addSigningAccounts to work when no nodes are defined --- Projects/Governance/UI/Function-Libraries/ProfileConstructor.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Projects/Governance/UI/Function-Libraries/ProfileConstructor.js b/Projects/Governance/UI/Function-Libraries/ProfileConstructor.js index 106575b8e..f8f178956 100644 --- a/Projects/Governance/UI/Function-Libraries/ProfileConstructor.js +++ b/Projects/Governance/UI/Function-Libraries/ProfileConstructor.js @@ -232,6 +232,7 @@ function newGovernanceFunctionLibraryProfileConstructor() { addSigningAccounts(p2pNetworkNodes, 'P2P Network Node') function addSigningAccounts(nodeArray, targetNodeType) { + if (nodeArray === undefined) return; for (let i = 0; i < nodeArray.length; i++) { let currentNode = nodeArray[i] addSigningAccount(currentNode, targetNodeType, i + 1) From c52629cda25ee40d09a07a46f4c85a3c20c2c432 Mon Sep 17 00:00:00 2001 From: Axel Date: Mon, 29 Nov 2021 21:44:46 -0300 Subject: [PATCH 04/15] Change WebAppPreLoader Test and refactor Updated own user-profile --- Desktop/DesktopApp.js | 4 +- Desktop/UI/WebAppLoader.js | 52 ++-- Desktop/UI/WebAppPreLoader.js | 108 ++------ Desktop/config/paths.js | 13 +- Desktop/src/index.js | 25 +- Desktop/src/store/index.js | 2 +- Desktop/src/store/slices/Example.slice.js | 21 -- Desktop/src/store/slices/Globals.js | 27 ++ .../Plugins/User-Profiles/Loui-Molina.json | 258 +++++++++--------- 9 files changed, 220 insertions(+), 290 deletions(-) delete mode 100644 Desktop/src/store/slices/Example.slice.js create mode 100644 Desktop/src/store/slices/Globals.js diff --git a/Desktop/DesktopApp.js b/Desktop/DesktopApp.js index 4e8afbaad..f1afac09f 100644 --- a/Desktop/DesktopApp.js +++ b/Desktop/DesktopApp.js @@ -73,10 +73,12 @@ exports.newDesktopApp = function newDesktopApp() { /* TODO refactor this into something better */ - let preloader = require('./UI/WebAppPreLoader'); + require('./UI/WebAppPreLoader').loadSuperalgos(); /* let react = require('./scripts/start'); react.start(JSON.parse(DK.desktopApp.p2pNetworkClient.node.config).webPort);*/ + + /* 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) diff --git a/Desktop/UI/WebAppLoader.js b/Desktop/UI/WebAppLoader.js index ce4e46b77..1d684c82a 100644 --- a/Desktop/UI/WebAppLoader.js +++ b/Desktop/UI/WebAppLoader.js @@ -1,34 +1,29 @@ -function newWebAppLoader() { +exports.newWebAppLoader = (UI) => { - let thisObject = { - loadModules: loadModules - } + try { + let modulesArray = [ + 'WebDebugLog.js', + 'WebApp.js' + ] - return thisObject + for (let i = 0; i < UI.schemas.projectSchema.length; i++) { + let project = UI.schemas.projectSchema[i] - async function loadModules() { - try { - let modulesArray = [ - 'WebDebugLog.js', - 'WebApp.js' - ] - - for (let i = 0; i < UI.schemas.projectSchema.length; i++) { - let project = UI.schemas.projectSchema[i] - - if (project.UI !== undefined) { - if (project.UI.functionLibraries !== undefined) { - for (let j = 0; j < project.UI.functionLibraries.length; j++) { - let fileName = project.UI.functionLibraries[j].fileName - if (fileName === undefined) {fileName = project.UI.functionLibraries[j].name.replaceAll(' ', '') + '.js'} - modulesArray.push('Projects' + '/' + project.name + '/' + 'UI' + '/' + 'Function-Libraries' + '/' + fileName) + if (project.UI !== undefined) { + if (project.UI.functionLibraries !== undefined) { + for (let j = 0; j < project.UI.functionLibraries.length; j++) { + let fileName = project.UI.functionLibraries[j].fileName + if (fileName === undefined) { + fileName = project.UI.functionLibraries[j].name.replaceAll(' ', '') + '.js' } + modulesArray.push('Projects' + '/' + project.name + '/' + 'UI' + '/' + 'Function-Libraries' + '/' + fileName) } - if (project.UI.utilities !== undefined) { - for (let j = 0; j < project.UI.utilities.length; j++) { - let fileName = project.UI.utilities[j].fileName - if (fileName === undefined) {fileName = project.UI.utilities[j].name.replaceAll(' ', '') + '.js'} + } + if (project.UI.utilities !== undefined) { + for (let j = 0; j < project.UI.utilities.length; j++) { + let fileName = project.UI.utilities[j].fileName + if (fileName === undefined) {fileName = project.UI.utilities[j].name.replaceAll(' ', '') + '.js'} modulesArray.push('Projects' + '/' + project.name + '/' + 'UI' + '/' + 'Utilities' + '/' + fileName) } } @@ -109,10 +104,9 @@ function newWebAppLoader() { } } } - - } catch (err) { - console.log('[ERROR] loadModules -> err = ' + err.stack) - } + } catch (err) { + console.log('[ERROR] loadModules -> err = ' + err.stack) } + } diff --git a/Desktop/UI/WebAppPreLoader.js b/Desktop/UI/WebAppPreLoader.js index 5676c2925..95e218eec 100644 --- a/Desktop/UI/WebAppPreLoader.js +++ b/Desktop/UI/WebAppPreLoader.js @@ -1,103 +1,33 @@ -loadSuperalgos(); - -function loadSuperalgos() { - - /* - let browser = checkBrowserVersion() - if ((browser.name !== "Chrome" && browser.name !== "Safari") || (browser.name === "Chrome" && parseInt(browser.version) < 85) || (browser.name === "Safari" && parseInt(browser.version) < 13)) { - alert("Superalgos is officially supported on Google Chrome 85 or Safari 13.1 and above. Your browser version has been detected as potentially beneath this. If you continue you may experience some functionality issues.\n\nDetected Browser: " + browser.name + "\nVersion: " + browser.version) - } - */ +exports.loadSuperalgos = () => { loadGlobals() function loadGlobals() { - - let globals = require('./Globals'); - globals.UI.environment = require('../../Environment') - setupClientNode() + let {UI} = require('./Globals'); + setupEnvironment(UI); } - function setupClientNode() { - httpRequest(undefined, 'ClientNode', onResponse) - - function onResponse(err, file) { - UI.clientNode = JSON.parse(file) - setupProjectsSchema() - } + function setupEnvironment(UI) { + let ENVIRONMENT = require('../../Environment'); + UI.environment = ENVIRONMENT.newEnvironment(); + setupClientNode(UI); } - function setupProjectsSchema() { - httpRequest(undefined, 'ProjectsSchema', onResponse) - - function onResponse(err, file) { - UI.schemas.projectSchema = JSON.parse(file) - loadModules() - } + function setupClientNode(UI) { + UI.clientNode = DK.desktopApp.p2pNetworkClient.node; + setupProjectsSchema(UI); } - function loadModules() { - let path = "WebAppLoader.js" - REQUIREJS([path], onRequired) - - function onRequired(pModule) { - let APP_LOADER_MODULE = newWebAppLoader() - APP_LOADER_MODULE.loadModules() - } - } -} - -function httpRequest(pContentToSend, pPath, callBackFunction) { - let xmlHttpRequest = new XMLHttpRequest() - xmlHttpRequest.onreadystatechange = function () { - if (this.readyState === 4 && this.status === 200) { - try { - callBackFunction(undefined, xmlHttpRequest.responseText) - } catch (err) { - console.log('[ERROR] httpRequest -> httpRequest -> err.stack = ' + err.stack) - console.log('[ERROR] httpRequest -> httpRequest -> pContentToSend = ' + pContentToSend) - console.log('[ERROR] httpRequest -> httpRequest -> pPath = ' + pPath) - console.log('[ERROR] httpRequest -> httpRequest -> xmlHttpRequest.responseText = ' + xmlHttpRequest.responseText) - console.log('[ERROR] httpRequest -> httpRequest -> callBackFunction = ' + callBackFunction) - - } - return - } else if (this.readyState === 4 && this.status === 404) { - callBackFunction({ result: "Fail", message: xmlHttpRequest.responseText.trim(), code: xmlHttpRequest.responseText.trim() }) - return - } + function setupProjectsSchema(UI) { + UI.schemas.projectSchema = require('../../Projects/ProjectsSchema.json'); + loadModules(UI) } - if (pContentToSend === undefined) { - xmlHttpRequest.open("GET", pPath, true) - xmlHttpRequest.send() - } else { - try { - let blob = new Blob([pContentToSend], { type: 'text/plain' }) - xmlHttpRequest.open("POST", pPath, true) - xmlHttpRequest.send(blob) - } catch (err) { - if (ERROR_LOG === true) { console.log("[ERROR] callServer -> err.message = " + err.message) } - callBackFunction({ result: "Fail", message: err.message }) - } - } -} + function loadModules(UI) { + let react = require('../scripts/start'); + react.start(JSON.parse(DK.desktopApp.p2pNetworkClient.node.config).webPort); -function checkBrowserVersion() { - - let ua = navigator.userAgent, tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [] - if (/trident/i.test(M[1])) { - tem = /\brv[ :]+(\d+)/g.exec(ua) || [] - return { name: 'IE', version: (tem[1] || '') } + /* let APP_LOADER_MODULE = require('./WebAppLoader'); + APP_LOADER_MODULE.newWebAppLoader(UI);*/ } - if (M[1] === 'Chrome') { - tem = ua.match(/\bOPR|Edge\/(\d+)/) - if (tem != null) { return { name: 'Opera', version: tem[1] } } - } - M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'] - if ((tem = ua.match(/version\/(\d+)/i)) != null) { M.splice(1, 1, tem[1]) } - return { - name: M[0], - version: M[1] - } -} +} \ No newline at end of file diff --git a/Desktop/config/paths.js b/Desktop/config/paths.js index fa9e0bf67..8df8e760e 100644 --- a/Desktop/config/paths.js +++ b/Desktop/config/paths.js @@ -22,7 +22,7 @@ const publicUrlOrPath = getPublicUrlOrPath( process.env.PUBLIC_URL ); -const buildPath = process.env.BUILD_PATH || 'build'; +const buildPath = process.env.BUILD_PATH || 'Desktop/build'; const moduleFileExtensions = [ 'web.mjs', @@ -52,7 +52,6 @@ const resolveModule = (resolveFn, filePath) => { }; // config after eject: we're in ./config/ -/* TODO check which is the correct path*/ module.exports = { dotenv: resolveApp('.env'), appPath: resolveApp('.'), @@ -62,13 +61,11 @@ module.exports = { appIndexJs: resolveModule(resolveApp, 'Desktop/src/index'), appPackageJson: resolveApp('package.json'), appSrc: resolveApp('Desktop/src'), - appTsConfig: resolveApp('tsconfig.json'), - appJsConfig: resolveApp('jsconfig.json'), - yarnLockFile: resolveApp('yarn.lock'), - testsSetup: resolveModule(resolveApp, 'Desktop/src/setupTests'), - proxySetup: resolveApp('Desktop/src/setupProxy.js'), appNodeModules: resolveApp('node_modules'), - swSrc: resolveModule(resolveApp, 'Desktop/src/service-worker'), + globals: resolveApp('Desktop/UI/Globals'), + projects: resolveApp('Projects'), + environment: resolveApp('Environments'), + schema: resolveApp('Projects/ProjectsSchema'), publicUrlOrPath, }; diff --git a/Desktop/src/index.js b/Desktop/src/index.js index 841a2a655..d7f9b6349 100644 --- a/Desktop/src/index.js +++ b/Desktop/src/index.js @@ -1,19 +1,21 @@ import './index.css'; import React from 'react'; import ReactDOM from 'react-dom'; -import reportWebVitals from './reportWebVitals'; -// import {SA, UI} from '../UI/Globals'; -// import {loadSuperalgos} from '../UI/WebAppPreLoader' import {Provider} from "react-redux"; import reducers from "./store"; import Root from "./components/Root"; -// const store = createStore(reducers); - - -// loadSuperalgos(); -// console.log(UI); -// console.log(SA); +/* + TODO save +exports.UI = { + projects: {}, + schemas: { projectSchema: undefined }, + environment: undefined, + clientNode: undefined, + webApp: undefined +} +exports.SA = { projects: {} } +*/ ReactDOM.render( @@ -23,8 +25,3 @@ ReactDOM.render( , document.getElementById('root') ); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/Desktop/src/store/index.js b/Desktop/src/store/index.js index 9874c25de..8085ae199 100644 --- a/Desktop/src/store/index.js +++ b/Desktop/src/store/index.js @@ -1,6 +1,6 @@ import { configureStore } from '@reduxjs/toolkit'; -import ExampleReducer from './slices/Example.slice' +import ExampleReducer from './slices/Globals' export default configureStore({ reducer:{ diff --git a/Desktop/src/store/slices/Example.slice.js b/Desktop/src/store/slices/Example.slice.js deleted file mode 100644 index 7e1d9b0c7..000000000 --- a/Desktop/src/store/slices/Example.slice.js +++ /dev/null @@ -1,21 +0,0 @@ -import { createSlice } from '@reduxjs/toolkit' - -export const ExampleSlice = createSlice({ - name: 'example', - initialState: { - exampleName:'', - token:'' - }, - reducers: { - exampleAction: (state, action) => { - state.userName = action.payload; - }, - }, - }) - - // Action creators are generated for each case reducer function - export const { exampleAction } = ExampleSlice.actions - - export default ExampleSlice.reducer - - diff --git a/Desktop/src/store/slices/Globals.js b/Desktop/src/store/slices/Globals.js new file mode 100644 index 000000000..fa33b3486 --- /dev/null +++ b/Desktop/src/store/slices/Globals.js @@ -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 + + diff --git a/Projects/Governance/Plugins/User-Profiles/Loui-Molina.json b/Projects/Governance/Plugins/User-Profiles/Loui-Molina.json index eff2181c6..9688b6015 100644 --- a/Projects/Governance/Plugins/User-Profiles/Loui-Molina.json +++ b/Projects/Governance/Plugins/User-Profiles/Loui-Molina.json @@ -12,12 +12,12 @@ "id": "f439a686-0607-469f-936f-60f0f672d975", "savedPayload": { "position": { - "x": 8370.804674504796, - "y": 11356.132034355964 + "x": 37287.867965644036, + "y": 19012.132034355964 }, "targetPosition": { - "x": 8582.93670886076, - "y": 11144 + "x": 37500, + "y": 18800 }, "floatingObject": { "isPinned": false, @@ -39,12 +39,12 @@ "id": "f3c83ef5-fd88-4db5-85b9-2496f483c98e", "savedPayload": { "position": { - "x": 8293.158960974039, - "y": 11221.645713530756 + "x": 37222.83614024661, + "y": 18914.80502970953 }, "targetPosition": { - "x": 8582.93670886076, - "y": 11144 + "x": 37500, + "y": 18800 }, "floatingObject": { "isPinned": false, @@ -59,92 +59,65 @@ } } }, - "signingAccounts": { - "type": "Signing Accounts", - "name": "New Signing Accounts", + "userApps": { + "type": "User Apps", + "name": "New User Apps", + "config": "{}", "project": "Governance", - "signingAccounts": [ - { - "type": "Signing Account", - "name": "New Signing Account", - "config": "{\n \"signature\": {\n \"message\": \"Loui-Molina\",\n \"messageHash\": \"0x6de0af55e96cd9355a0b5ecf1118d8ef9e919dbf03cb1a292f475c515ff35970\",\n \"v\": \"0x1b\",\n \"r\": \"0x20b662a60f3a74895f971e3f9597722e249d1409c105f9ec526aa1c11835b290\",\n \"s\": \"0x2aac4af8db1acaae33674ea184f1d6ac45d31e41631a3cea3a35e076a2fd62af\",\n \"signature\": \"0x20b662a60f3a74895f971e3f9597722e249d1409c105f9ec526aa1c11835b2902aac4af8db1acaae33674ea184f1d6ac45d31e41631a3cea3a35e076a2fd62af1b\"\n }\n}", - "project": "Governance", - "signingAccountChild": { - "type": "P2P Network Node", - "name": "P2P Network Node # 1", - "config": "{\n \"codeName\": \"P2P-Network-Node-1\",\n \"host\": \"localhost\",\n \"webPort\": \"31248\",\n \"webSocketsPort\": \"18042\"\n}", - "project": "Governance", - "id": "3ad8b03c-e301-4767-9e94-55d5010840e8", - "savedPayload": { - "position": { - "x": 7698.292044149752, - "y": 11144.670001801276 - }, - "targetPosition": { - "x": 7995.725502561895, - "y": 11105.51214413526 - }, - "floatingObject": { - "isPinned": false, - "isFrozen": false, - "isCollapsed": false, - "angleToParent": 1, - "distanceToParent": 3, - "arrangementStyle": 0 - }, - "uiObject": { - "isRunning": false - } - } - }, - "id": "359330c8-f24f-42a4-b6c6-b90b09ab9774", - "savedPayload": { - "position": { - "x": 7995.725502561895, - "y": 11105.51214413526 - }, - "targetPosition": { - "x": 8293.158960974039, - "y": 11066.354286469244 - }, - "floatingObject": { - "isPinned": false, - "isFrozen": false, - "isCollapsed": false, - "angleToParent": 3, - "distanceToParent": 3, - "arrangementStyle": 0 - }, - "uiObject": { - "isRunning": false - } - } - }, - { - "type": "Signing Account", - "name": "New Signing Account", - "config": "{\n \"signature\": {\n \"message\": \"Loui-Molina\",\n \"messageHash\": \"0x6de0af55e96cd9355a0b5ecf1118d8ef9e919dbf03cb1a292f475c515ff35970\",\n \"v\": \"0x1b\",\n \"r\": \"0x1cc898e6f281914282e184e486f116d9a96c1a8826756a0f6d2874b5b2e756ea\",\n \"s\": \"0x291ce017488c78173cc3380d9db8f0768fedf3a72a8409f9c8873297fd6a4261\",\n \"signature\": \"0x1cc898e6f281914282e184e486f116d9a96c1a8826756a0f6d2874b5b2e756ea291ce017488c78173cc3380d9db8f0768fedf3a72a8409f9c8873297fd6a42611b\"\n }\n}", - "project": "Governance", - "signingAccountChild": { + "desktopApps": { + "type": "Desktop Apps", + "name": "New Desktop Apps", + "config": "{}", + "project": "Governance", + "socialTradingDesktopApps": [ + { "type": "Social Trading Desktop App", - "name": "Social Trading Desktop App # 1", + "name": "Social Trading Desktop App #1", "config": "{\n \"codeName\": \"Social-Trading-Desktop-App-1\",\n \"host\": \"localhost\",\n \"webPort\": \"33248\",\n \"webSocketsPort\": \"17042\"\n}", "project": "Governance", - "id": "7e413033-5b4d-445d-9852-8d2dadd42bdd", + "signingAccount": { + "type": "Signing Account", + "name": "New Signing Account", + "config": "{\n \"codeName\": \"Social-Trading-Desktop-App-1\",\n \"signature\": {\n \"message\": \"Loui-Molina\",\n \"messageHash\": \"0x6de0af55e96cd9355a0b5ecf1118d8ef9e919dbf03cb1a292f475c515ff35970\",\n \"v\": \"0x1c\",\n \"r\": \"0x4399d38c8fd532a1e84c8ed4682bd5e5da68547e38518a8f7642d396767304cf\",\n \"s\": \"0x600be404cbf141fa209d5633f06c2551893458af65581f1c3e087537156d2e22\",\n \"signature\": \"0x4399d38c8fd532a1e84c8ed4682bd5e5da68547e38518a8f7642d396767304cf600be404cbf141fa209d5633f06c2551893458af65581f1c3e087537156d2e221c\"\n }\n}", + "project": "Governance", + "id": "c0fd597c-751a-49bc-8902-4b4232ba2e0a", + "savedPayload": { + "position": { + "x": 36300, + "y": 18800 + }, + "targetPosition": { + "x": 36600, + "y": 18800 + }, + "floatingObject": { + "isPinned": false, + "isFrozen": false, + "isCollapsed": false, + "angleToParent": 3, + "distanceToParent": 3, + "arrangementStyle": 0 + }, + "uiObject": { + "isRunning": false + } + } + }, + "id": "5b2ceb2c-3979-4583-937b-f85a7e8bfebb", "savedPayload": { "position": { - "x": 7713.603465200598, - "y": 10911.062859407733 + "x": 36600, + "y": 18800 }, "targetPosition": { - "x": 8003.381213087318, - "y": 10988.708572938489 + "x": 36900, + "y": 18800 }, "floatingObject": { "isPinned": false, "isFrozen": false, "isCollapsed": false, - "angleToParent": 1, + "angleToParent": 3, "distanceToParent": 3, "arrangementStyle": 0 }, @@ -152,55 +125,86 @@ "isRunning": false } } - }, - "id": "d896dfde-6ce2-43f0-aae5-f65406e3ccb0", - "savedPayload": { - "position": { - "x": 8003.381213087318, - "y": 10988.708572938489 - }, - "targetPosition": { - "x": 8293.158960974039, - "y": 11066.354286469244 - }, - "floatingObject": { - "isPinned": false, - "isFrozen": false, - "isCollapsed": false, - "angleToParent": 3, - "distanceToParent": 3, - "arrangementStyle": 0 - }, - "uiObject": { - "isRunning": false - } } + ], + "algoTradesPlatform": [], + "id": "51a98c64-ba95-43d3-bcfc-3f6b6821c5be", + "savedPayload": { + "position": { + "x": 36900, + "y": 18800 + }, + "targetPosition": { + "x": 37200, + "y": 18800 + }, + "floatingObject": { + "isPinned": false, + "isFrozen": false, + "isCollapsed": false, + "angleToParent": 3, + "distanceToParent": 3, + "arrangementStyle": 0 + }, + "uiObject": { + "isRunning": false + } + } + }, + "id": "0f6d57b2-71a1-4543-82a5-f3560df9090b", + "savedPayload": { + "position": { + "x": 37200, + "y": 18800 }, + "targetPosition": { + "x": 37500, + "y": 18800 + }, + "floatingObject": { + "isPinned": false, + "isFrozen": false, + "isCollapsed": false, + "angleToParent": 3, + "distanceToParent": 3, + "arrangementStyle": 0 + }, + "uiObject": { + "isRunning": false + } + } + }, + "p2pNetworkNodes": { + "type": "P2P Network Nodes", + "name": "New P2P Network Nodes", + "config": "{}", + "project": "Governance", + "p2pNetworkNodes": [ { - "type": "Signing Account", - "name": "New Signing Account", - "config": "{\n \"signature\": {\n \"message\": \"Loui-Molina\",\n \"messageHash\": \"0x6de0af55e96cd9355a0b5ecf1118d8ef9e919dbf03cb1a292f475c515ff35970\",\n \"v\": \"0x1c\",\n \"r\": \"0x8a5a28c86ceff0ae465c187ba8a22b428f3f10b8a99d4d95195ab471b664914f\",\n \"s\": \"0x66920073303073240bc4ebf6363788e3b76661dfc2366561261fa7812eedf18b\",\n \"signature\": \"0x8a5a28c86ceff0ae465c187ba8a22b428f3f10b8a99d4d95195ab471b664914f66920073303073240bc4ebf6363788e3b76661dfc2366561261fa7812eedf18b1c\"\n }\n}", + "type": "P2P Network Node", + "name": "P2P Network Node #1", + "config": "{\n \"codeName\": \"P2P-Network-Node-1\",\n \"host\": \"localhost\",\n \"webPort\": \"31248\",\n \"webSocketsPort\": \"18042\"\n}", "project": "Governance", - "signingAccountChild": { - "type": "Social Trading Server App", - "name": "Social Trading Server App # 1", - "config": "{\n \"codeName\": \"Social-Trading-Server-App-1\",\n \"host\": \"localhost\",\n \"webSocketsPort\": \"16042\"\n}", + "signingAccount": { + "type": "Signing Account", + "name": "New Signing Account", + "config": "{\n \"codeName\": \"P2P-Network-Node-1\",\n \"signature\": {\n \"message\": \"Loui-Molina\",\n \"messageHash\": \"0x6de0af55e96cd9355a0b5ecf1118d8ef9e919dbf03cb1a292f475c515ff35970\",\n \"v\": \"0x1b\",\n \"r\": \"0x60e982f313e3a6621199186afb653a89dfc616b333a4e89505a293834f1fc975\",\n \"s\": \"0x794a27d314980510df0bb1ee7fc9bf5d71eac027f02c77e637ba68cb1edb176f\",\n \"signature\": \"0x60e982f313e3a6621199186afb653a89dfc616b333a4e89505a293834f1fc975794a27d314980510df0bb1ee7fc9bf5d71eac027f02c77e637ba68cb1edb176f1b\"\n }\n}", "project": "Governance", - "id": "39418f01-fba1-4d9b-9484-0cd8642607e0", + "id": "e17583e1-a52f-42e1-86ec-17c1e924a1ff", "savedPayload": { "position": { - "x": 7817.146956799297, - "y": 10701.097429064013 + "x": 36668.50842073983, + "y": 18455.584910871425 }, "targetPosition": { - "x": 8055.152958886668, - "y": 10883.725857766629 + "x": 36945.67228049322, + "y": 18570.38994058095 }, "floatingObject": { "isPinned": false, "isFrozen": false, "isCollapsed": false, - "angleToParent": 1, + "angleToParent": 3, "distanceToParent": 3, "arrangementStyle": 0 }, @@ -209,15 +213,15 @@ } } }, - "id": "14e9d5eb-cdf9-48e9-ae7a-a2e1ac178c5e", + "id": "9f1b2ef2-9a62-4188-9c3e-7fc890f1d1c1", "savedPayload": { "position": { - "x": 8055.152958886668, - "y": 10883.725857766629 + "x": 36945.67228049322, + "y": 18570.38994058095 }, "targetPosition": { - "x": 8293.158960974039, - "y": 11066.354286469244 + "x": 37222.83614024661, + "y": 18685.194970290475 }, "floatingObject": { "isPinned": false, @@ -233,15 +237,15 @@ } } ], - "id": "deaff2f6-59f5-4c7e-8755-b0bff6ea9157", + "id": "153f9f68-9d5e-4633-bd1a-f29304f27838", "savedPayload": { "position": { - "x": 8293.158960974039, - "y": 11066.354286469244 + "x": 37222.83614024661, + "y": 18685.194970290475 }, "targetPosition": { - "x": 8582.93670886076, - "y": 11144 + "x": 37500, + "y": 18800 }, "floatingObject": { "isPinned": false, @@ -269,7 +273,7 @@ "floatingObject": { "isPinned": false, "isFrozen": false, - "isCollapsed": true, + "isCollapsed": false, "angleToParent": 1, "distanceToParent": 3, "arrangementStyle": 0 From 9def104a84e44cb8843f25e567c1a0ea48c11bae Mon Sep 17 00:00:00 2001 From: Axel Date: Mon, 29 Nov 2021 23:00:23 -0300 Subject: [PATCH 05/15] Restore web server files --- Desktop/DesktopApp.js | 10 +-- Desktop/UI/Globals.js | 9 ++- Desktop/UI/WebApp.js | 18 +----- Desktop/UI/WebAppLoader.js | 52 ++++++++------- Desktop/UI/WebAppPreLoader.js | 116 ++++++++++++++++++++++++++++------ Desktop/config/paths.js | 9 ++- Desktop/public/index.html | 2 - 7 files changed, 145 insertions(+), 71 deletions(-) diff --git a/Desktop/DesktopApp.js b/Desktop/DesktopApp.js index f1afac09f..ceddff1da 100644 --- a/Desktop/DesktopApp.js +++ b/Desktop/DesktopApp.js @@ -73,11 +73,13 @@ exports.newDesktopApp = function newDesktopApp() { /* TODO refactor this into something better */ - require('./UI/WebAppPreLoader').loadSuperalgos(); - - /* let react = require('./scripts/start'); - react.start(JSON.parse(DK.desktopApp.p2pNetworkClient.node.config).webPort);*/ + /* + require('./src/UI/WebAppPreLoader').loadSuperalgos(); + */ + /* let react = require('./scripts/start'); + react.start(JSON.parse(DK.desktopApp.p2pNetworkClient.node.config).webPort); +*/ /* thisObject.httpInterface = DK.projects.socialTrading.modules.httpInterface.newDesktopModulesHttpInterface() thisObject.httpInterface.initialize()*/ diff --git a/Desktop/UI/Globals.js b/Desktop/UI/Globals.js index c78ce9079..3d3b69587 100644 --- a/Desktop/UI/Globals.js +++ b/Desktop/UI/Globals.js @@ -1,6 +1,6 @@ /* Root object*/ /* Root objects*/ -exports.UI = { +let UI = { projects: {}, schemas: { projectSchema: undefined }, environment: undefined, @@ -8,7 +8,10 @@ exports.UI = { webApp: undefined } -exports.SA = { projects: {} } - +let SA = { projects: {} } +/* +We will use this to load functions originally intended for Nodejs +*/ +let exports = {} diff --git a/Desktop/UI/WebApp.js b/Desktop/UI/WebApp.js index b4e634eff..cb4b4da43 100644 --- a/Desktop/UI/WebApp.js +++ b/Desktop/UI/WebApp.js @@ -5,7 +5,7 @@ function newWebApp() { Everything is being coded here until some structure emerges. */ let thisObject = { - messageReceived: messageReceived, + messageReceived: messageReceived, initialize: initialize, finalize: finalize } @@ -21,7 +21,6 @@ function newWebApp() { setupRootObject(UI, 'UI') setupRootObject(SA, 'SA') await UI.projects.socialTrading.modules.webSocketsWebAppClient.initialize() - // loadReactDom(); loadWUserProfileTimeline() loadWhoToFollow() setupEventHandlers() @@ -101,15 +100,6 @@ function newWebApp() { } } - async function loadReactDom() { - - reactReqs.ReactDOM.render( - reactReqs.ReactBase, - document.getElementById('root') - ); - - } - async function loadWUserProfileTimeline() { let queryMessage = { queryType: SA.projects.socialTrading.globals.queryTypes.EVENTS, @@ -173,7 +163,8 @@ function newWebApp() { postDiv.appendChild(textNode) contentDiv.appendChild(postDiv) } - } catch (err) { + } + catch (err) { console.log('[ERROR] err.stack = ' + err.stack) } } @@ -294,7 +285,6 @@ function newWebApp() { function updateTextArea() { textArea.value = "" } - break } case 'Follow Profile': { @@ -312,7 +302,6 @@ function newWebApp() { button.setAttribute("class", "profile-to-unfollow-button") button.name = 'Unfollow Profile' } - break } case 'Unfollow Profile': { @@ -330,7 +319,6 @@ function newWebApp() { button.setAttribute("class", "profile-to-follow-button") button.name = 'Follow Profile' } - break } } diff --git a/Desktop/UI/WebAppLoader.js b/Desktop/UI/WebAppLoader.js index 1d684c82a..ce4e46b77 100644 --- a/Desktop/UI/WebAppLoader.js +++ b/Desktop/UI/WebAppLoader.js @@ -1,29 +1,34 @@ -exports.newWebAppLoader = (UI) => { +function newWebAppLoader() { - try { - let modulesArray = [ - 'WebDebugLog.js', - 'WebApp.js' - ] + let thisObject = { + loadModules: loadModules + } - for (let i = 0; i < UI.schemas.projectSchema.length; i++) { - let project = UI.schemas.projectSchema[i] + return thisObject - if (project.UI !== undefined) { - if (project.UI.functionLibraries !== undefined) { - for (let j = 0; j < project.UI.functionLibraries.length; j++) { - let fileName = project.UI.functionLibraries[j].fileName - if (fileName === undefined) { - fileName = project.UI.functionLibraries[j].name.replaceAll(' ', '') + '.js' + async function loadModules() { + try { + let modulesArray = [ + 'WebDebugLog.js', + 'WebApp.js' + ] + + for (let i = 0; i < UI.schemas.projectSchema.length; i++) { + let project = UI.schemas.projectSchema[i] + + if (project.UI !== undefined) { + if (project.UI.functionLibraries !== undefined) { + for (let j = 0; j < project.UI.functionLibraries.length; j++) { + let fileName = project.UI.functionLibraries[j].fileName + if (fileName === undefined) {fileName = project.UI.functionLibraries[j].name.replaceAll(' ', '') + '.js'} + modulesArray.push('Projects' + '/' + project.name + '/' + 'UI' + '/' + 'Function-Libraries' + '/' + fileName) } - modulesArray.push('Projects' + '/' + project.name + '/' + 'UI' + '/' + 'Function-Libraries' + '/' + fileName) } - } - if (project.UI.utilities !== undefined) { - for (let j = 0; j < project.UI.utilities.length; j++) { - let fileName = project.UI.utilities[j].fileName - if (fileName === undefined) {fileName = project.UI.utilities[j].name.replaceAll(' ', '') + '.js'} + if (project.UI.utilities !== undefined) { + for (let j = 0; j < project.UI.utilities.length; j++) { + let fileName = project.UI.utilities[j].fileName + if (fileName === undefined) {fileName = project.UI.utilities[j].name.replaceAll(' ', '') + '.js'} modulesArray.push('Projects' + '/' + project.name + '/' + 'UI' + '/' + 'Utilities' + '/' + fileName) } } @@ -104,9 +109,10 @@ exports.newWebAppLoader = (UI) => { } } } - } catch (err) { - console.log('[ERROR] loadModules -> err = ' + err.stack) - } + } catch (err) { + console.log('[ERROR] loadModules -> err = ' + err.stack) + } + } } diff --git a/Desktop/UI/WebAppPreLoader.js b/Desktop/UI/WebAppPreLoader.js index 95e218eec..548e3da05 100644 --- a/Desktop/UI/WebAppPreLoader.js +++ b/Desktop/UI/WebAppPreLoader.js @@ -1,33 +1,111 @@ -exports.loadSuperalgos = () => { + +function loadSuperalgos() { + + let browser = checkBrowserVersion() + if ((browser.name !== "Chrome" && browser.name !== "Safari") || (browser.name === "Chrome" && parseInt(browser.version) < 85) || (browser.name === "Safari" && parseInt(browser.version) < 13)) { + alert("Superalgos is officially supported on Google Chrome 85 or Safari 13.1 and above. Your browser version has been detected as potentially beneath this. If you continue you may experience some functionaility issues.\n\nDetected Browser: " + browser.name + "\nVersion: " + browser.version) + } loadGlobals() function loadGlobals() { - let {UI} = require('./Globals'); - setupEnvironment(UI); + let path = "Globals.js" + REQUIREJS([path], onRequired) + + function onRequired(pModule) { + setupEnvironment() + } } - function setupEnvironment(UI) { - let ENVIRONMENT = require('../../Environment'); - UI.environment = ENVIRONMENT.newEnvironment(); - setupClientNode(UI); + function setupEnvironment() { + httpRequest(undefined, 'Environment', onResponse) + + function onResponse(err, file) { + UI.environment = JSON.parse(file) + setupClientNode() + } } - function setupClientNode(UI) { - UI.clientNode = DK.desktopApp.p2pNetworkClient.node; - setupProjectsSchema(UI); + function setupClientNode() { + httpRequest(undefined, 'ClientNode', onResponse) + + function onResponse(err, file) { + UI.clientNode = JSON.parse(file) + setupProjectsSchema() + } } - function setupProjectsSchema(UI) { - UI.schemas.projectSchema = require('../../Projects/ProjectsSchema.json'); - loadModules(UI) + function setupProjectsSchema() { + httpRequest(undefined, 'ProjectsSchema', onResponse) + + function onResponse(err, file) { + UI.schemas.projectSchema = JSON.parse(file) + loadModules() + } } - function loadModules(UI) { - let react = require('../scripts/start'); - react.start(JSON.parse(DK.desktopApp.p2pNetworkClient.node.config).webPort); + function loadModules() { + let path = "WebAppLoader.js" + REQUIREJS([path], onRequired) - /* let APP_LOADER_MODULE = require('./WebAppLoader'); - APP_LOADER_MODULE.newWebAppLoader(UI);*/ + function onRequired(pModule) { + let APP_LOADER_MODULE = newWebAppLoader() + APP_LOADER_MODULE.loadModules() + } } -} \ No newline at end of file +} + +function httpRequest(pContentToSend, pPath, callBackFunction) { + let xmlHttpRequest = new XMLHttpRequest() + xmlHttpRequest.onreadystatechange = function () { + if (this.readyState === 4 && this.status === 200) { + try { + callBackFunction(undefined, xmlHttpRequest.responseText) + } catch (err) { + console.log('[ERROR] httpRequest -> httpRequest -> err.stack = ' + err.stack) + console.log('[ERROR] httpRequest -> httpRequest -> pContentToSend = ' + pContentToSend) + console.log('[ERROR] httpRequest -> httpRequest -> pPath = ' + pPath) + console.log('[ERROR] httpRequest -> httpRequest -> xmlHttpRequest.responseText = ' + xmlHttpRequest.responseText) + console.log('[ERROR] httpRequest -> httpRequest -> callBackFunction = ' + callBackFunction) + + } + return + } else if (this.readyState === 4 && this.status === 404) { + callBackFunction({ result: "Fail", message: xmlHttpRequest.responseText.trim(), code: xmlHttpRequest.responseText.trim() }) + return + } + } + + if (pContentToSend === undefined) { + xmlHttpRequest.open("GET", pPath, true) + xmlHttpRequest.send() + } else { + try { + let blob = new Blob([pContentToSend], { type: 'text/plain' }) + xmlHttpRequest.open("POST", pPath, true) + xmlHttpRequest.send(blob) + } catch (err) { + if (ERROR_LOG === true) { console.log("[ERROR] callServer -> err.message = " + err.message) } + callBackFunction({ result: "Fail", message: err.message }) + } + } +} + +function checkBrowserVersion() { + + let ua = navigator.userAgent, tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [] + if (/trident/i.test(M[1])) { + tem = /\brv[ :]+(\d+)/g.exec(ua) || [] + return { name: 'IE', version: (tem[1] || '') } + } + if (M[1] === 'Chrome') { + tem = ua.match(/\bOPR|Edge\/(\d+)/) + if (tem != null) { return { name: 'Opera', version: tem[1] } } + } + M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'] + if ((tem = ua.match(/version\/(\d+)/i)) != null) { M.splice(1, 1, tem[1]) } + return { + name: M[0], + version: M[1] + } +} diff --git a/Desktop/config/paths.js b/Desktop/config/paths.js index 8df8e760e..beb561b99 100644 --- a/Desktop/config/paths.js +++ b/Desktop/config/paths.js @@ -62,12 +62,11 @@ module.exports = { appPackageJson: resolveApp('package.json'), appSrc: resolveApp('Desktop/src'), appNodeModules: resolveApp('node_modules'), - globals: resolveApp('Desktop/UI/Globals'), - projects: resolveApp('Projects'), - environment: resolveApp('Environments'), - schema: resolveApp('Projects/ProjectsSchema'), publicUrlOrPath, }; - +/* globals: resolveApp('Desktop/UI/Globals'), + projects: resolveApp('Projects'), + environment: resolveApp('Environments'), + schema: resolveApp('Projects/ProjectsSchema'),*/ module.exports.moduleFileExtensions = moduleFileExtensions; diff --git a/Desktop/public/index.html b/Desktop/public/index.html index 81a214993..81b324ffe 100644 --- a/Desktop/public/index.html +++ b/Desktop/public/index.html @@ -6,10 +6,8 @@ - - + + + + + + + +
+
+ + + + + + +
+ + + + + + + + + + +
LOGO
PROFILE
+
+ + + + + + + +
CONTENT TITLE
+
+
+ + + + + + + +
SEARCH
+ + + + + + + + + + +
Who to follow
+
Show more
+
+
+
> +
+
+ Placeholder +
+
+ + + + + +
Profile Pic + + + + + + + + + + +
+ +
Permissions
Tool Bar
+
+
+
+ POST 2 +
+
+ POST 3 +
+
+ POST 4 +
+
+ POST 5 +
+
+
+ + + \ No newline at end of file diff --git a/DesktopReact/DesktopApp.js b/DesktopReact/DesktopApp.js new file mode 100644 index 000000000..4ac8410b5 --- /dev/null +++ b/DesktopReact/DesktopApp.js @@ -0,0 +1,81 @@ +const express = require("./backend/src/index"); +exports.newDesktopApp = function newDesktopApp() { + + let thisObject = { + userProfiles: undefined, + p2pNetworkClient: 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.p2pNetworkClient = SA.projects.network.modules.p2pNetworkClient.newNetworkModulesP2PNetworkClient() + await thisObject.p2pNetworkClient.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) + /* + 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.p2pNetworkClient, + 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.p2pNetworkClient.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.p2pNetworkClient.node.config).webPort + 1); + react.start(reactPort); + console.log('react Interface ................................................ Listening at port ' + reactPort); + } + } +} diff --git a/DesktopReact/UI/WebApp.js b/DesktopReact/UI/WebApp.js new file mode 100644 index 000000000..16d781e44 --- /dev/null +++ b/DesktopReact/UI/WebApp.js @@ -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) + } +} diff --git a/Desktop/backend/src/index.js b/DesktopReact/backend/src/index.js similarity index 100% rename from Desktop/backend/src/index.js rename to DesktopReact/backend/src/index.js diff --git a/Desktop/frontend/config/env.js b/DesktopReact/frontend/config/env.js similarity index 100% rename from Desktop/frontend/config/env.js rename to DesktopReact/frontend/config/env.js diff --git a/Desktop/frontend/config/getHttpsConfig.js b/DesktopReact/frontend/config/getHttpsConfig.js similarity index 100% rename from Desktop/frontend/config/getHttpsConfig.js rename to DesktopReact/frontend/config/getHttpsConfig.js diff --git a/Desktop/frontend/config/modules.js b/DesktopReact/frontend/config/modules.js similarity index 100% rename from Desktop/frontend/config/modules.js rename to DesktopReact/frontend/config/modules.js diff --git a/Desktop/frontend/config/paths.js b/DesktopReact/frontend/config/paths.js similarity index 100% rename from Desktop/frontend/config/paths.js rename to DesktopReact/frontend/config/paths.js diff --git a/Desktop/frontend/config/pnpTs.js b/DesktopReact/frontend/config/pnpTs.js similarity index 100% rename from Desktop/frontend/config/pnpTs.js rename to DesktopReact/frontend/config/pnpTs.js diff --git a/Desktop/frontend/config/webpack.config.js b/DesktopReact/frontend/config/webpack.config.js similarity index 100% rename from Desktop/frontend/config/webpack.config.js rename to DesktopReact/frontend/config/webpack.config.js diff --git a/Desktop/frontend/config/webpackDevServer.config.js b/DesktopReact/frontend/config/webpackDevServer.config.js similarity index 100% rename from Desktop/frontend/config/webpackDevServer.config.js rename to DesktopReact/frontend/config/webpackDevServer.config.js diff --git a/DesktopReact/frontend/public/Fonts/Saira-Condensed.woff2 b/DesktopReact/frontend/public/Fonts/Saira-Condensed.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..8a7ff57d055bb5660e8c344870f91c225bfa3fa2 GIT binary patch literal 17000 zcmV(@K-Rx^Pew8T0RR91077U05dZ)H0G03n073--0RR9100000000000000000000 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 BtqcGF literal 0 HcmV?d00001 diff --git a/Desktop/frontend/public/index.html b/DesktopReact/frontend/public/index.html similarity index 100% rename from Desktop/frontend/public/index.html rename to DesktopReact/frontend/public/index.html diff --git a/Desktop/frontend/public/manifest.json b/DesktopReact/frontend/public/manifest.json similarity index 100% rename from Desktop/frontend/public/manifest.json rename to DesktopReact/frontend/public/manifest.json diff --git a/Desktop/frontend/public/robots.txt b/DesktopReact/frontend/public/robots.txt similarity index 100% rename from Desktop/frontend/public/robots.txt rename to DesktopReact/frontend/public/robots.txt diff --git a/Desktop/frontend/scripts/build.js b/DesktopReact/frontend/scripts/build.js similarity index 100% rename from Desktop/frontend/scripts/build.js rename to DesktopReact/frontend/scripts/build.js diff --git a/Desktop/frontend/scripts/start.js b/DesktopReact/frontend/scripts/start.js similarity index 100% rename from Desktop/frontend/scripts/start.js rename to DesktopReact/frontend/scripts/start.js diff --git a/Desktop/frontend/scripts/test.js b/DesktopReact/frontend/scripts/test.js similarity index 100% rename from Desktop/frontend/scripts/test.js rename to DesktopReact/frontend/scripts/test.js diff --git a/Desktop/frontend/src/api/service.js b/DesktopReact/frontend/src/api/service.js similarity index 100% rename from Desktop/frontend/src/api/service.js rename to DesktopReact/frontend/src/api/service.js diff --git a/Desktop/frontend/src/components/Root.js b/DesktopReact/frontend/src/components/Root.js similarity index 100% rename from Desktop/frontend/src/components/Root.js rename to DesktopReact/frontend/src/components/Root.js diff --git a/Desktop/frontend/src/components/User/UserCard.css b/DesktopReact/frontend/src/components/User/UserCard.css similarity index 100% rename from Desktop/frontend/src/components/User/UserCard.css rename to DesktopReact/frontend/src/components/User/UserCard.css diff --git a/Desktop/frontend/src/components/User/UserCard.js b/DesktopReact/frontend/src/components/User/UserCard.js similarity index 100% rename from Desktop/frontend/src/components/User/UserCard.js rename to DesktopReact/frontend/src/components/User/UserCard.js diff --git a/Desktop/frontend/src/components/UsersSidebar/UsersSidebar.css b/DesktopReact/frontend/src/components/UsersSidebar/UsersSidebar.css similarity index 100% rename from Desktop/frontend/src/components/UsersSidebar/UsersSidebar.css rename to DesktopReact/frontend/src/components/UsersSidebar/UsersSidebar.css diff --git a/Desktop/frontend/src/components/UsersSidebar/UsersSidebar.js b/DesktopReact/frontend/src/components/UsersSidebar/UsersSidebar.js similarity index 100% rename from Desktop/frontend/src/components/UsersSidebar/UsersSidebar.js rename to DesktopReact/frontend/src/components/UsersSidebar/UsersSidebar.js diff --git a/Desktop/frontend/src/components/feed/Feed.css b/DesktopReact/frontend/src/components/feed/Feed.css similarity index 100% rename from Desktop/frontend/src/components/feed/Feed.css rename to DesktopReact/frontend/src/components/feed/Feed.css diff --git a/Desktop/frontend/src/components/feed/Feed.js b/DesktopReact/frontend/src/components/feed/Feed.js similarity index 100% rename from Desktop/frontend/src/components/feed/Feed.js rename to DesktopReact/frontend/src/components/feed/Feed.js diff --git a/Desktop/frontend/src/components/home/App.css b/DesktopReact/frontend/src/components/home/App.css similarity index 100% rename from Desktop/frontend/src/components/home/App.css rename to DesktopReact/frontend/src/components/home/App.css diff --git a/Desktop/frontend/src/components/home/App.js b/DesktopReact/frontend/src/components/home/App.js similarity index 100% rename from Desktop/frontend/src/components/home/App.js rename to DesktopReact/frontend/src/components/home/App.js diff --git a/Desktop/frontend/src/components/logo/Logo.css b/DesktopReact/frontend/src/components/logo/Logo.css similarity index 100% rename from Desktop/frontend/src/components/logo/Logo.css rename to DesktopReact/frontend/src/components/logo/Logo.css diff --git a/Desktop/frontend/src/components/logo/Logo.js b/DesktopReact/frontend/src/components/logo/Logo.js similarity index 100% rename from Desktop/frontend/src/components/logo/Logo.js rename to DesktopReact/frontend/src/components/logo/Logo.js diff --git a/Desktop/frontend/src/components/notFound/NotFound.js b/DesktopReact/frontend/src/components/notFound/NotFound.js similarity index 100% rename from Desktop/frontend/src/components/notFound/NotFound.js rename to DesktopReact/frontend/src/components/notFound/NotFound.js diff --git a/Desktop/frontend/src/components/post/Post.css b/DesktopReact/frontend/src/components/post/Post.css similarity index 100% rename from Desktop/frontend/src/components/post/Post.css rename to DesktopReact/frontend/src/components/post/Post.css diff --git a/Desktop/frontend/src/components/post/Post.js b/DesktopReact/frontend/src/components/post/Post.js similarity index 100% rename from Desktop/frontend/src/components/post/Post.js rename to DesktopReact/frontend/src/components/post/Post.js diff --git a/Desktop/frontend/src/components/postPlaceholder/PostPlaceholder.css b/DesktopReact/frontend/src/components/postPlaceholder/PostPlaceholder.css similarity index 100% rename from Desktop/frontend/src/components/postPlaceholder/PostPlaceholder.css rename to DesktopReact/frontend/src/components/postPlaceholder/PostPlaceholder.css diff --git a/Desktop/frontend/src/components/postPlaceholder/PostPlaceholder.js b/DesktopReact/frontend/src/components/postPlaceholder/PostPlaceholder.js similarity index 100% rename from Desktop/frontend/src/components/postPlaceholder/PostPlaceholder.js rename to DesktopReact/frontend/src/components/postPlaceholder/PostPlaceholder.js diff --git a/Desktop/frontend/src/components/postsFeed/PostsFeed.css b/DesktopReact/frontend/src/components/postsFeed/PostsFeed.css similarity index 100% rename from Desktop/frontend/src/components/postsFeed/PostsFeed.css rename to DesktopReact/frontend/src/components/postsFeed/PostsFeed.css diff --git a/Desktop/frontend/src/components/postsFeed/PostsFeed.js b/DesktopReact/frontend/src/components/postsFeed/PostsFeed.js similarity index 100% rename from Desktop/frontend/src/components/postsFeed/PostsFeed.js rename to DesktopReact/frontend/src/components/postsFeed/PostsFeed.js diff --git a/Desktop/frontend/src/components/searchBox/SearchBox.css b/DesktopReact/frontend/src/components/searchBox/SearchBox.css similarity index 100% rename from Desktop/frontend/src/components/searchBox/SearchBox.css rename to DesktopReact/frontend/src/components/searchBox/SearchBox.css diff --git a/Desktop/frontend/src/components/searchBox/SearchBox.js b/DesktopReact/frontend/src/components/searchBox/SearchBox.js similarity index 100% rename from Desktop/frontend/src/components/searchBox/SearchBox.js rename to DesktopReact/frontend/src/components/searchBox/SearchBox.js diff --git a/Desktop/frontend/src/components/showMoreUsers/ShowMoreUsers.js b/DesktopReact/frontend/src/components/showMoreUsers/ShowMoreUsers.js similarity index 100% rename from Desktop/frontend/src/components/showMoreUsers/ShowMoreUsers.js rename to DesktopReact/frontend/src/components/showMoreUsers/ShowMoreUsers.js diff --git a/Desktop/frontend/src/components/sidebar/Sidebar.css b/DesktopReact/frontend/src/components/sidebar/Sidebar.css similarity index 100% rename from Desktop/frontend/src/components/sidebar/Sidebar.css rename to DesktopReact/frontend/src/components/sidebar/Sidebar.css diff --git a/Desktop/frontend/src/components/sidebar/Sidebar.js b/DesktopReact/frontend/src/components/sidebar/Sidebar.js similarity index 100% rename from Desktop/frontend/src/components/sidebar/Sidebar.js rename to DesktopReact/frontend/src/components/sidebar/Sidebar.js diff --git a/Desktop/frontend/src/components/sidebarItem/SidebarItem.css b/DesktopReact/frontend/src/components/sidebarItem/SidebarItem.css similarity index 100% rename from Desktop/frontend/src/components/sidebarItem/SidebarItem.css rename to DesktopReact/frontend/src/components/sidebarItem/SidebarItem.css diff --git a/Desktop/frontend/src/components/sidebarItem/SidebarItem.js b/DesktopReact/frontend/src/components/sidebarItem/SidebarItem.js similarity index 100% rename from Desktop/frontend/src/components/sidebarItem/SidebarItem.js rename to DesktopReact/frontend/src/components/sidebarItem/SidebarItem.js diff --git a/Desktop/frontend/src/components/suggestedUsers/SuggestedUsers.css b/DesktopReact/frontend/src/components/suggestedUsers/SuggestedUsers.css similarity index 100% rename from Desktop/frontend/src/components/suggestedUsers/SuggestedUsers.css rename to DesktopReact/frontend/src/components/suggestedUsers/SuggestedUsers.css diff --git a/Desktop/frontend/src/components/suggestedUsers/SuggestedUsers.js b/DesktopReact/frontend/src/components/suggestedUsers/SuggestedUsers.js similarity index 100% rename from Desktop/frontend/src/components/suggestedUsers/SuggestedUsers.js rename to DesktopReact/frontend/src/components/suggestedUsers/SuggestedUsers.js diff --git a/Desktop/frontend/src/components/userProfile/UserProfile.css b/DesktopReact/frontend/src/components/userProfile/UserProfile.css similarity index 100% rename from Desktop/frontend/src/components/userProfile/UserProfile.css rename to DesktopReact/frontend/src/components/userProfile/UserProfile.css diff --git a/Desktop/frontend/src/components/userProfile/UserProfile.js b/DesktopReact/frontend/src/components/userProfile/UserProfile.js similarity index 100% rename from Desktop/frontend/src/components/userProfile/UserProfile.js rename to DesktopReact/frontend/src/components/userProfile/UserProfile.js diff --git a/Desktop/frontend/src/components/userProfileHeader/UserProfileHeader.css b/DesktopReact/frontend/src/components/userProfileHeader/UserProfileHeader.css similarity index 100% rename from Desktop/frontend/src/components/userProfileHeader/UserProfileHeader.css rename to DesktopReact/frontend/src/components/userProfileHeader/UserProfileHeader.css diff --git a/Desktop/frontend/src/components/userProfileHeader/UserProfileHeader.js b/DesktopReact/frontend/src/components/userProfileHeader/UserProfileHeader.js similarity index 100% rename from Desktop/frontend/src/components/userProfileHeader/UserProfileHeader.js rename to DesktopReact/frontend/src/components/userProfileHeader/UserProfileHeader.js diff --git a/Desktop/frontend/src/images/banner.jpg b/DesktopReact/frontend/src/images/banner.jpg similarity index 100% rename from Desktop/frontend/src/images/banner.jpg rename to DesktopReact/frontend/src/images/banner.jpg diff --git a/DesktopReact/frontend/src/images/favicon.ico b/DesktopReact/frontend/src/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..cd2c5d6afbe08d2ee060535f131cc21ad521575c GIT binary patch literal 15086 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 literal 0 HcmV?d00001 diff --git a/Desktop/frontend/src/images/pfp.png b/DesktopReact/frontend/src/images/pfp.png similarity index 100% rename from Desktop/frontend/src/images/pfp.png rename to DesktopReact/frontend/src/images/pfp.png diff --git a/Desktop/frontend/src/images/superalgos.png b/DesktopReact/frontend/src/images/superalgos.png similarity index 100% rename from Desktop/frontend/src/images/superalgos.png rename to DesktopReact/frontend/src/images/superalgos.png diff --git a/Desktop/frontend/src/index.css b/DesktopReact/frontend/src/index.css similarity index 100% rename from Desktop/frontend/src/index.css rename to DesktopReact/frontend/src/index.css diff --git a/Desktop/frontend/src/index.js b/DesktopReact/frontend/src/index.js similarity index 100% rename from Desktop/frontend/src/index.js rename to DesktopReact/frontend/src/index.js diff --git a/DesktopReact/frontend/src/main.css b/DesktopReact/frontend/src/main.css new file mode 100644 index 000000000..3dadc5d11 --- /dev/null +++ b/DesktopReact/frontend/src/main.css @@ -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; + } \ No newline at end of file diff --git a/Desktop/frontend/src/reportWebVitals.js b/DesktopReact/frontend/src/reportWebVitals.js similarity index 100% rename from Desktop/frontend/src/reportWebVitals.js rename to DesktopReact/frontend/src/reportWebVitals.js diff --git a/Desktop/frontend/src/store/index.js b/DesktopReact/frontend/src/store/index.js similarity index 100% rename from Desktop/frontend/src/store/index.js rename to DesktopReact/frontend/src/store/index.js diff --git a/Desktop/frontend/src/store/slices/Globals.js b/DesktopReact/frontend/src/store/slices/Globals.js similarity index 100% rename from Desktop/frontend/src/store/slices/Globals.js rename to DesktopReact/frontend/src/store/slices/Globals.js diff --git a/DesktopRootReact.js b/DesktopRootReact.js new file mode 100644 index 000000000..62eed4c02 --- /dev/null +++ b/DesktopRootReact.js @@ -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!') + } + } +} \ No newline at end of file diff --git a/desktop-react.js b/desktop-react.js new file mode 100644 index 000000000..37586f795 --- /dev/null +++ b/desktop-react.js @@ -0,0 +1,16 @@ +/* +The Superalgos Desktop is one of the 3 clients of the Superalgos P2P Network: + + * Superalgos Mobile + * Superalgos Desktop + * Superalgos Server + +Users can use any of these clients for their social trading activities. + +This module represents the Nodejs command that users have to start the Desktop App.. + +*/ + +let APP_ROOT = require('./DesktopRoot.js') +let APP_ROOT_MODULE = APP_ROOT.newDesktopRoot() +APP_ROOT_MODULE.run() \ No newline at end of file From dbc0bda0d2b485fdada6245405d5071aa55ea801 Mon Sep 17 00:00:00 2001 From: Axel Date: Wed, 1 Dec 2021 15:39:10 -0300 Subject: [PATCH 13/15] Add desktop react start script to package.json --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 356aee713..98b9040c3 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "dist": "electron-builder -c build/electron-builder.json", "postinstall": "electron-builder install-app-deps", "startNetwork" : "node network", - "startDesktop": "node desktop" + "startDesktop": "node desktop", + "startDesktopReact": "node desktop-react" }, "dependencies": { "@octokit/rest": "^18.12.0", From fe4e66a4057072cc0446ea1b93df6af2039b515b Mon Sep 17 00:00:00 2001 From: Axel Date: Wed, 1 Dec 2021 16:12:20 -0300 Subject: [PATCH 14/15] Re-add social trading interfaces --- .../Plugins/User-Profiles/Loui-Molina.json | 84 ++++++++++++++ Projects/ProjectsSchema.json | 12 ++ .../DK/modules/WebAppInterface.js | 44 ++++---- .../DK/modules/WebSocketsInterface.js | 103 ++++++++++++++++++ .../DK/modules/httpInterface.js | 98 +++++++++++++++++ 5 files changed, 321 insertions(+), 20 deletions(-) create mode 100644 Projects/Governance/Plugins/User-Profiles/Loui-Molina.json create mode 100644 Projects/Social-Trading/DK/modules/WebSocketsInterface.js create mode 100644 Projects/Social-Trading/DK/modules/httpInterface.js diff --git a/Projects/Governance/Plugins/User-Profiles/Loui-Molina.json b/Projects/Governance/Plugins/User-Profiles/Loui-Molina.json new file mode 100644 index 000000000..2a9820354 --- /dev/null +++ b/Projects/Governance/Plugins/User-Profiles/Loui-Molina.json @@ -0,0 +1,84 @@ +{ + "type": "User Profile", + "name": "Loui-Molina", + "config": "{\n \"signature\": {\n \"message\": \"Loui-Molina\",\n \"messageHash\": \"0x6de0af55e96cd9355a0b5ecf1118d8ef9e919dbf03cb1a292f475c515ff35970\",\n \"v\": \"0x1c\",\n \"r\": \"0xd2e9995091dac4866d903d7780d5cae7a89d04a457549a594f719c3315401da1\",\n \"s\": \"0x29ebebf46649a453f4abf9a55c2d7163eb9b5580365fb811372edc16c9203157\",\n \"signature\": \"0xd2e9995091dac4866d903d7780d5cae7a89d04a457549a594f719c3315401da129ebebf46649a453f4abf9a55c2d7163eb9b5580365fb811372edc16c92031571c\"\n }\n}", + "project": "Governance", + "tokenPowerSwitch": { + "type": "Token Power Switch", + "name": "New Token Power Switch", + "config": "{}", + "project": "Governance", + "tokenPowerSwitch": [], + "id": "f439a686-0607-469f-936f-60f0f672d975", + "savedPayload": { + "position": { + "x": 13265.000000000002, + "y": 6027.05830753354 + }, + "targetPosition": { + "x": 13185.000000000002, + "y": 6104.05830753354 + }, + "floatingObject": { + "isPinned": false, + "isFrozen": false, + "isCollapsed": false, + "angleToParent": 1, + "distanceToParent": 3, + "arrangementStyle": 0 + }, + "uiObject": { + "isRunning": false + } + } + }, + "tokensMined": { + "type": "Tokens Mined", + "name": "New Tokens Mined", + "project": "Governance", + "id": "f3c83ef5-fd88-4db5-85b9-2496f483c98e", + "savedPayload": { + "position": { + "x": 13249.000000000002, + "y": 6153.05830753354 + }, + "targetPosition": { + "x": 13185.000000000002, + "y": 6104.05830753354 + }, + "floatingObject": { + "isPinned": false, + "isFrozen": false, + "isCollapsed": false, + "angleToParent": 1, + "distanceToParent": 3, + "arrangementStyle": 0 + }, + "uiObject": { + "isRunning": false + } + } + }, + "id": "743fba6e-a3c9-4a84-9125-5cc45f219d6d", + "savedPayload": { + "position": { + "x": 13185.000000000002, + "y": 6104.05830753354 + }, + "targetPosition": { + "x": 13106.000000000002, + "y": 6031.05830753354 + }, + "floatingObject": { + "isPinned": false, + "isFrozen": false, + "isCollapsed": false, + "angleToParent": 1, + "distanceToParent": 3, + "arrangementStyle": 0 + }, + "uiObject": { + "isRunning": false + } + } +} \ No newline at end of file diff --git a/Projects/ProjectsSchema.json b/Projects/ProjectsSchema.json index 99b4cb1da..527b02889 100644 --- a/Projects/ProjectsSchema.json +++ b/Projects/ProjectsSchema.json @@ -1823,6 +1823,18 @@ "DK": { "globals": [], "modules": [ + { + "name": "Http Interface", + "propertyName": "httpInterface", + "functionName": "newDesktopModulesHttpInterface", + "fileName": "HttpInterface.js" + }, + { + "name": "Web Sockets Interface", + "propertyName": "webSocketsInterface", + "functionName": "newDesktopModulesWebSocketsInterface", + "fileName": "WebSocketsInterface.js" + }, { "name": "Web App Interface", "propertyName": "webAppInterface", diff --git a/Projects/Social-Trading/DK/modules/WebAppInterface.js b/Projects/Social-Trading/DK/modules/WebAppInterface.js index e99d04dfc..d6ef8c92c 100644 --- a/Projects/Social-Trading/DK/modules/WebAppInterface.js +++ b/Projects/Social-Trading/DK/modules/WebAppInterface.js @@ -7,12 +7,14 @@ exports.newSocialTradingModulesWebAppInterface = function newSocialTradingModule Later, it will try to use the personal social graph as a cache, so as to minimize the requests to Network Nodes. */ - return { + let thisObject = { messageReceived: messageReceived, initialize: initialize, finalize: finalize } + return thisObject + function finalize() { } @@ -189,32 +191,34 @@ exports.newSocialTradingModulesWebAppInterface = function newSocialTradingModule the Network Nodes do not store that info themselves, they just store the structure of the social graph. */ - return new Promise((resolve, reject) => { + let promise = new Promise((resolve, reject) => { - const fileName = postHash + ".json" - const filePath = 'My-Social-Trading-Data/main/User-Posts/' + SA.projects.foundations.utilities.filesAndDirectories.pathFromDate(timestamp) + const fileName = postHash + ".json" + const filePath = 'My-Social-Trading-Data/main/User-Posts/' + SA.projects.foundations.utilities.filesAndDirectories.pathFromDate(timestamp) - const fetch = SA.nodeModules.nodeFetch - let url = 'https://raw.githubusercontent.com/' + userProfileHandle + '/' + filePath + '/' + fileName + const fetch = SA.nodeModules.nodeFetch + let url = 'https://raw.githubusercontent.com/' + userProfileHandle + '/' + filePath + '/' + fileName - fetch(url) - .then((response) => { + fetch(url) + .then((response) => { - if (response.status !== 200) { - reject('Github.com responded with status ' + response.status) - return - } + if (response.status != 200) { + reject('Github.com responded with status ' + response.status) + return + } - response.text().then(body => { - post = JSON.parse(body) - resolve(post.postText) - }) - }) - .catch(err => { - resolve('Post Text could not be fetched. ' + err.message) + response.text().then(body => { + post = JSON.parse(body) + resolve(post.postText) }) + }) + .catch(err => { + resolve('Post Text could not be fetched. ' + err.message) + }) - } + } ) + + return promise } } \ No newline at end of file diff --git a/Projects/Social-Trading/DK/modules/WebSocketsInterface.js b/Projects/Social-Trading/DK/modules/WebSocketsInterface.js new file mode 100644 index 000000000..f982fcecd --- /dev/null +++ b/Projects/Social-Trading/DK/modules/WebSocketsInterface.js @@ -0,0 +1,103 @@ +exports.newDesktopModulesWebSocketsInterface = function newDesktopModulesWebSocketsInterface() { + /* + This module handles the websockets communication between the + Desktop App's Client and the Desktop App's Web App. + */ + let thisObject = { + sendToWebApp: sendToWebApp, + initialize: initialize, + finalize: finalize + } + + let socketServer + let webApp + + return thisObject + + function finalize() { + socketServer = undefined + webApp = undefined + } + + function initialize() { + let port = JSON.parse(DK.desktopApp.p2pNetworkClient.node.config).webSocketsPort + socketServer = new SA.nodeModules.ws.Server({ port: port }) + setUpWebSocketServer() + } + + function setUpWebSocketServer() { + try { + socketServer.on('connection', onConnectionOpened) + + function onConnectionOpened(socket) + /* + This function is executed every time a new Websockets connection + is established. + */ { + if (webApp !== undefined) { + console.log('[ERROR] Only one websockets client allowed.') + return + } + + webApp = { + socket: socket + } + console.log('Desktop Web App Connected to Web Sockets Interface ........................... Connected to port ' + global.env.DESKTOP_WEB_SOCKETS_INTERFACE_PORT) + + webApp.socket.on('close', onConnectionClosed) + webApp.socket.on('message', onMenssage) + + async function onMenssage(message) { + try { + let messageHeader + try { + messageHeader = JSON.parse(message) + } catch (err) { + let response = { + messageId: messageHeader.messageId, + result: 'Error', + message: 'messageHeader Not Coorrect JSON Format.' + } + webApp.socket.send(JSON.stringify(response)) + return + } + + await DK.desktopApp.webAppInterface.messageReceived(messageHeader.payload) + .then(sendResponseToWebApp) + .catch(onError) + + function sendResponseToWebApp(response) { + response.messageId = messageHeader.messageId + webApp.socket.send(JSON.stringify(response)) + } + + function onError(errorMessage) { + console.log('[ERROR] Web Sockets Interface -> onMenssage -> errorMessage = ' + errorMessage) + let response = { + messageId: messageHeader.messageId, + result: 'Error', + message: errorMessage + } + webApp.socket.send(JSON.stringify(response)) + } + + } catch (err) { + console.log('[ERROR] Web Sockets Interface -> onMenssage -> err.stack = ' + err.stack) + } + } + } + + function onConnectionClosed() { + webApp = undefined + console.log('Desktop Web App Discoonnected from Web Sockets Interface ................... Disonnected from port ' + global.env.DESKTOP_WEB_SOCKETS_INTERFACE_PORT) + } + + } catch (err) { + console.log('[ERROR] Web Sockets Interface -> setUpWebSocketServer -> err.stack = ' + err.stack) + } + } + + function sendToWebApp(message) { + webApp.socket.send(JSON.stringify(message)) + } +} \ No newline at end of file diff --git a/Projects/Social-Trading/DK/modules/httpInterface.js b/Projects/Social-Trading/DK/modules/httpInterface.js new file mode 100644 index 000000000..639e1e360 --- /dev/null +++ b/Projects/Social-Trading/DK/modules/httpInterface.js @@ -0,0 +1,98 @@ +exports.newDesktopModulesHttpInterface = function newDesktopModulesHttpInterface() { + /* + This module represent the HTTP API of the + Desktop App Client. All HTTP request are processed + by this module. + */ + let thisObject = { + initialize: initialize, + finalize: finalize + } + + return thisObject + + function finalize() { + } + + function initialize() { + let port = JSON.parse(DK.desktopApp.p2pNetworkClient.node.config).webPort + /* + We will create an HTTP Server and leave it running forever. + */ + SA.nodeModules.http.createServer(onHttpRequest).listen(port) + /* Starting the browser now is optional */ + if (process.argv.includes("noBrowser")) { + //Running Client only with no UI. + } else { + SA.nodeModules.open('http://localhost:' + port) + } + } + + function onHttpRequest(httpRequest, httpResponse) { + try { + let requestPathAndParameters = httpRequest.url.split('?') // Remove version information + let requestPath = requestPathAndParameters[0].split('/') + let endpointOrFile = requestPath[1] + + switch (endpointOrFile) { + case 'Environment': + { + SA.projects.foundations.utilities.httpResponses.respondWithContent(JSON.stringify(global.env), httpResponse) + } + break + case 'ClientNode': + { + SA.projects.foundations.utilities.httpResponses.respondWithContent(JSON.stringify(DK.desktopApp.p2pNetworkClient.node), httpResponse) + } + break + case 'ProjectsSchema': + { + let path = global.env.PATH_TO_PROJECTS + '/' + 'ProjectsSchema.json' + SA.projects.foundations.utilities.httpResponses.respondWithFile(path, httpResponse) + } + break + case 'Projects': + { + let path = '' + for (let i = 2; i < 10; i++) { + if (requestPath[i] !== undefined) { + let parameter = unescape(requestPath[i]) + path = path + '/' + parameter + } + + } + let filePath = global.env.PATH_TO_PROJECTS + path + SA.projects.foundations.utilities.httpResponses.respondWithFile(filePath, httpResponse) + } + break + case 'Images': // This means the Images folder. + { + let path = global.env.PATH_TO_DESKTOP + '/WebServer/Images/' + requestPath[2] + + if (requestPath[3] !== undefined) { + path = path + '/' + requestPath[3] + } + + if (requestPath[4] !== undefined) { + path = path + '/' + requestPath[4] + } + + if (requestPath[5] !== undefined) { + path = path + '/' + requestPath[5] + } + + path = unescape(path) + + SA.projects.foundations.utilities.httpResponses.respondWithImage(path, httpResponse) + } + break + default: + { + SA.projects.foundations.utilities.httpResponses.respondWithWebFile(httpResponse, endpointOrFile, global.env.PATH_TO_DESKTOP) + } + } + } catch (err) { + console.log(err.stack) + } + } +} From bae4a48ed20fe5b7ee99a1669bb076e05516e883 Mon Sep 17 00:00:00 2001 From: Axel Date: Wed, 1 Dec 2021 16:28:32 -0300 Subject: [PATCH 15/15] Update Routes --- DesktopReact/frontend/config/paths.js | 10 +++++----- desktop-react.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DesktopReact/frontend/config/paths.js b/DesktopReact/frontend/config/paths.js index 80695c8cf..a013d9aa4 100644 --- a/DesktopReact/frontend/config/paths.js +++ b/DesktopReact/frontend/config/paths.js @@ -22,7 +22,7 @@ const publicUrlOrPath = getPublicUrlOrPath( process.env.PUBLIC_URL ); -const buildPath = process.env.BUILD_PATH || 'Desktop/build'; +const buildPath = process.env.BUILD_PATH || 'DesktopReact/build'; const moduleFileExtensions = [ 'web.mjs', @@ -56,11 +56,11 @@ module.exports = { dotenv: resolveApp('.env'), appPath: resolveApp('.'), appBuild: resolveApp(buildPath), - appPublic: resolveApp('Desktop/frontend/public'), - appHtml: resolveApp('Desktop/frontend/public/index.html'), - appIndexJs: resolveModule(resolveApp, 'Desktop/frontend/src/index'), + 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('Desktop/frontend/src'), + appSrc: resolveApp('DesktopReact/frontend/src'), appNodeModules: resolveApp('node_modules'), publicUrlOrPath, }; diff --git a/desktop-react.js b/desktop-react.js index 37586f795..1e6a33f29 100644 --- a/desktop-react.js +++ b/desktop-react.js @@ -11,6 +11,6 @@ This module represents the Nodejs command that users have to start the Desktop A */ -let APP_ROOT = require('./DesktopRoot.js') +let APP_ROOT = require('./DesktopRootReact') let APP_ROOT_MODULE = APP_ROOT.newDesktopRoot() APP_ROOT_MODULE.run() \ No newline at end of file