[WEB-1956] fix: Keyboard shortcuts (#5134)
* fix: shortcuts * fix: naming * fix: structure optimization
This commit is contained in:
parent
c2b5464e40
commit
e7948eabf2
5 changed files with 90 additions and 19 deletions
|
|
@ -46,7 +46,7 @@ export const CommandModal: React.FC = observer(() => {
|
||||||
// hooks
|
// hooks
|
||||||
const { getProjectById, workspaceProjectIds } = useProject();
|
const { getProjectById, workspaceProjectIds } = useProject();
|
||||||
const { isMobile } = usePlatformOS();
|
const { isMobile } = usePlatformOS();
|
||||||
const { canPerformWorkspaceCreateActions } = useUser();
|
const { canPerformAnyCreateAction } = useUser();
|
||||||
// states
|
// states
|
||||||
const [placeholder, setPlaceholder] = useState("Type a command or search...");
|
const [placeholder, setPlaceholder] = useState("Type a command or search...");
|
||||||
const [resultsCount, setResultsCount] = useState(0);
|
const [resultsCount, setResultsCount] = useState(0);
|
||||||
|
|
@ -286,7 +286,7 @@ export const CommandModal: React.FC = observer(() => {
|
||||||
{workspaceSlug &&
|
{workspaceSlug &&
|
||||||
workspaceProjectIds &&
|
workspaceProjectIds &&
|
||||||
workspaceProjectIds.length > 0 &&
|
workspaceProjectIds.length > 0 &&
|
||||||
canPerformWorkspaceCreateActions && (
|
canPerformAnyCreateAction && (
|
||||||
<Command.Group heading="Issue">
|
<Command.Group heading="Issue">
|
||||||
<Command.Item
|
<Command.Item
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,12 @@ export const CommandPalette: FC = observer(() => {
|
||||||
const { toggleSidebar } = useAppTheme();
|
const { toggleSidebar } = useAppTheme();
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement } = useEventTracker();
|
||||||
const { platform } = usePlatformOS();
|
const { platform } = usePlatformOS();
|
||||||
const { data: currentUser, canPerformProjectCreateActions, canPerformWorkspaceCreateActions } = useUser();
|
const {
|
||||||
|
data: currentUser,
|
||||||
|
canPerformProjectCreateActions,
|
||||||
|
canPerformWorkspaceCreateActions,
|
||||||
|
canPerformAnyCreateAction,
|
||||||
|
} = useUser();
|
||||||
const {
|
const {
|
||||||
issues: { removeIssue },
|
issues: { removeIssue },
|
||||||
} = useIssuesStore();
|
} = useIssuesStore();
|
||||||
|
|
@ -120,6 +125,18 @@ export const CommandPalette: FC = observer(() => {
|
||||||
[canPerformWorkspaceCreateActions]
|
[canPerformWorkspaceCreateActions]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const performAnyProjectCreateActions = useCallback(
|
||||||
|
(showToast: boolean = true) => {
|
||||||
|
if (!canPerformAnyCreateAction && showToast)
|
||||||
|
setToast({
|
||||||
|
type: TOAST_TYPE.ERROR,
|
||||||
|
title: "You don't have permission to perform this action.",
|
||||||
|
});
|
||||||
|
return canPerformAnyCreateAction;
|
||||||
|
},
|
||||||
|
[canPerformAnyCreateAction]
|
||||||
|
);
|
||||||
|
|
||||||
const shortcutsList: {
|
const shortcutsList: {
|
||||||
global: Record<string, { title: string; description: string; action: () => void }>;
|
global: Record<string, { title: string; description: string; action: () => void }>;
|
||||||
workspace: Record<string, { title: string; description: string; action: () => void }>;
|
workspace: Record<string, { title: string; description: string; action: () => void }>;
|
||||||
|
|
@ -222,7 +239,10 @@ export const CommandPalette: FC = observer(() => {
|
||||||
}
|
}
|
||||||
} else if (!isAnyModalOpen) {
|
} else if (!isAnyModalOpen) {
|
||||||
setTrackElement("Shortcut key");
|
setTrackElement("Shortcut key");
|
||||||
if (Object.keys(shortcutsList.global).includes(keyPressed) && performWorkspaceCreateActions())
|
if (
|
||||||
|
Object.keys(shortcutsList.global).includes(keyPressed) &&
|
||||||
|
((!projectId && performAnyProjectCreateActions()) || performProjectCreateActions())
|
||||||
|
)
|
||||||
shortcutsList.global[keyPressed].action();
|
shortcutsList.global[keyPressed].action();
|
||||||
// workspace authorized actions
|
// workspace authorized actions
|
||||||
else if (
|
else if (
|
||||||
|
|
@ -244,6 +264,7 @@ export const CommandPalette: FC = observer(() => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
|
performAnyProjectCreateActions,
|
||||||
performProjectCreateActions,
|
performProjectCreateActions,
|
||||||
performWorkspaceCreateActions,
|
performWorkspaceCreateActions,
|
||||||
copyIssueUrlToClipboard,
|
copyIssueUrlToClipboard,
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ import { renderFormattedPayloadDate, getDate } from "@/helpers/date-time.helper"
|
||||||
import { getChangedIssuefields, getDescriptionPlaceholder } from "@/helpers/issue.helper";
|
import { getChangedIssuefields, getDescriptionPlaceholder } from "@/helpers/issue.helper";
|
||||||
import { shouldRenderProject } from "@/helpers/project.helper";
|
import { shouldRenderProject } from "@/helpers/project.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useProjectEstimates, useInstance, useIssueDetail, useProject, useWorkspace } from "@/hooks/store";
|
import { useProjectEstimates, useInstance, useIssueDetail, useProject, useWorkspace, useUser } from "@/hooks/store";
|
||||||
import useKeypress from "@/hooks/use-keypress";
|
import useKeypress from "@/hooks/use-keypress";
|
||||||
import { useProjectIssueProperties } from "@/hooks/use-project-issue-properties";
|
import { useProjectIssueProperties } from "@/hooks/use-project-issue-properties";
|
||||||
// services
|
// services
|
||||||
|
|
@ -121,6 +121,8 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
||||||
const workspaceStore = useWorkspace();
|
const workspaceStore = useWorkspace();
|
||||||
const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug?.toString())?.id as string;
|
const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug?.toString())?.id as string;
|
||||||
const { config } = useInstance();
|
const { config } = useInstance();
|
||||||
|
const { projectsWithCreatePermissions } = useUser();
|
||||||
|
|
||||||
const { getProjectById } = useProject();
|
const { getProjectById } = useProject();
|
||||||
const { areEstimateEnabledByProjectId } = useProjectEstimates();
|
const { areEstimateEnabledByProjectId } = useProjectEstimates();
|
||||||
|
|
||||||
|
|
@ -341,7 +343,8 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
||||||
rules={{
|
rules={{
|
||||||
required: true,
|
required: true,
|
||||||
}}
|
}}
|
||||||
render={({ field: { value, onChange } }) => (
|
render={({ field: { value, onChange } }) =>
|
||||||
|
projectsWithCreatePermissions && projectsWithCreatePermissions[value!] ? (
|
||||||
<div className="h-7">
|
<div className="h-7">
|
||||||
<ProjectDropdown
|
<ProjectDropdown
|
||||||
value={value}
|
value={value}
|
||||||
|
|
@ -354,7 +357,10 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
||||||
tabIndex={getTabIndex("project_id")}
|
tabIndex={getTabIndex("project_id")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
) : (
|
||||||
|
<></>
|
||||||
|
)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<h3 className="text-xl font-medium text-custom-text-200">{data?.id ? "Update" : "Create"} Issue</h3>
|
<h3 className="text-xl font-medium text-custom-text-200">{data?.id ? "Update" : "Create"} Issue</h3>
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ export interface IUserStore {
|
||||||
// computed
|
// computed
|
||||||
canPerformProjectCreateActions: boolean;
|
canPerformProjectCreateActions: boolean;
|
||||||
canPerformWorkspaceCreateActions: boolean;
|
canPerformWorkspaceCreateActions: boolean;
|
||||||
|
canPerformAnyCreateAction: boolean;
|
||||||
|
projectsWithCreatePermissions: { [projectId: string]: number } | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserStore implements IUserStore {
|
export class UserStore implements IUserStore {
|
||||||
|
|
@ -91,6 +93,8 @@ export class UserStore implements IUserStore {
|
||||||
// computed
|
// computed
|
||||||
canPerformProjectCreateActions: computed,
|
canPerformProjectCreateActions: computed,
|
||||||
canPerformWorkspaceCreateActions: computed,
|
canPerformWorkspaceCreateActions: computed,
|
||||||
|
canPerformAnyCreateAction: computed,
|
||||||
|
projectsWithCreatePermissions: computed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,6 +140,26 @@ export class UserStore implements IUserStore {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description fetches the prjects with write permissions
|
||||||
|
* @returns {{[projectId: string]: number} || null}
|
||||||
|
*/
|
||||||
|
fetchProjectsWithCreatePermissions() {
|
||||||
|
const allWorkspaceRoles =
|
||||||
|
this.membership.workspaceProjectsRole &&
|
||||||
|
this.membership.workspaceProjectsRole[this.membership.router.workspaceSlug || ""];
|
||||||
|
return (
|
||||||
|
(allWorkspaceRoles &&
|
||||||
|
Object.keys(allWorkspaceRoles)
|
||||||
|
.filter((key) => allWorkspaceRoles[key] >= EUserProjectRoles.MEMBER)
|
||||||
|
.reduce(
|
||||||
|
(res: { [projectId: string]: number }, key: string) => ((res[key] = allWorkspaceRoles[key]), res),
|
||||||
|
{}
|
||||||
|
)) ||
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description updates the current user
|
* @description updates the current user
|
||||||
* @param data
|
* @param data
|
||||||
|
|
@ -229,6 +253,23 @@ export class UserStore implements IUserStore {
|
||||||
this.store.resetOnSignOut();
|
this.store.resetOnSignOut();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description returns projects where user has permissions
|
||||||
|
* @returns {{[projectId: string]: number} || null}
|
||||||
|
*/
|
||||||
|
get projectsWithCreatePermissions() {
|
||||||
|
return this.fetchProjectsWithCreatePermissions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description returns true if user has permissions to write in any project
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
get canPerformAnyCreateAction() {
|
||||||
|
const filteredProjects = this.fetchProjectsWithCreatePermissions();
|
||||||
|
return filteredProjects ? Object.keys(filteredProjects).length > 0 : false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description tells if user has project create actions permissions
|
* @description tells if user has project create actions permissions
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { ProjectMemberService } from "@/services/project";
|
||||||
import { UserService } from "@/services/user.service";
|
import { UserService } from "@/services/user.service";
|
||||||
// plane web store
|
// plane web store
|
||||||
import { CoreRootStore } from "../root.store";
|
import { CoreRootStore } from "../root.store";
|
||||||
|
import { IRouterStore } from "../router.store";
|
||||||
|
|
||||||
export interface IUserMembershipStore {
|
export interface IUserMembershipStore {
|
||||||
// observables
|
// observables
|
||||||
|
|
@ -47,6 +48,8 @@ export interface IUserMembershipStore {
|
||||||
leaveWorkspace: (workspaceSlug: string) => Promise<void>;
|
leaveWorkspace: (workspaceSlug: string) => Promise<void>;
|
||||||
joinProject: (workspaceSlug: string, projectIds: string[]) => Promise<any>;
|
joinProject: (workspaceSlug: string, projectIds: string[]) => Promise<any>;
|
||||||
leaveProject: (workspaceSlug: string, projectId: string) => Promise<void>;
|
leaveProject: (workspaceSlug: string, projectId: string) => Promise<void>;
|
||||||
|
|
||||||
|
router: IRouterStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserMembershipStore implements IUserMembershipStore {
|
export class UserMembershipStore implements IUserMembershipStore {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue