Feat: Share application publicly (#89)
* Feat: Share application publically * fix: eslint warnings and code refactor
This commit is contained in:
parent
b1e8e83e5d
commit
2956f1b3d0
|
|
@ -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<ApiResponse> {
|
||||
return Api.put(ApplicationApi.setDefaultPagePath(request));
|
||||
}
|
||||
|
||||
static changeAppViewAccess(
|
||||
request: ChangeAppViewAccessRequest,
|
||||
): AxiosPromise<ApiResponse> {
|
||||
return Api.put(
|
||||
ApplicationApi.baseURL +
|
||||
ApplicationApi.changeAppViewAccessPath(request.applicationId),
|
||||
{ publicAccess: request.publicAccess },
|
||||
);
|
||||
}
|
||||
|
||||
static deleteApplication(
|
||||
request: DeleteApplicationRequest,
|
||||
): AxiosPromise<ApiResponse> {
|
||||
|
|
|
|||
|
|
@ -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[];
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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) => {
|
|||
<StyledHeader>
|
||||
<StretchedBreadCrumb items={navigation} minVisibleItems={3} />
|
||||
<CustomizedDropdown {...pageSelectorData} />
|
||||
<InviteButton>
|
||||
{/* <Button text="Share" intent="primary" filled size="small" /> */}
|
||||
</InviteButton>
|
||||
<ShareButton>
|
||||
<FormDialogComponent
|
||||
trigger={
|
||||
<Button
|
||||
text="Share"
|
||||
intent="primary"
|
||||
outline
|
||||
size="small"
|
||||
className="t--application-publish-btn"
|
||||
/>
|
||||
}
|
||||
Form={ShareApplicationForm}
|
||||
title={props.currentOrg.name}
|
||||
/>
|
||||
</ShareButton>
|
||||
|
||||
<LoadingContainer>{saveStatusMessage}</LoadingContainer>
|
||||
<PreviewPublishSection>
|
||||
<Button
|
||||
|
|
@ -166,4 +198,19 @@ export const EditorHeader = (props: EditorHeaderProps) => {
|
|||
);
|
||||
};
|
||||
|
||||
export default EditorHeader;
|
||||
const mapStateToProps = (state: AppState) => ({
|
||||
currentOrg: getCurrentOrg(state),
|
||||
currentOrgId: getCurrentOrgId(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch: any) => ({
|
||||
fetchCurrentOrg: (orgId: string) =>
|
||||
dispatch({
|
||||
type: ReduxActionTypes.FETCH_CURRENT_ORG,
|
||||
payload: {
|
||||
orgId,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(EditorHeader);
|
||||
|
|
|
|||
94
app/client/src/pages/Editor/ShareApplicationForm.tsx
Normal file
94
app/client/src/pages/Editor/ShareApplicationForm.tsx
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import React, { useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { withRouter } from "react-router";
|
||||
import { connect } from "react-redux";
|
||||
import { AppState } from "reducers";
|
||||
import { StyledSwitch } from "components/propertyControls/StyledControls";
|
||||
import { fetchApplication } from "actions/applicationActions";
|
||||
import Spinner from "components/editorComponents/Spinner";
|
||||
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||
|
||||
const ShareWithPublicOption = styled.div`
|
||||
{
|
||||
display: flex;
|
||||
padding-top: 20px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
`;
|
||||
|
||||
const ShareToggle = styled.div`
|
||||
{
|
||||
&&& label {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
&&& div {
|
||||
margin-right: 5px;
|
||||
}
|
||||
display: flex;
|
||||
}
|
||||
`;
|
||||
|
||||
export const ShareApplicationForm = (props: any) => {
|
||||
const {
|
||||
match: {
|
||||
params: { applicationId },
|
||||
},
|
||||
fetchApplication,
|
||||
isFetchingApplication,
|
||||
isChangingViewAccess,
|
||||
currentApplicationDetails,
|
||||
changeAppViewAccess,
|
||||
} = props;
|
||||
|
||||
useEffect(() => {
|
||||
fetchApplication(applicationId);
|
||||
}, [fetchApplication, applicationId]);
|
||||
|
||||
return (
|
||||
<ShareWithPublicOption>
|
||||
Share the application with anyone
|
||||
<ShareToggle>
|
||||
{(isChangingViewAccess || isFetchingApplication) && (
|
||||
<Spinner size={20} />
|
||||
)}
|
||||
{currentApplicationDetails && (
|
||||
<StyledSwitch
|
||||
onChange={() => {
|
||||
changeAppViewAccess(
|
||||
applicationId,
|
||||
!currentApplicationDetails.isPublic,
|
||||
);
|
||||
}}
|
||||
disabled={isChangingViewAccess || isFetchingApplication}
|
||||
checked={currentApplicationDetails.isPublic}
|
||||
large
|
||||
/>
|
||||
)}
|
||||
</ShareToggle>
|
||||
</ShareWithPublicOption>
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps = (state: AppState) => ({
|
||||
currentApplicationDetails: state.ui.applications.currentApplication,
|
||||
isFetchingApplication: state.ui.applications.isFetchingApplication,
|
||||
isChangingViewAccess: state.ui.applications.isChangingViewAccess,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch: any) => ({
|
||||
fetchApplication: (applicationId: string) => {
|
||||
return dispatch(fetchApplication(applicationId));
|
||||
},
|
||||
changeAppViewAccess: (applicationId: string, publicAccess: boolean) =>
|
||||
dispatch({
|
||||
type: ReduxActionTypes.CHANGE_APPVIEW_ACCESS_INIT,
|
||||
payload: {
|
||||
applicationId,
|
||||
publicAccess,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
export default withRouter(
|
||||
connect(mapStateToProps, mapDispatchToProps)(ShareApplicationForm),
|
||||
);
|
||||
|
|
@ -10,6 +10,8 @@ import { ERROR_MESSAGE_CREATE_APPLICATION } from "constants/messages";
|
|||
|
||||
const initialState: ApplicationsReduxState = {
|
||||
isFetchingApplications: false,
|
||||
isFetchingApplication: false,
|
||||
isChangingViewAccess: false,
|
||||
applicationList: [],
|
||||
creatingApplication: false,
|
||||
deletingApplication: false,
|
||||
|
|
@ -56,6 +58,22 @@ const applicationsReducer = createReducer(initialState, {
|
|||
) => {
|
||||
return { ...state, deletingApplication: false };
|
||||
},
|
||||
[ReduxActionTypes.CHANGE_APPVIEW_ACCESS_INIT]: (
|
||||
state: ApplicationsReduxState,
|
||||
) => ({ ...state, isChangingViewAccess: true }),
|
||||
[ReduxActionTypes.CHANGE_APPVIEW_ACCESS_SUCCESS]: (
|
||||
state: ApplicationsReduxState,
|
||||
action: ReduxAction<{ id: string; isPublic: boolean }>,
|
||||
) => {
|
||||
return {
|
||||
...state,
|
||||
isChangingViewAccess: false,
|
||||
currentApplication: {
|
||||
...state.currentApplication,
|
||||
isPublic: action.payload.isPublic,
|
||||
},
|
||||
};
|
||||
},
|
||||
[ReduxActionTypes.FETCH_APPLICATION_LIST_INIT]: (
|
||||
state: ApplicationsReduxState,
|
||||
) => ({ ...state, isFetchingApplications: true }),
|
||||
|
|
@ -127,6 +145,8 @@ export interface ApplicationsReduxState {
|
|||
applicationList: ApplicationPayload[];
|
||||
searchKeyword?: string;
|
||||
isFetchingApplications: boolean;
|
||||
isFetchingApplication: boolean;
|
||||
isChangingViewAccess: boolean;
|
||||
creatingApplication: boolean;
|
||||
createApplicationError?: string;
|
||||
deletingApplication: boolean;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import ApplicationApi, {
|
|||
FetchUsersApplicationsOrgsResponse,
|
||||
OrganizationApplicationObject,
|
||||
ApplicationObject,
|
||||
ChangeAppViewAccessRequest,
|
||||
} from "api/ApplicationApi";
|
||||
import { getDefaultPageId } from "./SagaUtils";
|
||||
import { call, put, takeLatest, all, select } from "redux-saga/effects";
|
||||
|
|
@ -29,6 +30,7 @@ import { BUILDER_PAGE_URL } from "constants/routes";
|
|||
import { AppState } from "reducers";
|
||||
import { setDefaultApplicationPageSuccess } from "actions/applicationActions";
|
||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||
|
||||
export function* publishApplicationSaga(
|
||||
requestAction: ReduxAction<PublishApplicationRequest>,
|
||||
) {
|
||||
|
|
@ -214,6 +216,35 @@ export function* deleteApplicationSaga(
|
|||
}
|
||||
}
|
||||
|
||||
export function* changeAppViewAccessSaga(
|
||||
requestAction: ReduxAction<ChangeAppViewAccessRequest>,
|
||||
) {
|
||||
try {
|
||||
const request = requestAction.payload;
|
||||
const response: ApiResponse = yield call(
|
||||
ApplicationApi.changeAppViewAccess,
|
||||
request,
|
||||
);
|
||||
const isValidResponse = yield validateResponse(response);
|
||||
if (isValidResponse) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.CHANGE_APPVIEW_ACCESS_SUCCESS,
|
||||
payload: {
|
||||
id: response.data.id,
|
||||
isPublic: response.data.isPublic,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionErrorTypes.CHANGE_APPVIEW_ACCESS_ERROR,
|
||||
payload: {
|
||||
error,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function* createApplicationSaga(
|
||||
action: ReduxAction<{
|
||||
applicationName: string;
|
||||
|
|
@ -299,6 +330,10 @@ export default function* applicationSagas() {
|
|||
ReduxActionTypes.FETCH_APPLICATION_LIST_INIT,
|
||||
fetchApplicationListSaga,
|
||||
),
|
||||
takeLatest(
|
||||
ReduxActionTypes.CHANGE_APPVIEW_ACCESS_INIT,
|
||||
changeAppViewAccessSaga,
|
||||
),
|
||||
takeLatest(
|
||||
ReduxActionTypes.GET_ALL_APPLICATION_INIT,
|
||||
getAllApplicationSaga,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user