From b45cc8f35e91caa5902bbc2fbb744322ec4d0982 Mon Sep 17 00:00:00 2001 From: akash-codemonk <67054171+akash-codemonk@users.noreply.github.com> Date: Tue, 14 Jul 2020 16:00:33 +0530 Subject: [PATCH 1/5] Fix autocomplete for special cases (#31) * Fix autocomplete for special cases - Send the currently focused dynamic binding to tern instead of the entire editor value * Add tests for TernServer methods - Make request callback a seperate so that it could be testable - Add tests for getFocusedDynamicValue, buildRequest and requestCallback * Keep input and expected values together --- .../src/utils/autocomplete/TernServer.test.ts | 165 +++++++++++++++ .../src/utils/autocomplete/TernServer.ts | 189 ++++++++++++------ .../test/__mocks__/CodeMirrorEditorMock.ts | 2 + 3 files changed, 298 insertions(+), 58 deletions(-) create mode 100644 app/client/src/utils/autocomplete/TernServer.test.ts diff --git a/app/client/src/utils/autocomplete/TernServer.test.ts b/app/client/src/utils/autocomplete/TernServer.test.ts new file mode 100644 index 0000000000..c391290c64 --- /dev/null +++ b/app/client/src/utils/autocomplete/TernServer.test.ts @@ -0,0 +1,165 @@ +import TernServer from "./TernServer"; +import { MockCodemirrorEditor } from "../../../test/__mocks__/CodeMirrorEditorMock"; +jest.mock("jsExecution/RealmExecutor"); + +describe("Tern server", () => { + it("Check whether the correct value is being sent to tern", () => { + const ternServer = new TernServer({}); + + const testCases = [ + { + input: { + name: "test", + doc: ({ + getCursor: () => ({ ch: 0, line: 0 }), + getLine: () => "{{Api.}}", + } as unknown) as CodeMirror.Doc, + changed: null, + }, + expectedOutput: "{{Api.}}", + }, + { + input: { + name: "test", + doc: ({ + getCursor: () => ({ ch: 0, line: 0 }), + getLine: () => "a{{Api.}}", + } as unknown) as CodeMirror.Doc, + changed: null, + }, + expectedOutput: "a{{Api.}}", + }, + { + input: { + name: "test", + doc: ({ + getCursor: () => ({ ch: 2, line: 0 }), + getLine: () => "a{{Api.}}", + } as unknown) as CodeMirror.Doc, + changed: null, + }, + expectedOutput: "{{Api.}}", + }, + ]; + + testCases.forEach(testCase => { + const value = ternServer.getFocusedDynamicValue(testCase.input); + expect(value).toBe(testCase.expectedOutput); + }); + }); + + it("Check whether the correct position is sent for querying autocomplete", () => { + const ternServer = new TernServer({}); + const testCases = [ + { + input: { + name: "test", + doc: ({ + getCursor: () => ({ ch: 0, line: 0 }), + getLine: () => "{{Api.}}", + somethingSelected: () => false, + } as unknown) as CodeMirror.Doc, + changed: null, + }, + expectedOutput: { ch: 0, line: 0 }, + }, + { + input: { + name: "test", + doc: ({ + getCursor: () => ({ ch: 0, line: 1 }), + getLine: () => "{{Api.}}", + somethingSelected: () => false, + } as unknown) as CodeMirror.Doc, + changed: null, + }, + expectedOutput: { ch: 0, line: 0 }, + }, + { + input: { + name: "test", + doc: ({ + getCursor: () => ({ ch: 3, line: 1 }), + getLine: () => "g {{Api.}}", + somethingSelected: () => false, + } as unknown) as CodeMirror.Doc, + changed: null, + }, + expectedOutput: { ch: 1, line: 0 }, + }, + ]; + + testCases.forEach((testCase, index) => { + const request = ternServer.buildRequest(testCase.input, {}); + + expect(request.query.end).toEqual(testCase.expectedOutput); + }); + }); + + it(`Check whether the position is evaluated correctly for placing the selected + autocomplete value`, () => { + const ternServer = new TernServer({}); + + const testCases = [ + { + input: { + codeEditor: { + value: "{{}}", + cursor: { ch: 2, line: 0 }, + doc: ({ + getCursor: () => ({ ch: 2, line: 0 }), + getLine: () => "{{}}", + somethingSelected: () => false, + } as unknown) as CodeMirror.Doc, + }, + requestCallbackData: { + completions: [{ name: "Api1" }], + start: { ch: 2, line: 0 }, + end: { ch: 6, line: 0 }, + }, + }, + expectedOutput: { ch: 2, line: 0 }, + }, + { + input: { + codeEditor: { + value: "\n {{}}", + cursor: { ch: 3, line: 1 }, + doc: ({ + getCursor: () => ({ ch: 3, line: 1 }), + getLine: () => " {{}}", + somethingSelected: () => false, + } as unknown) as CodeMirror.Doc, + }, + requestCallbackData: { + completions: [{ name: "Api1" }], + start: { ch: 2, line: 1 }, + end: { ch: 6, line: 1 }, + }, + }, + expectedOutput: { ch: 3, line: 1 }, + }, + ]; + + testCases.forEach((testCase, index) => { + MockCodemirrorEditor.getValue.mockReturnValueOnce( + testCase.input.codeEditor.value, + ); + MockCodemirrorEditor.getCursor.mockReturnValueOnce( + testCase.input.codeEditor.cursor, + ); + MockCodemirrorEditor.getDoc.mockReturnValueOnce( + testCase.input.codeEditor.doc, + ); + + const value: any = ternServer.requestCallback( + null, + testCase.input.requestCallbackData, + (MockCodemirrorEditor as unknown) as CodeMirror.Editor, + () => null, + ); + + expect(value.from).toEqual(testCase.expectedOutput); + }); + }); +}); diff --git a/app/client/src/utils/autocomplete/TernServer.ts b/app/client/src/utils/autocomplete/TernServer.ts index ad83b58f52..69153289e7 100644 --- a/app/client/src/utils/autocomplete/TernServer.ts +++ b/app/client/src/utils/autocomplete/TernServer.ts @@ -6,6 +6,10 @@ import ecma from "tern/defs/ecmascript.json"; import lodash from "constants/defs/lodash.json"; import { dataTreeTypeDefCreator } from "utils/autocomplete/dataTreeTypeDefCreator"; import CodeMirror, { Hint, Pos, cmpPos } from "codemirror"; +import { + getDynamicStringSegments, + isDynamicValue, +} from "utils/DynamicBindingUtils"; const DEFS = [ecma, lodash]; const bigDoc = 250; @@ -70,6 +74,79 @@ class TernServer { this.server.addDefs(def, true); } + requestCallback( + error: any, + data: any, + cm: CodeMirror.Editor, + resolve: Function, + ) { + if (error) return this.showError(cm, error); + if (data.completions.length === 0) { + return this.showError(cm, "No suggestions"); + } + const doc = this.findDoc(cm.getDoc()); + const cursor = cm.getCursor(); + const lineValue = this.lineValue(doc); + const focusedValue = this.getFocusedDynamicValue(doc); + const index = lineValue.indexOf(focusedValue); + let completions: Completion[] = []; + let after = ""; + const { start, end } = data; + const from = { + ...start, + ch: start.ch + index, + line: cursor.line, + }; + const to = { + ...end, + ch: end.ch + index, + line: cursor.line, + }; + if ( + cm.getRange(Pos(from.line, from.ch - 2), from) === '["' && + cm.getRange(to, Pos(to.line, to.ch + 2)) !== '"]' + ) { + after = '"]'; + } + for (let i = 0; i < data.completions.length; ++i) { + const completion = data.completions[i]; + let className = this.typeToIcon(completion.type); + if (data.guess) className += " " + cls + "guess"; + completions.push({ + text: completion.name + after, + displayText: completion.displayName || completion.name, + className: className, + data: completion, + origin: completion.origin, + }); + } + completions = this.sortCompletions(completions); + + const obj = { from: from, to: to, list: completions }; + let tooltip: HTMLElement | undefined = undefined; + CodeMirror.on(obj, "close", () => this.remove(tooltip)); + CodeMirror.on(obj, "update", () => this.remove(tooltip)); + CodeMirror.on( + obj, + "select", + (cur: { data: { doc: string } }, node: any) => { + this.remove(tooltip); + const content = cur.data.doc; + if (content) { + tooltip = this.makeTooltip( + node.parentNode.getBoundingClientRect().right + window.pageXOffset, + node.getBoundingClientRect().top + window.pageYOffset, + content, + ); + tooltip.className += " " + cls + "hint-doc"; + } + }, + ); + resolve(obj); + + return obj; + } + getHint(cm: CodeMirror.Editor) { return new Promise(resolve => { this.request( @@ -82,58 +159,7 @@ class TernServer { origins: true, caseInsensitive: true, }, - (error, data) => { - if (error) return this.showError(cm, error); - if (data.completions.length === 0) { - return this.showError(cm, "No suggestions"); - } - let completions: Completion[] = []; - let after = ""; - const from = data.start; - const to = data.end; - if ( - cm.getRange(Pos(from.line, from.ch - 2), from) === '["' && - cm.getRange(to, Pos(to.line, to.ch + 2)) !== '"]' - ) { - after = '"]'; - } - for (let i = 0; i < data.completions.length; ++i) { - const completion = data.completions[i]; - let className = this.typeToIcon(completion.type); - if (data.guess) className += " " + cls + "guess"; - completions.push({ - text: completion.name + after, - displayText: completion.displayName || completion.name, - className: className, - data: completion, - origin: completion.origin, - }); - } - completions = this.sortCompletions(completions); - - const obj = { from: from, to: to, list: completions }; - let tooltip: HTMLElement | undefined = undefined; - CodeMirror.on(obj, "close", () => this.remove(tooltip)); - CodeMirror.on(obj, "update", () => this.remove(tooltip)); - CodeMirror.on( - obj, - "select", - (cur: { data: { doc: string } }, node: any) => { - this.remove(tooltip); - const content = cur.data.doc; - if (content) { - tooltip = this.makeTooltip( - node.parentNode.getBoundingClientRect().right + - window.pageXOffset, - node.getBoundingClientRect().top + window.pageYOffset, - content, - ); - tooltip.className += " " + cls + "hint-doc"; - } - }, - ); - resolve(obj); - }, + (error, data) => this.requestCallback(error, data, cm, resolve), ); }); } @@ -231,7 +257,7 @@ class TernServer { addDoc(name: string, doc: CodeMirror.Doc) { const data = { doc: doc, name: name, changed: null }; - this.server.addFile(name, this.docValue(data)); + this.server.addFile(name, this.getFocusedDynamicValue(data)); CodeMirror.on(doc, "change", this.trackChange.bind(this)); return (this.docs[name] = data); } @@ -258,7 +284,19 @@ class TernServer { if (!allowFragments) delete query.fullDocs; query.lineCharPositions = true; if (!query.end) { - query.end = pos || doc.doc.getCursor("end"); + const lineValue = this.lineValue(doc); + const focusedValue = this.getFocusedDynamicValue(doc); + const index = lineValue.indexOf(focusedValue); + + const positions = pos || doc.doc.getCursor("end"); + const queryChPosition = positions.ch - index; + + query.end = { + ...positions, + line: 0, + ch: queryChPosition, + }; + if (doc.doc.somethingSelected()) { query.start = doc.doc.getCursor("start"); } @@ -283,7 +321,7 @@ class TernServer { files.push({ type: "full", name: doc.name, - text: this.docValue(doc), + text: this.getFocusedDynamicValue(doc), }); query.file = doc.name; doc.changed = null; @@ -297,11 +335,12 @@ class TernServer { files.push({ type: "full", name: cur.name, - text: this.docValue(cur), + text: this.getFocusedDynamicValue(cur), }); cur.changed = null; } } + return { query: query, files: files }; } @@ -341,8 +380,17 @@ class TernServer { sendDoc(doc: TernDoc) { this.server.request( - // @ts-ignore - { files: [{ type: "full", name: doc.name, text: this.docValue(doc) }] }, + { + // @ts-ignore + files: [ + // @ts-ignore + { + type: "full", + name: doc.name, + text: this.getFocusedDynamicValue(doc), + }, + ], + }, function(error: Error) { if (error) window.console.error(error); else doc.changed = null; @@ -350,10 +398,35 @@ class TernServer { ); } + lineValue(doc: TernDoc) { + const cursor = doc.doc.getCursor(); + + return doc.doc.getLine(cursor.line); + } + docValue(doc: TernDoc) { return doc.doc.getValue(); } + getFocusedDynamicValue(doc: TernDoc) { + const cursor = doc.doc.getCursor(); + const value = this.lineValue(doc); + const stringSegments = getDynamicStringSegments(value); + const dynamicStrings = stringSegments.filter(segment => { + if (isDynamicValue(segment)) { + const index = value.indexOf(segment); + + if (cursor.ch >= index && cursor.ch <= index + segment.length) { + return true; + } + } + + return false; + }); + + return dynamicStrings.length ? dynamicStrings[0] : value; + } + getFragmentAround( data: TernDoc, start: CodeMirror.Position, diff --git a/app/client/test/__mocks__/CodeMirrorEditorMock.ts b/app/client/test/__mocks__/CodeMirrorEditorMock.ts index aacc235d9c..f20679894b 100644 --- a/app/client/test/__mocks__/CodeMirrorEditorMock.ts +++ b/app/client/test/__mocks__/CodeMirrorEditorMock.ts @@ -8,4 +8,6 @@ export const MockCodemirrorEditor = { showHint: jest.fn(), getLine: jest.fn(), closeHint: jest.fn(), + getRange: jest.fn(), + getDoc: jest.fn(), }; From 204e65924fe8af1c19e2145f456569cbc1442fbe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2020 11:36:02 +0530 Subject: [PATCH 2/5] Bump websocket-extensions from 0.1.3 to 0.1.4 in /app/client (#70) Bumps [websocket-extensions](https://github.com/faye/websocket-extensions-node) from 0.1.3 to 0.1.4. - [Release notes](https://github.com/faye/websocket-extensions-node/releases) - [Changelog](https://github.com/faye/websocket-extensions-node/blob/master/CHANGELOG.md) - [Commits](https://github.com/faye/websocket-extensions-node/compare/0.1.3...0.1.4) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- app/client/yarn.lock | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/client/yarn.lock b/app/client/yarn.lock index 37f11b2331..53ce0f7d06 100644 --- a/app/client/yarn.lock +++ b/app/client/yarn.lock @@ -12651,8 +12651,9 @@ websocket-driver@>=0.5.1: websocket-extensions ">=0.1.1" websocket-extensions@>=0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3, whatwg-encoding@^1.0.5: version "1.0.5" From de151e1b46f64d18b375fc46025224f4ce6026af Mon Sep 17 00:00:00 2001 From: Nikhil Nandagopal Date: Wed, 15 Jul 2020 12:49:52 +0530 Subject: [PATCH 3/5] Fix/deploy script (#94) * Added information to setup domain * Fixed incorrect var reference * Updated documentation message * Updated env template Co-authored-by: Nikhil Nandagopal --- app/client/src/api/Api.tsx | 5 +---- app/client/src/sagas/ErrorSagas.tsx | 4 ++-- deploy/install.sh | 1 - deploy/template/docker.env.sh | 30 ++++++++++++++++++++--------- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/app/client/src/api/Api.tsx b/app/client/src/api/Api.tsx index bc6f54e6aa..a599879511 100644 --- a/app/client/src/api/Api.tsx +++ b/app/client/src/api/Api.tsx @@ -67,10 +67,7 @@ axiosInstance.interceptors.response.use( } } const errorData = error.response.data.responseMeta; - if ( - errorData.status === 404 && - errorData.error.code === 4028 - ) { + if (errorData.status === 404 && errorData.error.code === 4028) { history.push(PAGE_NOT_FOUND_URL); return Promise.reject({ code: 404, diff --git a/app/client/src/sagas/ErrorSagas.tsx b/app/client/src/sagas/ErrorSagas.tsx index ff08077e86..cf8b31ddd8 100644 --- a/app/client/src/sagas/ErrorSagas.tsx +++ b/app/client/src/sagas/ErrorSagas.tsx @@ -59,7 +59,7 @@ export function getResponseErrorMessage(response: ApiResponse) { : undefined; } -type ErrorPayloadType = { code?: number; message?: string, show?:boolean }; +type ErrorPayloadType = { code?: number; message?: string; show?: boolean }; let ActionErrorDisplayMap: { [key: string]: (error: ErrorPayloadType) => string; } = {}; @@ -92,7 +92,7 @@ export function* errorSaga( } = errorAction; const message = error && error.message ? error.message : ActionErrorDisplayMap[type](error); - + if (show && error && error.show) { // error.code !== 401 IS A HACK! // TODO(abhinav): Figure out a generic way diff --git a/deploy/install.sh b/deploy/install.sh index 513185b930..1f54fc07bd 100755 --- a/deploy/install.sh +++ b/deploy/install.sh @@ -130,7 +130,6 @@ if [[ "$setup_encryption" = "true" ]];then fi fi echo "" - read -p 'Would you like to host appsmith on a custom domain / subdomain? [Y/n]: ' setup_domain setup_domain=${setup_domain:-Y} if [ $setup_domain == "Y" -o $setup_domain == "y" -o $setup_domain == "yes" -o $setup_domain == "Yes" ];then diff --git a/deploy/template/docker.env.sh b/deploy/template/docker.env.sh index e63cea4133..e322ee0118 100644 --- a/deploy/template/docker.env.sh +++ b/deploy/template/docker.env.sh @@ -8,6 +8,10 @@ if [ -f docker-compose.yml ] fi cat > docker.env << EOF +# Read our documentation on how to configure these features +# https://docs.appsmith.com/v/v1.1/enabling-3p-services + +# ***** Email ********** APPSMITH_MAIL_ENABLED=false # APPSMITH_MAIL_HOST= # APPSMITH_MAIL_PASSWORD= @@ -15,16 +19,24 @@ APPSMITH_MAIL_ENABLED=false # APPSMITH_MAIL_SMTP_AUTH= # APPSMITH_MAIL_SMTP_TLS_ENABLED= # APPSMITH_MAIL_USERNAME= -# APPSMITH_MARKETPLACE_URL= -APPSMITH_MONGODB_URI=mongodb://$mongo_root_user:$mongo_root_password@$mongo_host/appsmith?retryWrites=true -# APPSMITH_OAUTH2_GITHUB_CLIENT_ID= -# APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET= +# ****************************** + +# ******** Google OAuth ******** # APPSMITH_OAUTH2_GOOGLE_CLIENT_ID= # APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET= -# APPSMITH_RAPID_API_KEY_VALUE= -APPSMITH_REDIS_URL=redis://redis:6379 -# APPSMITH_ROLLBAR_ACCESS_TOKEN= -# APPSMITH_ROLLBAR_ENV= -# APPSMITH_SEGMENT_KEY= +# ****************************** +# ********* Github OAUth ********** +# APPSMITH_OAUTH2_GITHUB_CLIENT_ID= +# APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET= +# ********************************* + +# ******** Google Maps *********** +# APPSMITH_GOOGLE_MAPS_API_KEY= +# ******************************** + +# ******** Database ************* +APPSMITH_REDIS_URL=redis://redis:6379 +APPSMITH_MONGODB_URI=mongodb://$mongo_root_user:$mongo_root_password@$mongo_host/appsmith?retryWrites=true +# ******************************* EOF From b1e8e83e5df7304e43b866de77e6b4a020271ee9 Mon Sep 17 00:00:00 2001 From: akash-codemonk <67054171+akash-codemonk@users.noreply.github.com> Date: Wed, 15 Jul 2020 13:30:34 +0530 Subject: [PATCH 4/5] Show error toast for delete datasource (#93) --- app/client/src/sagas/DatasourcesSagas.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/client/src/sagas/DatasourcesSagas.ts b/app/client/src/sagas/DatasourcesSagas.ts index a87913f229..c0f1e47375 100644 --- a/app/client/src/sagas/DatasourcesSagas.ts +++ b/app/client/src/sagas/DatasourcesSagas.ts @@ -129,6 +129,10 @@ export function* deleteDatasourceSaga( history.push(DATA_SOURCES_EDITOR_URL(applicationId, pageId)); } } catch (error) { + AppToaster.show({ + message: error.message, + type: ToastType.ERROR, + }); yield put({ type: ReduxActionErrorTypes.DELETE_DATASOURCE_ERROR, payload: { error, id: actionPayload.payload.id }, From 2956f1b3d07ebccc64c6a70831ad2b0a585ade8c Mon Sep 17 00:00:00 2001 From: Tejaaswini Narendra <67053685+tejaaswini-narendra@users.noreply.github.com> Date: Wed, 15 Jul 2020 15:17:39 +0530 Subject: [PATCH 5/5] Feat: Share application publicly (#89) * Feat: Share application publically * fix: eslint warnings and code refactor --- app/client/src/api/ApplicationApi.tsx | 18 ++++ .../src/constants/ReduxActionConstants.tsx | 4 + app/client/src/pages/Editor/EditorHeader.tsx | 59 ++++++++++-- .../src/pages/Editor/ShareApplicationForm.tsx | 94 +++++++++++++++++++ .../uiReducers/applicationsReducer.tsx | 20 ++++ app/client/src/sagas/ApplicationSagas.tsx | 35 +++++++ 6 files changed, 224 insertions(+), 6 deletions(-) create mode 100644 app/client/src/pages/Editor/ShareApplicationForm.tsx diff --git a/app/client/src/api/ApplicationApi.tsx b/app/client/src/api/ApplicationApi.tsx index 2023d0fdc2..ec2477a677 100644 --- a/app/client/src/api/ApplicationApi.tsx +++ b/app/client/src/api/ApplicationApi.tsx @@ -6,6 +6,11 @@ export interface PublishApplicationRequest { applicationId: string; } +export interface ChangeAppViewAccessRequest { + applicationId: string; + publicAccess: boolean; +} + export interface PublishApplicationResponse extends ApiResponse { data: {}; } @@ -79,6 +84,8 @@ class ApplicationApi extends Api { static baseURL = "v1/applications/"; static publishURLPath = (applicationId: string) => `publish/${applicationId}`; static createApplicationPath = (orgId: string) => `?orgId=${orgId}`; + static changeAppViewAccessPath = (applicationId: string) => + `${applicationId}/changeAccess`; static setDefaultPagePath = (request: SetDefaultPageRequest) => `${ApplicationApi.baseURL}${request.applicationId}/page/${request.pageId}/makeDefault`; static publishApplication( @@ -120,6 +127,17 @@ class ApplicationApi extends Api { ): AxiosPromise { return Api.put(ApplicationApi.setDefaultPagePath(request)); } + + static changeAppViewAccess( + request: ChangeAppViewAccessRequest, + ): AxiosPromise { + return Api.put( + ApplicationApi.baseURL + + ApplicationApi.changeAppViewAccessPath(request.applicationId), + { publicAccess: request.publicAccess }, + ); + } + static deleteApplication( request: DeleteApplicationRequest, ): AxiosPromise { diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx index 24c506d1a5..d5e2dbc831 100644 --- a/app/client/src/constants/ReduxActionConstants.tsx +++ b/app/client/src/constants/ReduxActionConstants.tsx @@ -82,6 +82,9 @@ export const ReduxActionTypes: { [key: string]: string } = { STORE_AS_DATASOURCE_COMPLETE: "STORE_AS_DATASOURCE_COMPLETE", PUBLISH_APPLICATION_INIT: "PUBLISH_APPLICATION_INIT", PUBLISH_APPLICATION_SUCCESS: "PUBLISH_APPLICATION_SUCCESS", + CHANGE_APPVIEW_ACCESS_INIT: "CHANGE_APPVIEW_ACCESS_INIT", + CHANGE_APPVIEW_ACCESS_SUCCESS: "CHANGE_APPVIEW_ACCESS_SUCCESS", + CHANGE_APPVIEW_ACCESS_ERROR: "CHANGE_APPVIEW_ACCESS_ERROR", CREATE_PAGE_INIT: "CREATE_PAGE_INIT", CREATE_PAGE_SUCCESS: "CREATE_PAGE_SUCCESS", FETCH_PAGE_LIST_INIT: "FETCH_PAGE_LIST_INIT", @@ -382,6 +385,7 @@ export type ApplicationPayload = { organizationId: string; pageCount: number; defaultPageId?: string; + isPublic?: boolean; userPermissions?: string[]; }; diff --git a/app/client/src/pages/Editor/EditorHeader.tsx b/app/client/src/pages/Editor/EditorHeader.tsx index 63b1ae67ff..8473bee5ab 100644 --- a/app/client/src/pages/Editor/EditorHeader.tsx +++ b/app/client/src/pages/Editor/EditorHeader.tsx @@ -1,6 +1,14 @@ -import React from "react"; +import React, { useEffect } from "react"; +import { connect } from "react-redux"; +import { AppState } from "reducers"; import styled from "styled-components"; import { Breadcrumbs, IBreadcrumbProps } from "@blueprintjs/core"; +import { ReduxActionTypes } from "constants/ReduxActionConstants"; +import { + getCurrentOrg, + getCurrentOrgId, +} from "selectors/organizationSelectors"; +import { Org } from "constants/orgConstants"; import { BASE_URL, APPLICATIONS_URL, @@ -22,6 +30,8 @@ import { import AnalyticsUtil from "utils/AnalyticsUtil"; import { Skin } from "constants/DefaultTheme"; import { HelpModal } from "components/designSystems/appsmith/help/HelpModal"; +import { FormDialogComponent } from "components/editorComponents/form/FormDialogComponent"; +import ShareApplicationForm from "pages/Editor/ShareApplicationForm"; const LoadingContainer = styled.div` display: flex; @@ -48,13 +58,16 @@ const StretchedBreadCrumb = styled(Breadcrumbs)` } `; -const InviteButton = styled.div` +const ShareButton = styled.div` display: flex; flex-grow: 1; justify-content: flex-end; `; type EditorHeaderProps = { + currentOrg: Org; + currentOrgId: string; + fetchCurrentOrg: (orgId: string) => void; isSaving?: boolean; pageSaveError?: boolean; pageName?: string; @@ -77,6 +90,12 @@ export const EditorHeader = (props: EditorHeaderProps) => { page => page.pageId === props.currentPageId, )?.pageName; + const { fetchCurrentOrg, currentOrgId } = props; + + useEffect(() => { + fetchCurrentOrg(currentOrgId); + }, [fetchCurrentOrg, currentOrgId]); + const pageSelectorData: CustomizedDropdownProps = { sections: [ { @@ -146,9 +165,22 @@ export const EditorHeader = (props: EditorHeaderProps) => { - - {/*