From f58c461ab61e00d75a2051ca62d47b8cad010697 Mon Sep 17 00:00:00 2001 From: sadakchap Date: Fri, 10 Apr 2026 16:45:22 +0530 Subject: [PATCH 1/5] implement server url & live query --- src/dashboard/Dashboard.js | 3 + src/dashboard/DashboardView.react.js | 5 + .../ServerURLLiveQuery.react.js | 578 ++++++++++++++++++ .../ServerURLLiveQuery.scss | 95 +++ src/lib/ParseApp.js | 20 + src/lib/serverInfo.js | 2 + 6 files changed, 703 insertions(+) create mode 100644 src/dashboard/ServerURLLiveQuery/ServerURLLiveQuery.react.js create mode 100644 src/dashboard/ServerURLLiveQuery/ServerURLLiveQuery.scss diff --git a/src/dashboard/Dashboard.js b/src/dashboard/Dashboard.js index b44e5163b..54a3d1f19 100644 --- a/src/dashboard/Dashboard.js +++ b/src/dashboard/Dashboard.js @@ -71,6 +71,7 @@ import Deployments from './Deployments/Deployments.react'; import DeploymentDetails from './Deployments/DeploymentDetails.react'; import { useAppPageTracking } from './instrument'; import DomainSettings from './DomainSettings/DomainSettings.react'; +import ServerURLLiveQuery from './ServerURLLiveQuery/ServerURLLiveQuery.react'; import CustomParseOptions from './CustomParseOptions/CustomParseOptions.react'; const LazyGraphQLConsole = lazy(() => import('./Data/ApiConsole/GraphQLConsole.react')); @@ -82,6 +83,7 @@ const LazyDatabaseProfile = lazy(() => import('./DatabaseProfiler/DatabaseProfil const LazyEmailVerification = lazy(() => import('./Notification/EmailVerification.react')); const LazyEmailPasswordReset = lazy(() => import('./Notification/EmailPasswordReset.react')); + async function fetchHubUser() { try { // eslint-disable-next-line no-undef @@ -511,6 +513,7 @@ class Dashboard extends React.Component { } /> } /> } /> + } /> } /> } /> diff --git a/src/dashboard/DashboardView.react.js b/src/dashboard/DashboardView.react.js index 18d9e541b..1f673fd9d 100644 --- a/src/dashboard/DashboardView.react.js +++ b/src/dashboard/DashboardView.react.js @@ -266,6 +266,11 @@ export default class DashboardView extends React.Component { link: '/domain-settings', }); + settingsSections.push({ + name: 'Server URL & Live Query', + link: '/server-url-live-query', + }); + settingsSections.push({ name: 'Environment Variables', link: '/settings/environment-variables', diff --git a/src/dashboard/ServerURLLiveQuery/ServerURLLiveQuery.react.js b/src/dashboard/ServerURLLiveQuery/ServerURLLiveQuery.react.js new file mode 100644 index 000000000..fc3422e29 --- /dev/null +++ b/src/dashboard/ServerURLLiveQuery/ServerURLLiveQuery.react.js @@ -0,0 +1,578 @@ +/* + * Copyright (c) 2016-present, Parse, LLC + * All rights reserved. + * + * This source code is licensed under the license found in the LICENSE file in + * the root directory of this source tree. + */ +import AccountManager from 'lib/AccountManager'; +import React from 'react'; +import Toolbar from 'components/Toolbar/Toolbar.react'; +import { withRouter } from 'lib/withRouter'; +import DashboardView from 'dashboard/DashboardView.react'; +import B4aLoaderContainer from 'components/B4aLoaderContainer/B4aLoaderContainer.react'; +import FlowView from 'components/FlowView/FlowView.react'; +import styles from './ServerURLLiveQuery.scss'; +import EmptyGhostState from 'components/EmptyGhostState/EmptyGhostState.react'; +import Button from 'components/Button/Button.react'; +import B4aToggle from 'components/Toggle/B4aToggle.react'; +import Label from 'components/Label/Label.react'; +import Field from 'components/Field/Field.react'; +import TextInput from 'components/TextInput/TextInput.react'; +import Fieldset from 'components/Fieldset/Fieldset.react'; +import B4aModal from 'components/B4aModal/B4aModal.react'; +import { amplitudeLogEvent } from 'lib/amplitudeEvents'; +import renderFlowFooterChanges from 'lib/renderFlowFooterChanges'; + +const SIX_MONTHS_MS = 6 * 30 * 24 * 60 * 60 * 1000; + +const FOOTER_FIELD_OPTIONS = { + activated: { friendlyName: 'Back4App subdomain', type: 'boolean' }, + subdomainName: { friendlyName: 'subdomain name', showTo: true }, + currentDomain: { friendlyName: 'domain', showTo: true }, + statusLiveQuery: { friendlyName: 'Live Query', type: 'boolean' }, + schemasChoose: { friendlyName: 'Live Query classes' }, +}; + +@withRouter +class ServerURLLiveQuery extends DashboardView { + constructor() { + super(); + this.section = 'App Settings'; + this.subsection = 'Server URL & Live Query'; + this.state = { + isLoading: true, + loadingError: null, + + isUserVerified: false, + hasPermission: true, + + isActivated: false, + currentSubdomain: '', + availableDomains: ['b4a.io'], + schema: [], + activatedLiveQuery: {}, + + initialFields: { + activated: false, + subdomainName: '', + currentDomain: 'b4a.io', + statusLiveQuery: false, + schemasChoose: {}, + }, + + modal: null, + }; + + this._flowViewRef = React.createRef(); + this.unblock = null; + this._onBeforeUnload = null; + } + + _hasUnsavedChanges() { + if (this._flowViewRef.current) { + const { changes, saveState } = this._flowViewRef.current.state; + return Object.keys(changes || {}).length > 0 && saveState !== 'SAVING'; + } + return false; + } + + componentDidMount() { + super.componentDidMount(); + this.loadData(); + + this._onBeforeUnload = (e) => { + if (this._hasUnsavedChanges()) { + e.preventDefault(); + e.returnValue = ''; + return ''; + } + }; + window.addEventListener('beforeunload', this._onBeforeUnload); + + if (this.props.navigator && typeof this.props.navigator.block === 'function') { + this.unblock = this.props.navigator.block(tx => { + if (this._hasUnsavedChanges()) { + const unblock = this.unblock && this.unblock.bind(this); + const autoUnblockingTx = { + ...tx, + retry() { + if (unblock) { + unblock(); + } + tx.retry(); + }, + }; + + const modal = ( + +