From 3c0fa37a5f1492f8185f15209d6090a4301c63e0 Mon Sep 17 00:00:00 2001 From: Gandharv Date: Mon, 21 Feb 2022 23:07:48 +0500 Subject: [PATCH] Refactor: create component for walkthrough & ws (#2307) * refactor: create component for walkthrough * fix: lint * refactor: move websocket to a different file * close socket on unmount --- frontend/src/Editor/Editor.jsx | 171 +++---------------- frontend/src/_helpers/createWalkThrough.js | 85 +++++++++ frontend/src/_helpers/websocketConnection.js | 45 +++++ 3 files changed, 151 insertions(+), 150 deletions(-) create mode 100644 frontend/src/_helpers/createWalkThrough.js create mode 100644 frontend/src/_helpers/websocketConnection.js diff --git a/frontend/src/Editor/Editor.jsx b/frontend/src/Editor/Editor.jsx index e6fc9f0896..1e21b41acd 100644 --- a/frontend/src/Editor/Editor.jsx +++ b/frontend/src/Editor/Editor.jsx @@ -25,8 +25,6 @@ import { setStateAsync, computeComponentState, getSvgIcon, - addToLocalStorage, - getDataFromLocalStorage, } from '@/_helpers/appUtils'; import { Confirm } from './Viewer/Confirm'; import ReactTooltip from 'react-tooltip'; @@ -45,9 +43,9 @@ import DesktopSelectedIcon from './Icons/desktop-selected.svg'; import Modal from 'react-bootstrap/Modal'; import Button from 'react-bootstrap/Button'; import { AppVersionsManager } from './AppVersionsManager'; -import * as Driver from 'driver.js'; -import 'driver.js/dist/driver.min.css'; import { SearchBoxComponent } from '@/_ui/Search'; +import { initEditorWalkThrough } from '@/_helpers/createWalkThrough'; +import { createWebsocketConnection } from '@/_helpers/websocketConnection'; setAutoFreeze(false); enablePatches(); @@ -58,8 +56,13 @@ class Editor extends React.Component { const appId = this.props.match.params.id; const currentUser = authenticationService.currentUserValue; + + const { socket } = createWebsocketConnection(appId); + let userVars = {}; + this.socket = socket; + if (currentUser) { userVars = { email: currentUser.email, @@ -114,7 +117,6 @@ class Editor extends React.Component { isDeletingDataQuery: false, showHiddenOptionsForDataQueryId: null, showQueryConfirmation: false, - socket: null, showInitVersionCreateModal: false, isCreatingInitVersion: false, initVersionName: 'v1', @@ -139,100 +141,12 @@ class Editor extends React.Component { this.fetchApp(); this.initComponentVersioning(); this.initEventListeners(); - config.COMMENT_FEATURE_ENABLE && this.initWebSocket(); this.setState({ currentSidebarTab: 2, selectedComponent: null, }); } - initWalkThrough() { - const driver = new Driver({ - allowClose: true, - closeBtnText: 'Skip', - nextBtnText: 'Next', - prevBtnText: 'Previous', - padding: 2, - onReset: () => { - // Here we need to write the logic to update walkthroughCompleted column of the current user. - addToLocalStorage({ key: 'walkthroughCompleted', value: true }); - }, - className: `${this.props.darkMode ? 'dark-theme' : 'light-theme'}-walkthrough`, - }); - - if ( - getDataFromLocalStorage('walkthroughCompleted') == undefined || - !getDataFromLocalStorage('walkthroughCompleted') - ) { - driver.defineSteps([ - { - element: '.component-image-holder', - popover: { - title: 'Drag and drop widgets', - description: 'From the widget sidebar, drag and drop widgets to the canvas.', - position: 'left', - closeBtnText: 'Skip (1/6)', - }, - }, - { - element: '.sidebar-datasources', - popover: { - title: 'Connect to data sources', - description: 'You can manage your data sources from here.', - position: 'right', - closeBtnText: 'Skip (2/6)', - }, - }, - { - element: '.left-sidebar-inspector', - popover: { - title: 'Inspector', - description: 'Inspector lets you check the properties of widgets, results of queries etc.', - position: 'right', - closeBtnText: 'Skip (3/6)', - }, - }, - { - element: '.queries-header ', - popover: { - title: 'Create queries', - description: - 'Create queries to interact with your data sources, run JavaScript snippets and to make API requests.', - position: 'top', - closeBtnText: 'Skip (4/6)', - }, - }, - { - element: '.release-buttons', - popover: { - title: 'Preview, release & share', - description: - 'Click on preview to view the current changes on app viewer. Click on share button to view the sharing options. Release the editing version to make the changes live. Released versions cannot be modified, you will have to create another version to make more changes.', - position: 'bottom', - closeBtnText: 'Skip (5/6)', - }, - }, - { - element: '.sidebar-comments', - popover: { - title: 'Collaborate', - description: 'Add comments on canvas and tag your team members to collaborate.', - position: 'right', - closeBtnText: 'Skip (6/6)', - }, - }, - ]); - - driver.start(); - } - } - - componentDidUpdate(prevProps, prevState) { - if (prevState.editingVersion == undefined && this.state.editingVersion) { - this.initWalkThrough(); - } - } - isVersionReleased = (version = this.state.editingVersion) => { if (isEmpty(version)) { return false; @@ -291,59 +205,10 @@ class Editor extends React.Component { componentWillUnmount() { document.removeEventListener('mousemove', this.onMouseMove); document.removeEventListener('mouseup', this.onMouseUp); - if (this.state.socket) { - this.state.socket?.close(); - } document.title = 'Tooljet - Dashboard'; + this.socket && this.socket?.close(); } - getWebsocketUrl = () => { - const re = /https?:\/\//g; - if (re.test(config.apiUrl)) return config.apiUrl.replace(/(^\w+:|^)\/\//, '').replace('/api', ''); - - return window.location.host; - }; - - initWebSocket = () => { - // TODO: add retry policy - const socket = new WebSocket(`${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${this.getWebsocketUrl()}`); - - const appId = this.props.match.params.id; - - // Connection opened - socket.addEventListener('open', function (event) { - console.log('connection established', event); - const currentUser = JSON.parse(localStorage.getItem('currentUser')); - - socket.send( - JSON.stringify({ - event: 'authenticate', - data: currentUser.auth_token, - }) - ); - socket.send( - JSON.stringify({ - event: 'subscribe', - data: appId, - }) - ); - }); - - // Connection closed - socket.addEventListener('close', function (event) { - console.log('connection closed', event); - }); - - // Listen for possible errors - socket.addEventListener('error', function (event) { - console.log('WebSocket error: ', event); - }); - - this.setState({ - socket, - }); - }; - // 1. When we receive an undoable action – we can always undo but cannot redo anymore. // 2. Whenever you perform an undo – you can always redo and keep doing undo as long as we have a patch for it. // 3. Whenever you redo – you can always undo and keep doing redo as long as we have a patch for it. @@ -912,12 +777,14 @@ class Editor extends React.Component { ); }; + handleKeyPress = (event) => { if (event.key === 'Enter') { // eslint-disable-next-line no-undef this.createInitVersion(); } }; + createInitVersion = () => { const newVersionName = this.state.initVersionName; const appId = this.state.appId; @@ -925,10 +792,15 @@ class Editor extends React.Component { if (!isEmpty(newVersionName?.trim())) { this.setState({ isCreatingInitVersion: true }); appVersionService.create(appId, newVersionName).then(() => { - this.setState({ - showInitVersionCreateModal: false, - isCreatingInitVersion: false, - }); + this.setState( + { + showInitVersionCreateModal: false, + isCreatingInitVersion: false, + }, + () => { + initEditorWalkThrough(); + } + ); toast.success('Version Created'); this.fetchApp(); }); @@ -961,7 +833,6 @@ class Editor extends React.Component { enforceFocus={false} animation={false} centered={true} - // eslint-disable-next-line no-undef > Create Version @@ -1211,7 +1082,7 @@ class Editor extends React.Component { <> {config.COMMENT_FEATURE_ENABLE && showComments && ( diff --git a/frontend/src/_helpers/createWalkThrough.js b/frontend/src/_helpers/createWalkThrough.js new file mode 100644 index 0000000000..8d7f2ee3cd --- /dev/null +++ b/frontend/src/_helpers/createWalkThrough.js @@ -0,0 +1,85 @@ +import * as Driver from 'driver.js'; +import { addToLocalStorage, getDataFromLocalStorage } from '@/_helpers/appUtils'; +import 'driver.js/dist/driver.min.css'; + +export const initEditorWalkThrough = () => { + if ( + getDataFromLocalStorage('walkthroughCompleted') == undefined || + !getDataFromLocalStorage('walkthroughCompleted') + ) { + const darkMode = getDataFromLocalStorage('darkMode') === 'true'; + const driver = new Driver({ + allowClose: true, + closeBtnText: 'Skip', + nextBtnText: 'Next', + prevBtnText: 'Previous', + padding: 2, + onReset: () => { + // Here we need to write the logic to update walkthroughCompleted column of the current user. + addToLocalStorage({ key: 'walkthroughCompleted', value: true }); + }, + className: `${darkMode ? 'dark-theme' : 'light-theme'}-walkthrough`, + }); + + driver.defineSteps([ + { + element: '.component-image-holder', + popover: { + title: 'Drag and drop widgets', + description: 'From the widget sidebar, drag and drop widgets to the canvas.', + position: 'left', + closeBtnText: 'Skip (1/6)', + }, + }, + { + element: '.sidebar-datasources', + popover: { + title: 'Connect to data sources', + description: 'You can manage your data sources from here.', + position: 'right', + closeBtnText: 'Skip (2/6)', + }, + }, + { + element: '.left-sidebar-inspector', + popover: { + title: 'Inspector', + description: 'Inspector lets you check the properties of widgets, results of queries etc.', + position: 'right', + closeBtnText: 'Skip (3/6)', + }, + }, + { + element: '.queries-header ', + popover: { + title: 'Create queries', + description: + 'Create queries to interact with your data sources, run JavaScript snippets and to make API requests.', + position: 'top', + closeBtnText: 'Skip (4/6)', + }, + }, + { + element: '.release-buttons', + popover: { + title: 'Preview, release & share', + description: + 'Click on preview to view the current changes on app viewer. Click on share button to view the sharing options. Release the editing version to make the changes live. Released versions cannot be modified, you will have to create another version to make more changes.', + position: 'bottom', + closeBtnText: 'Skip (5/6)', + }, + }, + { + element: '.sidebar-comments', + popover: { + title: 'Collaborate', + description: 'Add comments on canvas and tag your team members to collaborate.', + position: 'right', + closeBtnText: 'Skip (6/6)', + }, + }, + ]); + + driver.start(); + } +}; diff --git a/frontend/src/_helpers/websocketConnection.js b/frontend/src/_helpers/websocketConnection.js new file mode 100644 index 0000000000..f4bcc81226 --- /dev/null +++ b/frontend/src/_helpers/websocketConnection.js @@ -0,0 +1,45 @@ +import config from 'config'; + +class WebSocketConnection { + constructor(appId) { + this.socket = new WebSocket(`${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${this.getWebsocketUrl()}`); + + this.addListeners(appId); + } + + getWebsocketUrl() { + const re = /https?:\/\//g; + if (re.test(config.apiUrl)) return config.apiUrl.replace(/(^\w+:|^)\/\//, '').replace('/api', ''); + + return window.location.host; + } + + addListeners(appId) { + // Connection opened + this.socket.addEventListener('open', (event) => { + console.log('connection established', event); + const currentUser = JSON.parse(localStorage.getItem('currentUser')); + + this.socket.send( + JSON.stringify({ + event: 'authenticate', + data: currentUser.auth_token, + }) + ); + }); + + // Connection closed + this.socket.addEventListener('close', (event) => { + console.log('connection closed', event); + }); + + // Listen for possible errors + this.socket.addEventListener('error', (event) => { + console.log('WebSocket error: ', event); + }); + } +} + +export const createWebsocketConnection = (appId) => { + return new WebSocketConnection(appId); +};