Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion app/client/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
const CLOUD_HOSTING = parseConfig('{{env "APPSMITH_CLOUD_HOSTING"}}');
const AIRGAPPED = parseConfig('{{env "APPSMITH_AIRGAP_ENABLED"}}');
const REO_CLIENT_ID = parseConfig('{{env "APPSMITH_REO_CLIENT_ID"}}');
const DISABLE_BETTERBUGS = parseConfig('{{env "APPSMITH_DISABLE_BETTERBUGS"}}');
</script>
<script>
window.__APPSMITH_CHUNKS_TO_PRELOAD =
Expand Down Expand Up @@ -71,6 +72,28 @@
}
</script>
<!-- End of Reo Javascript -->

<!-- Start of BetterBugs Recording Links (logs + recorder; only when BetterBugs enabled and not airgapped) -->
<script type="text/javascript">
if (!DISABLE_BETTERBUGS && !AIRGAPPED) {
window.__BetterbugsRecordingLinkConfig = {
styles: { theme: "light", primaryColor: "#E15615", primaryTextColor: "#ffffff" },
successMessageHeaderText: "Information received",
successMessageSubHeaderText: "Our support team will use it to review the issue",
};
(function () {
var s1 = document.createElement("script");
s1.src = "https://cdn.betterbugs.io/scripts/latest/logs-capture.js";
s1.async = true;
document.head.appendChild(s1);
var s2 = document.createElement("script");
s2.src = "https://cdn.betterbugs.io/scripts/latest/recorder.js";
s2.async = true;
document.head.appendChild(s2);
})();
}
</script>
<!-- End of BetterBugs Recording Links -->
</head>

<body class="appsmith-light-theme">
Expand Down Expand Up @@ -160,7 +183,6 @@
parseConfig("%REACT_APP_INTERCOM_APP_ID%") ||
parseConfig('{{env "APPSMITH_INTERCOM_APP_ID"}}');
const DISABLE_INTERCOM = parseConfig('{{env "APPSMITH_DISABLE_INTERCOM"}}');
const DISABLE_BETTERBUGS = parseConfig('{{env "APPSMITH_DISABLE_BETTERBUGS"}}');

// Initialize the Intercom library
if (INTERCOM_APP_ID.length && !DISABLE_INTERCOM) {
Expand Down
41 changes: 41 additions & 0 deletions app/client/src/actions/applicationActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {
ReduxActionErrorTypes,
ReduxActionTypes,
} from "ee/constants/ReduxActionConstants";
import type { ApplicationPayload } from "entities/Application";

export const toggleFavoriteApplication = (applicationId: string) => ({
type: ReduxActionTypes.TOGGLE_FAVORITE_APPLICATION_INIT,
payload: { applicationId },
});

export const toggleFavoriteApplicationSuccess = (
applicationId: string,
isFavorited: boolean,
) => ({
type: ReduxActionTypes.TOGGLE_FAVORITE_APPLICATION_SUCCESS,
payload: { applicationId, isFavorited },
});

export const fetchFavoriteApplications = () => ({
type: ReduxActionTypes.FETCH_FAVORITE_APPLICATIONS_INIT,
});

export const fetchFavoriteApplicationsSuccess = (
applications: ApplicationPayload[],
) => ({
type: ReduxActionTypes.FETCH_FAVORITE_APPLICATIONS_SUCCESS,
payload: applications,
});

export const fetchFavoriteApplicationsError = () => ({
type: ReduxActionErrorTypes.FETCH_FAVORITE_APPLICATIONS_ERROR,
});

export const toggleFavoriteApplicationError = (
applicationId: string,
error: unknown,
) => ({
type: ReduxActionErrorTypes.TOGGLE_FAVORITE_APPLICATION_ERROR,
payload: { applicationId, error, show: false },
});
4 changes: 4 additions & 0 deletions app/client/src/assets/icons/ads/heart-fill-red.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions app/client/src/ce/api/ApplicationApi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,16 @@ export class ApplicationApi extends Api {
`${ApplicationApi.baseURL}/${applicationId}/static-url/suggest-app-slug`,
);
}

static async toggleFavoriteApplication(
applicationId: string,
): Promise<AxiosPromise<ApiResponse>> {
return Api.put(`v1/users/applications/${applicationId}/favorite`);
}

static async getFavoriteApplications(): Promise<AxiosPromise<ApiResponse>> {
return Api.get("v1/users/favoriteApplications");
}
}

export default ApplicationApi;
6 changes: 6 additions & 0 deletions app/client/src/ce/constants/ReduxActionConstants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,10 @@ const ApplicationActionTypes = {
FORK_APPLICATION_INIT: "FORK_APPLICATION_INIT",
FORK_APPLICATION_SUCCESS: "FORK_APPLICATION_SUCCESS",
RESET_CURRENT_APPLICATION: "RESET_CURRENT_APPLICATION",
TOGGLE_FAVORITE_APPLICATION_INIT: "TOGGLE_FAVORITE_APPLICATION_INIT",
TOGGLE_FAVORITE_APPLICATION_SUCCESS: "TOGGLE_FAVORITE_APPLICATION_SUCCESS",
FETCH_FAVORITE_APPLICATIONS_INIT: "FETCH_FAVORITE_APPLICATIONS_INIT",
FETCH_FAVORITE_APPLICATIONS_SUCCESS: "FETCH_FAVORITE_APPLICATIONS_SUCCESS",
};

const ApplicationActionErrorTypes = {
Expand All @@ -692,6 +696,8 @@ const ApplicationActionErrorTypes = {
FETCH_APP_SLUG_SUGGESTION_ERROR: "FETCH_APP_SLUG_SUGGESTION_ERROR",
ENABLE_STATIC_URL_ERROR: "ENABLE_STATIC_URL_ERROR",
DISABLE_STATIC_URL_ERROR: "DISABLE_STATIC_URL_ERROR",
TOGGLE_FAVORITE_APPLICATION_ERROR: "TOGGLE_FAVORITE_APPLICATION_ERROR",
FETCH_FAVORITE_APPLICATIONS_ERROR: "FETCH_FAVORITE_APPLICATIONS_ERROR",
};

const IDEDebuggerActionTypes = {
Expand Down
9 changes: 4 additions & 5 deletions app/client/src/ce/constants/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1093,7 +1093,7 @@ export const ERROR_GIT_INVALID_REMOTE = () =>

// Git Connect V2
export const CHOOSE_A_GIT_PROVIDER_STEP = () => "Choose a Git provider";
export const GENERATE_SSH_KEY_STEP = () => "Generate SSH key";
export const GENERATE_SSH_KEY_STEP = () => "Configure SSH Key";
export const ADD_DEPLOY_KEY_STEP = () => "Add deploy key";
export const CHOOSE_GIT_PROVIDER_QUESTION = () =>
"To begin with, choose your Git service provider";
Expand All @@ -1114,19 +1114,18 @@ export const ERROR_REPO_NOT_EMPTY_MESSAGE = () =>
"Kindly create a new repository and provide its remote SSH URL here. We require an empty repository to continue.";
export const READ_DOCS = () => "Read Docs";
export const COPY_SSH_URL_MESSAGE = () =>
"To generate the SSH Key, in your repo, copy the Remote SSH URL & paste it in the input field below.";
"Copy the Remote SSH URL from your repository and paste it in the input field below.";
export const REMOTE_URL_INPUT_LABEL = () => "Remote SSH URL";
export const HOW_TO_COPY_REMOTE_URL = () =>
"How to copy & paste SSH remote URL";
export const ERROR_SSH_KEY_MISCONF_TITLE = () => "SSH key misconfiguration";
export const ERROR_SSH_KEY_MISCONF_MESSAGE = () =>
"It seems that your SSH key hasn't been added to your repository. To proceed, please revisit the steps below and configure your SSH key correctly.";
export const ADD_DEPLOY_KEY_STEP_TITLE = () =>
"Add deploy key & give write access";
export const ADD_DEPLOY_KEY_STEP_TITLE = () => "Set Up SSH Key";
export const HOW_TO_ADD_DEPLOY_KEY = () =>
"How to paste SSH Key in repo and give write access?";
export const CONSENT_ADDED_DEPLOY_KEY = () =>
"I've added the deploy key and gave it write access";
"I confirm this SSH key has write access to the repository";
export const PREVIOUS_STEP = () => "Previous step";
export const GIT_AUTHOR = () => "Git author";
export const DISCONNECT_GIT = () => "Disconnect Git";
Expand Down
10 changes: 10 additions & 0 deletions app/client/src/ce/constants/workspaceConstants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
export const FAVORITES_KEY = "__favorites__";

export const DEFAULT_FAVORITES_WORKSPACE = {
id: FAVORITES_KEY,
name: "Favorites",
isVirtual: true,
userPermissions: [] as string[],
};

export interface WorkspaceRole {
id: string;
name: string;
Expand All @@ -13,6 +22,7 @@ export interface Workspace {
logoUrl?: string;
uploadProgress?: number;
userPermissions?: string[];
isVirtual?: boolean;
}

export interface WorkspaceUserRoles {
Expand Down
2 changes: 2 additions & 0 deletions app/client/src/ce/entities/FeatureFlag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export const FEATURE_FLAG = {
release_static_url_enabled: "release_static_url_enabled",
release_window_dimensions_enabled: "release_window_dimensions_enabled",
release_branding_logo_resize_enabled: "release_branding_logo_resize_enabled",
release_ssh_key_manager_enabled: "release_ssh_key_manager_enabled",
} as const;

export type FeatureFlag = keyof typeof FEATURE_FLAG;
Expand Down Expand Up @@ -122,6 +123,7 @@ export const DEFAULT_FEATURE_FLAG_VALUE: FeatureFlags = {
release_static_url_enabled: false,
release_window_dimensions_enabled: false,
release_branding_logo_resize_enabled: false,
release_ssh_key_manager_enabled: false,
};

export const AB_TESTING_EVENT_KEYS = {
Expand Down
20 changes: 20 additions & 0 deletions app/client/src/ce/hooks/useSSHKeyManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import noop from "lodash/noop";
import type { SSHKeyOption } from "git/components/common/types";

export interface UseSSHKeyManagerReturn {
isSSHKeyManagerEnabled: boolean;
sshKeys: SSHKeyOption[] | null;
isSSHKeysLoading: boolean;
fetchSSHKeys: () => void;
onCreateSSHKey: () => void;
}

export default function useSSHKeyManager(): UseSSHKeyManagerReturn {
return {
isSSHKeyManagerEnabled: false,
sshKeys: null,
isSSHKeysLoading: false,
fetchSSHKeys: noop,
onCreateSSHKey: noop,
};
}
53 changes: 48 additions & 5 deletions app/client/src/ce/pages/Applications/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,19 @@ import {
getApplicationSearchKeyword,
getCreateApplicationError,
getCurrentApplicationIdForCreateNewApp,
getHasFavorites,
getIsCreatingApplication,
getIsDeletingApplication,
} from "ee/selectors/applicationSelectors";
import {
DEFAULT_FAVORITES_WORKSPACE,
FAVORITES_KEY,
} from "ee/constants/workspaceConstants";
import { Classes as BlueprintClasses } from "@blueprintjs/core";
import { Position } from "@blueprintjs/core/lib/esm/common/position";
import { leaveWorkspace } from "actions/userActions";
import NoSearchImage from "assets/images/NoSearchResult.svg";
import HeartIconRed from "assets/icons/ads/heart-fill-red.svg";
import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper";
import {
thinScrollbar,
Expand Down Expand Up @@ -98,6 +104,7 @@ import {
getApplicationsOfWorkspace,
getCurrentWorkspaceId,
getIsFetchingApplications,
getIsFetchingFavoriteApplications,
} from "ee/selectors/selectedWorkspaceSelectors";
import {
getIsFetchingMyOrganizations,
Expand Down Expand Up @@ -143,6 +150,7 @@ import {
GitImportModal as NewGitImportModal,
GitImportOverrideModal,
} from "git";
import { GitImportContextProvider } from "git-artifact-helpers/application/components";
import OldRepoLimitExceededErrorModal from "pages/Editor/gitSync/RepoLimitExceededErrorModal";
import { trackCurrentDomain } from "utils/multiOrgDomains";
import OrganizationDropdown from "components/OrganizationDropdown";
Expand All @@ -154,11 +162,11 @@ function GitModals() {
const isGitModEnabled = useGitModEnabled();

return isGitModEnabled ? (
<>
<GitImportContextProvider>
<NewGitImportModal />
<NewGitRepoLimitErrorModal />
<GitImportOverrideModal />
</>
</GitImportContextProvider>
) : (
<>
<OldGitSyncModal isImport />
Expand Down Expand Up @@ -330,7 +338,6 @@ export function LeftPaneSection(props: {
},
dispatch,
);
dispatch(fetchAllWorkspaces());
};

return (
Expand Down Expand Up @@ -475,13 +482,32 @@ export function WorkspaceMenuItem({

if (!workspace.id) return null;

const isFavoritesWorkspace = workspace.id === FAVORITES_KEY;
const hasLogo = workspace?.logoUrl && !imageError;
const displayText = isFetchingWorkspaces
? workspace?.name
: workspace?.name?.length > 22
? workspace.name.slice(0, 22).concat(" ...")
: workspace?.name;

// Use custom component for favorites workspace with heart icon
if (isFavoritesWorkspace && !isFetchingWorkspaces) {
return (
<WorkspaceItemRow
className={selected ? "selected-workspace" : ""}
onClick={handleWorkspaceClick}
selected={selected}
>
<WorkspaceIconContainer>
<WorkspaceLogoImage alt="Favorites" src={HeartIconRed} />
<Text type={TextType.H5} weight={FontWeight.NORMAL}>
{displayText}
</Text>
</WorkspaceIconContainer>
</WorkspaceItemRow>
);
}

// Use custom component when there's a logo, otherwise use ListItem
if (hasLogo && !isFetchingWorkspaces) {
const showTooltip = workspace?.name && workspace.name.length > 22;
Expand Down Expand Up @@ -677,6 +703,7 @@ export function ApplicationsSection(props: any) {
const isSavingWorkspaceInfo = useSelector(getIsSavingWorkspaceInfo);
const isFetchingWorkspaces = useSelector(getIsFetchingWorkspaces);
const isFetchingApplications = useSelector(getIsFetchingApplications);
const isFetchingFavoriteApps = useSelector(getIsFetchingFavoriteApplications);
const isDeletingWorkspace = useSelector(getIsDeletingWorkspace);
const { isFetchingPackages } = usePackage();
const creatingApplicationMap = useSelector(getIsCreatingApplication);
Expand Down Expand Up @@ -711,7 +738,10 @@ export function ApplicationsSection(props: any) {
dispatch(updateApplication(id, data));
};
const isLoadingResources =
isFetchingWorkspaces || isFetchingApplications || isFetchingPackages;
isFetchingWorkspaces ||
isFetchingApplications ||
isFetchingPackages ||
(activeWorkspaceId === FAVORITES_KEY && isFetchingFavoriteApps);
const isGACEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
const [
isCreateAppFromTemplateModalOpen,
Expand Down Expand Up @@ -1120,6 +1150,7 @@ export const ApplictionsMainPage = (props: any) => {
const isFetchingOrganizations = useSelector(getIsFetchingMyOrganizations);
const currentOrganizationId = useSelector(activeOrganizationId);
const isCloudBillingEnabled = useIsCloudBillingEnabled();
const hasFavorites = useSelector(getHasFavorites);

// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand All @@ -1135,6 +1166,14 @@ export const ApplictionsMainPage = (props: any) => {
) as any;
}

// Inject virtual Favorites workspace at the top if user has favorites or URL is Favorites (e.g. after 404 redirect)
if (
(hasFavorites || workspaceIdFromQueryParams === FAVORITES_KEY) &&
!isFetchingWorkspaces
) {
workspaces = [DEFAULT_FAVORITES_WORKSPACE, ...workspaces];
}

const [activeWorkspaceId, setActiveWorkspaceId] = useState<
string | undefined
>(
Expand All @@ -1159,10 +1198,14 @@ export const ApplictionsMainPage = (props: any) => {
fetchedWorkspaceId &&
fetchedWorkspaceId !== activeWorkspaceId
) {
const activeWorkspace: Workspace = workspaces.find(
let activeWorkspace: Workspace | undefined = workspaces.find(
(workspace: Workspace) => workspace.id === activeWorkspaceId,
);

if (!activeWorkspace && activeWorkspaceId === FAVORITES_KEY) {
activeWorkspace = DEFAULT_FAVORITES_WORKSPACE;
}

if (activeWorkspace) {
dispatch({
type: ReduxActionTypes.SET_CURRENT_WORKSPACE,
Expand Down
Loading
Loading