Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WEB-704] fix: inbox responsiveness #4275

Merged
merged 4 commits into from
Apr 28, 2024
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
29 changes: 27 additions & 2 deletions web/components/inbox/content/inbox-issue-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { Button, ControlLink, CustomMenu, TOAST_TYPE, setToast } from "@plane/ui
import {
DeclineIssueModal,
DeleteInboxIssueModal,
InboxIssueActionsMobileHeader,
InboxIssueCreateEditModalRoot,
InboxIssueSnoozeModal,
InboxIssueStatus,
Expand All @@ -38,10 +39,12 @@ type TInboxIssueActionsHeader = {
projectId: string;
inboxIssue: IInboxIssueStore | undefined;
isSubmitting: "submitting" | "submitted" | "saved";
toggleMobileSidebar: boolean;
setToggleMobileSidebar: (value: boolean) => void;
};

export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((props) => {
const { workspaceSlug, projectId, inboxIssue, isSubmitting } = props;
const { workspaceSlug, projectId, inboxIssue, isSubmitting, toggleMobileSidebar, setToggleMobileSidebar } = props;
// states
const [isSnoozeDateModalOpen, setIsSnoozeDateModalOpen] = useState(false);
const [selectDuplicateIssue, setSelectDuplicateIssue] = useState(false);
Expand Down Expand Up @@ -207,7 +210,7 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
/>
</>

<div className="relative flex h-full w-full items-center justify-between gap-2 px-4">
<div className="hidden relative lg:flex h-full w-full items-center justify-between gap-2 px-4">
<div className="flex items-center gap-4">
{issue?.project_id && issue.sequence_id && (
<h3 className="text-base font-medium text-custom-text-300 flex-shrink-0">
Expand Down Expand Up @@ -319,6 +322,28 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
</div>
</div>
</div>

<div className="lg:hidden">
<InboxIssueActionsMobileHeader
inboxIssue={inboxIssue}
isSubmitting={isSubmitting}
handleCopyIssueLink={handleCopyIssueLink}
setAcceptIssueModal={setAcceptIssueModal}
setDeclineIssueModal={setDeclineIssueModal}
setIsSnoozeDateModalOpen={setIsSnoozeDateModalOpen}
setSelectDuplicateIssue={setSelectDuplicateIssue}
setDeleteIssueModal={setDeleteIssueModal}
canMarkAsAccepted={canMarkAsAccepted}
canMarkAsDeclined={canMarkAsDeclined}
canMarkAsDuplicate={canMarkAsDuplicate}
canDelete={canDelete}
isAcceptedOrDeclined={isAcceptedOrDeclined}
handleInboxIssueNavigation={handleInboxIssueNavigation}
workspaceSlug={workspaceSlug}
toggleMobileSidebar={toggleMobileSidebar}
setToggleMobileSidebar={setToggleMobileSidebar}
/>
</div>
</>
);
});
170 changes: 170 additions & 0 deletions web/components/inbox/content/inbox-issue-mobile-header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import React from "react";
import { observer } from "mobx-react";
import { useRouter } from "next/router";
import {
CircleCheck,
CircleX,
ChevronDown,
ChevronUp,
Clock,
ExternalLink,
FileStack,
Link,
Trash2,
PanelLeft,
} from "lucide-react";
import { CustomMenu } from "@plane/ui";
// components
import { InboxIssueStatus } from "@/components/inbox";
import { IssueUpdateStatus } from "@/components/issues";
// helpers
import { cn } from "@/helpers/common.helper";
// store types
import type { IInboxIssueStore } from "@/store/inbox/inbox-issue.store";

type Props = {
workspaceSlug: string;
inboxIssue: IInboxIssueStore | undefined;
isSubmitting: "submitting" | "submitted" | "saved";
handleInboxIssueNavigation: (direction: "next" | "prev") => void;
canMarkAsAccepted: boolean;
canMarkAsDeclined: boolean;
isAcceptedOrDeclined: boolean | undefined;
canMarkAsDuplicate: boolean;
canDelete: boolean;
setAcceptIssueModal: (value: boolean) => void;
setDeclineIssueModal: (value: boolean) => void;
setDeleteIssueModal: (value: boolean) => void;
setIsSnoozeDateModalOpen: (value: boolean) => void;
setSelectDuplicateIssue: (value: boolean) => void;
handleCopyIssueLink: () => void;
toggleMobileSidebar: boolean;
setToggleMobileSidebar: (value: boolean) => void;
};

export const InboxIssueActionsMobileHeader: React.FC<Props> = observer((props) => {
const {
inboxIssue,
isSubmitting,
handleInboxIssueNavigation,
canMarkAsAccepted,
canMarkAsDeclined,
canDelete,
canMarkAsDuplicate,
isAcceptedOrDeclined,
workspaceSlug,
setAcceptIssueModal,
setDeclineIssueModal,
setDeleteIssueModal,
setIsSnoozeDateModalOpen,
setSelectDuplicateIssue,
handleCopyIssueLink,
toggleMobileSidebar,
setToggleMobileSidebar,
} = props;
const router = useRouter();
const issue = inboxIssue?.issue;
const currentInboxIssueId = issue?.id;

if (!issue || !inboxIssue) return null;

return (
<div className="h-12 relative flex border-custom-border-200 w-full items-center gap-2 px-4">
<PanelLeft
onClick={() => setToggleMobileSidebar(!toggleMobileSidebar)}
className={cn(
"w-4 h-4 flex-shrink-0 mr-2",
toggleMobileSidebar ? "text-custom-primary-100" : "text-custom-text-200"
)}
/>
<div className="flex items-center gap-2 w-full">
<div className="flex items-center gap-x-2">
<button
type="button"
className="rounded border border-custom-border-200 p-1.5"
onClick={() => handleInboxIssueNavigation("prev")}
>
<ChevronUp size={14} strokeWidth={2} />
</button>
<button
type="button"
className="rounded border border-custom-border-200 p-1.5"
onClick={() => handleInboxIssueNavigation("next")}
>
<ChevronDown size={14} strokeWidth={2} />
</button>
</div>
<div className="flex items-center gap-4">
<InboxIssueStatus inboxIssue={inboxIssue} iconSize={12} />
<div className="flex items-center justify-end w-full">
<IssueUpdateStatus isSubmitting={isSubmitting} />
</div>
</div>
<div className="ml-auto">
<CustomMenu verticalEllipsis placement="bottom-start">
{isAcceptedOrDeclined && (
<CustomMenu.MenuItem onClick={handleCopyIssueLink}>
<div className="flex items-center gap-2">
<Link size={14} strokeWidth={2} />
Copy issue link
</div>
</CustomMenu.MenuItem>
)}
{isAcceptedOrDeclined && (
<CustomMenu.MenuItem
onClick={() =>
router.push(`/${workspaceSlug}/projects/${issue?.project_id}/issues/${currentInboxIssueId}`)
}
>
<div className="flex items-center gap-2">
<ExternalLink size={14} strokeWidth={2} />
Open issue
</div>
</CustomMenu.MenuItem>
)}
{canMarkAsAccepted && !isAcceptedOrDeclined && (
<CustomMenu.MenuItem onClick={() => setIsSnoozeDateModalOpen(true)}>
<div className="flex items-center gap-2">
<Clock size={14} strokeWidth={2} />
Snooze
</div>
</CustomMenu.MenuItem>
)}
{canMarkAsDuplicate && !isAcceptedOrDeclined && (
<CustomMenu.MenuItem onClick={() => setSelectDuplicateIssue(true)}>
<div className="flex items-center gap-2">
<FileStack size={14} strokeWidth={2} />
Mark as duplicate
</div>
</CustomMenu.MenuItem>
)}
{canMarkAsAccepted && (
<CustomMenu.MenuItem onClick={() => setAcceptIssueModal(true)}>
<div className="flex items-center gap-2 text-green-500">
<CircleCheck size={14} strokeWidth={2} />
Accept
</div>
</CustomMenu.MenuItem>
)}
{canMarkAsDeclined && (
<CustomMenu.MenuItem onClick={() => setDeclineIssueModal(true)}>
<div className="flex items-center gap-2 text-red-500">
<CircleX size={14} strokeWidth={2} />
Decline
</div>
</CustomMenu.MenuItem>
)}
{canDelete && !isAcceptedOrDeclined && (
<CustomMenu.MenuItem onClick={() => setDeleteIssueModal(true)}>
<div className="flex items-center gap-2 text-red-500">
<Trash2 size={14} strokeWidth={2} />
Delete
</div>
</CustomMenu.MenuItem>
)}
</CustomMenu>
</div>
</div>
</div>
);
});
1 change: 1 addition & 0 deletions web/components/inbox/content/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./root";
export * from "./inbox-issue-header";
export * from "./inbox-issue-mobile-header";
export * from "./issue-properties";
export * from "./issue-root";
6 changes: 5 additions & 1 deletion web/components/inbox/content/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ type TInboxContentRoot = {
workspaceSlug: string;
projectId: string;
inboxIssueId: string;
toggleMobileSidebar: boolean;
setToggleMobileSidebar: (value: boolean) => void;
};

export const InboxContentRoot: FC<TInboxContentRoot> = observer((props) => {
const { workspaceSlug, projectId, inboxIssueId } = props;
const { workspaceSlug, projectId, inboxIssueId, toggleMobileSidebar, setToggleMobileSidebar } = props;
// states
const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved");
// hooks
Expand Down Expand Up @@ -43,6 +45,8 @@ export const InboxContentRoot: FC<TInboxContentRoot> = observer((props) => {
<div className="w-full h-full overflow-hidden relative flex flex-col">
<div className="flex-shrink-0 min-h-[50px] border-b border-custom-border-300">
<InboxIssueActionsHeader
setToggleMobileSidebar={setToggleMobileSidebar}
toggleMobileSidebar={toggleMobileSidebar}
workspaceSlug={workspaceSlug}
projectId={projectId}
inboxIssue={inboxIssue}
Expand Down
57 changes: 42 additions & 15 deletions web/components/inbox/root.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { FC } from "react";
import { FC, useState } from "react";
import { observer } from "mobx-react";
import useSWR from "swr";
import { Inbox } from "lucide-react";
import { Inbox, PanelLeft } from "lucide-react";
// components
import { EmptyState } from "@/components/empty-state";
import { InboxSidebar, InboxContentRoot } from "@/components/inbox";
import { InboxLayoutLoader } from "@/components/ui";
// constants
import { EmptyStateType } from "@/constants/empty-state";
// helpers
import { cn } from "@/helpers/common.helper";
// hooks
import { useProjectInbox } from "@/hooks/store";

Expand All @@ -20,6 +22,8 @@ type TInboxIssueRoot = {

export const InboxIssueRoot: FC<TInboxIssueRoot> = observer((props) => {
const { workspaceSlug, projectId, inboxIssueId, inboxAccessible } = props;
// states
const [toggleMobileSidebar, setToggleMobileSidebar] = useState(false);
// hooks
const { loader, error, fetchInboxIssues } = useProjectInbox();

Expand Down Expand Up @@ -52,20 +56,43 @@ export const InboxIssueRoot: FC<TInboxIssueRoot> = observer((props) => {
);

return (
<div className="relative w-full h-full flex overflow-hidden">
<InboxSidebar workspaceSlug={workspaceSlug.toString()} projectId={projectId.toString()} />

{inboxIssueId ? (
<InboxContentRoot
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
inboxIssueId={inboxIssueId.toString()}
/>
) : (
<div className="w-full h-full relative flex justify-center items-center">
<EmptyState type={EmptyStateType.INBOX_DETAIL_EMPTY_STATE} layout="screen-simple" />
<>
{!inboxIssueId && (
<div className="flex lg:hidden items-center px-4 w-full h-12 border-b border-custom-border-200">
<PanelLeft
onClick={() => setToggleMobileSidebar(!toggleMobileSidebar)}
className={cn("w-4 h-4 ", toggleMobileSidebar ? "text-custom-primary-100" : " text-custom-text-200")}
/>
</div>
)}
</div>
<div className="w-full h-full flex overflow-hidden bg-custom-background-100">
<div
className={cn(
"absolute z-10 top-[50px] lg:!top-0 lg:!relative bg-custom-background-100 flex-shrink-0 w-full lg:w-2/6 bottom-0 transition-all",
toggleMobileSidebar ? "translate-x-0" : "-translate-x-full lg:!translate-x-0",
)}
>
<InboxSidebar
setToggleMobileSidebar={setToggleMobileSidebar}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
/>
</div>

{inboxIssueId ? (
<InboxContentRoot
setToggleMobileSidebar={setToggleMobileSidebar}
toggleMobileSidebar={toggleMobileSidebar}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
inboxIssueId={inboxIssueId.toString()}
/>
) : (
<div className="w-full h-full relative flex justify-center items-center">
<EmptyState type={EmptyStateType.INBOX_DETAIL_EMPTY_STATE} layout="screen-simple" />
</div>
)}
</div>
</>
);
});
4 changes: 3 additions & 1 deletion web/components/inbox/sidebar/inbox-list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ type InboxIssueListItemProps = {
projectId: string;
projectIdentifier?: string;
inboxIssue: IInboxIssueStore;
setToggleMobileSidebar: (value: boolean) => void;
};

export const InboxIssueListItem: FC<InboxIssueListItemProps> = observer((props) => {
const { workspaceSlug, projectId, inboxIssue, projectIdentifier } = props;
const { workspaceSlug, projectId, inboxIssue, projectIdentifier,setToggleMobileSidebar } = props;
// router
const router = useRouter();
const { inboxIssueId } = router.query;
Expand All @@ -34,6 +35,7 @@ export const InboxIssueListItem: FC<InboxIssueListItemProps> = observer((props)

const handleIssueRedirection = (event: MouseEvent, currentIssueId: string | undefined) => {
if (inboxIssueId === currentIssueId) event.preventDefault();
setToggleMobileSidebar(false);
};

if (!issue) return <></>;
Expand Down
4 changes: 3 additions & 1 deletion web/components/inbox/sidebar/inbox-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ export type InboxIssueListProps = {
projectId: string;
projectIdentifier?: string;
inboxIssues: IInboxIssueStore[];
setToggleMobileSidebar: (value: boolean) => void;
};

export const InboxIssueList: FC<InboxIssueListProps> = observer((props) => {
const { workspaceSlug, projectId, projectIdentifier, inboxIssues } = props;
const { workspaceSlug, projectId, projectIdentifier, inboxIssues, setToggleMobileSidebar } = props;

return (
<>
{inboxIssues.map((inboxIssue) => (
<Fragment key={inboxIssue.id}>
<InboxIssueListItem
setToggleMobileSidebar={setToggleMobileSidebar}
workspaceSlug={workspaceSlug}
projectId={projectId}
projectIdentifier={projectIdentifier}
Expand Down
Loading
Loading