Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a47f709c5d |
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.24 on 2026-01-10 18:13
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('db', '0115_auto_20260105_1406'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='profile',
|
||||
name='notification_view_mode',
|
||||
field=models.CharField(choices=[('full', 'Full'), ('compact', 'Compact')], default='full', max_length=255),
|
||||
),
|
||||
]
|
||||
@@ -1,38 +0,0 @@
|
||||
# Generated by Django 4.2.27 on 2026-01-13 10:38
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('db', '0115_auto_20260105_1406'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='profile',
|
||||
name='notification_view_mode',
|
||||
field=models.CharField(choices=[('full', 'Full'), ('compact', 'Compact')], default='full', max_length=255),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='is_password_reset_required',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspacemember',
|
||||
name='explored_features',
|
||||
field=models.JSONField(default=dict),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspacemember',
|
||||
name='getting_started_checklist',
|
||||
field=models.JSONField(default=dict),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspacemember',
|
||||
name='tips',
|
||||
field=models.JSONField(default=dict),
|
||||
),
|
||||
]
|
||||
@@ -84,7 +84,7 @@ class User(AbstractBaseUser, PermissionsMixin):
|
||||
is_staff = models.BooleanField(default=False)
|
||||
is_email_verified = models.BooleanField(default=False)
|
||||
is_password_autoset = models.BooleanField(default=False)
|
||||
is_password_reset_required = models.BooleanField(default=False)
|
||||
|
||||
# random token generated
|
||||
token = models.CharField(max_length=64, blank=True)
|
||||
|
||||
|
||||
@@ -214,9 +214,6 @@ class WorkspaceMember(BaseModel):
|
||||
default_props = models.JSONField(default=get_default_props)
|
||||
issue_props = models.JSONField(default=get_issue_props)
|
||||
is_active = models.BooleanField(default=True)
|
||||
getting_started_checklist = models.JSONField(default=dict)
|
||||
tips = models.JSONField(default=dict)
|
||||
explored_features = models.JSONField(default=dict)
|
||||
|
||||
class Meta:
|
||||
unique_together = ["workspace", "member", "deleted_at"]
|
||||
|
||||
@@ -20,7 +20,7 @@ type Props = {
|
||||
|
||||
const PEEK_MODES: {
|
||||
key: IPeekMode;
|
||||
icon: any;
|
||||
icon: React.FC<React.SVGProps<SVGSVGElement>>;
|
||||
label: string;
|
||||
}[] = [
|
||||
{ key: "side", icon: SidePanelIcon, label: "Side Peek" },
|
||||
|
||||
@@ -2,8 +2,8 @@ import { set } from "lodash-es";
|
||||
import { action, makeObservable, observable, runInAction } from "mobx";
|
||||
// plane imports
|
||||
import { UserService } from "@plane/services";
|
||||
import { NOTIFICATION_VIEW_MODES, EStartOfTheWeek } from "@plane/types";
|
||||
import type { TUserProfile } from "@plane/types";
|
||||
import { EStartOfTheWeek } from "@plane/types";
|
||||
// store
|
||||
import type { CoreRootStore } from "@/store/root.store";
|
||||
|
||||
@@ -44,6 +44,7 @@ export class ProfileStore implements IProfileStore {
|
||||
},
|
||||
is_onboarded: false,
|
||||
is_tour_completed: false,
|
||||
notification_view_mode: NOTIFICATION_VIEW_MODES[0].key,
|
||||
use_case: undefined,
|
||||
billing_address_country: undefined,
|
||||
billing_address: undefined,
|
||||
|
||||
@@ -1,43 +1,24 @@
|
||||
// components
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams, usePathname } from "next/navigation";
|
||||
import { cn } from "@plane/utils";
|
||||
import { TopNavPowerK } from "@/components/navigation";
|
||||
import { HelpMenuRoot } from "@/components/workspace/sidebar/help-section/root";
|
||||
import { UserMenuRoot } from "@/components/workspace/sidebar/user-menu-root";
|
||||
import { WorkspaceMenuRoot } from "@/components/workspace/sidebar/workspace-menu-root";
|
||||
import { useAppRailPreferences } from "@/hooks/use-navigation-preferences";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
import { AppSidebarItem } from "@/components/sidebar/sidebar-item";
|
||||
import { InboxIcon } from "@plane/propel/icons";
|
||||
import useSWR from "swr";
|
||||
import { useWorkspaceNotifications } from "@/hooks/store/notifications";
|
||||
import { cn } from "@plane/utils";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// local imports
|
||||
import { StarUsOnGitHubLink } from "@/app/(all)/[workspaceSlug]/(projects)/star-us-link";
|
||||
import { NotificationsPopoverRoot } from "@/components/notifications/popover/root";
|
||||
|
||||
export const TopNavigationRoot = observer(function TopNavigationRoot() {
|
||||
// router
|
||||
const { workspaceSlug } = useParams();
|
||||
const pathname = usePathname();
|
||||
|
||||
// store hooks
|
||||
const { unreadNotificationsCount, getUnreadNotificationsCount } = useWorkspaceNotifications();
|
||||
const { preferences } = useAppRailPreferences();
|
||||
|
||||
const showLabel = preferences.displayMode === "icon_with_label";
|
||||
|
||||
// Fetch notification count
|
||||
useSWR(
|
||||
workspaceSlug ? "WORKSPACE_UNREAD_NOTIFICATION_COUNT" : null,
|
||||
workspaceSlug ? () => getUnreadNotificationsCount(workspaceSlug.toString()) : null
|
||||
);
|
||||
|
||||
// Calculate notification count
|
||||
const isMentionsEnabled = unreadNotificationsCount.mention_unread_notifications_count > 0;
|
||||
const totalNotifications = isMentionsEnabled
|
||||
? unreadNotificationsCount.mention_unread_notifications_count
|
||||
: unreadNotificationsCount.total_unread_notifications_count;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn("flex items-center min-h-10 w-full px-3.5 bg-canvas z-[27] transition-all duration-300", {
|
||||
@@ -54,23 +35,7 @@ export const TopNavigationRoot = observer(function TopNavigationRoot() {
|
||||
</div>
|
||||
{/* Additional Actions */}
|
||||
<div className="shrink-0 flex-1 flex gap-1 items-center justify-end">
|
||||
<Tooltip tooltipContent="Inbox" position="bottom">
|
||||
<AppSidebarItem
|
||||
variant="link"
|
||||
item={{
|
||||
href: `/${workspaceSlug?.toString()}/notifications/`,
|
||||
icon: (
|
||||
<div className="relative">
|
||||
<InboxIcon className="size-5" />
|
||||
{totalNotifications > 0 && (
|
||||
<span className="absolute top-0 right-0 size-2 rounded-full bg-danger-primary" />
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
isActive: pathname?.includes("/notifications/"),
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<NotificationsPopoverRoot workspaceSlug={workspaceSlug?.toString()} />
|
||||
<HelpMenuRoot />
|
||||
<StarUsOnGitHubLink />
|
||||
<div className="flex items-center justify-center size-8 hover:bg-layer-1-hover rounded-md">
|
||||
|
||||
@@ -3,6 +3,7 @@ import { NotificationCardListRoot } from "./notification-card/root";
|
||||
export type TNotificationListRoot = {
|
||||
workspaceSlug: string;
|
||||
workspaceId: string;
|
||||
onNotificationClick?: () => void;
|
||||
};
|
||||
|
||||
export function NotificationListRoot(props: TNotificationListRoot) {
|
||||
|
||||
@@ -11,10 +11,11 @@ import { useWorkspaceNotifications } from "@/hooks/store/notifications";
|
||||
type TNotificationCardListRoot = {
|
||||
workspaceSlug: string;
|
||||
workspaceId: string;
|
||||
onNotificationClick?: () => void;
|
||||
};
|
||||
|
||||
export const NotificationCardListRoot = observer(function NotificationCardListRoot(props: TNotificationCardListRoot) {
|
||||
const { workspaceSlug, workspaceId } = props;
|
||||
const { workspaceSlug, workspaceId, onNotificationClick } = props;
|
||||
// hooks
|
||||
const { loader, paginationInfo, getNotifications, notificationIdsByWorkspaceId } = useWorkspaceNotifications();
|
||||
const notificationIds = notificationIdsByWorkspaceId(workspaceId);
|
||||
@@ -32,7 +33,12 @@ export const NotificationCardListRoot = observer(function NotificationCardListRo
|
||||
return (
|
||||
<div>
|
||||
{notificationIds.map((notificationId: string) => (
|
||||
<NotificationItem key={notificationId} workspaceSlug={workspaceSlug} notificationId={notificationId} />
|
||||
<NotificationItem
|
||||
key={notificationId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
notificationId={notificationId}
|
||||
onNotificationClick={onNotificationClick}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* fetch next page notifications */}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023-present Plane Software, Inc.
|
||||
* SPDX-License-Identifier: LicenseRef-Plane-Commercial
|
||||
*
|
||||
* Licensed under the Plane Commercial License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* https://plane.so/legals/eula
|
||||
*
|
||||
* DO NOT remove or modify this notice.
|
||||
* NOTICE: Proprietary and confidential. Unauthorized use or distribution is prohibited.
|
||||
*/
|
||||
|
||||
import { useWorkspaceNotifications } from "@/hooks/store/notifications";
|
||||
|
||||
import { InboxIcon } from "lucide-react";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
|
||||
import { AppSidebarItem } from "@/components/sidebar/sidebar-item";
|
||||
import { NotificationsSidebarRoot } from "@/components/workspace-notifications/sidebar";
|
||||
import { Popover } from "@plane/propel/popover";
|
||||
|
||||
type NotificationsPopoverRootProps = {
|
||||
workspaceSlug: string;
|
||||
};
|
||||
|
||||
export function NotificationsPopoverRoot({ workspaceSlug }: NotificationsPopoverRootProps) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const { unreadNotificationsCount, viewMode } = useWorkspaceNotifications();
|
||||
|
||||
const isMentionsEnabled = unreadNotificationsCount.mention_unread_notifications_count > 0;
|
||||
const totalNotifications = isMentionsEnabled
|
||||
? unreadNotificationsCount.mention_unread_notifications_count
|
||||
: unreadNotificationsCount.total_unread_notifications_count;
|
||||
const isNotificationsPath = pathname.includes(`/${workspaceSlug}/notifications/`);
|
||||
const shouldPopoverBeOpen = viewMode === "compact" && !isNotificationsPath && isOpen;
|
||||
|
||||
const handleSidebarClick = () => {
|
||||
if (viewMode === "full") {
|
||||
setIsOpen(false);
|
||||
router.push(`/${workspaceSlug}/notifications/`);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePopoverChange = (open: boolean) => {
|
||||
if (!isNotificationsPath) {
|
||||
setIsOpen(open);
|
||||
} else {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover open={shouldPopoverBeOpen} onOpenChange={handlePopoverChange}>
|
||||
<Popover.Button>
|
||||
<AppSidebarItem
|
||||
variant={"button"}
|
||||
item={{
|
||||
icon: (
|
||||
<div className="relative">
|
||||
<InboxIcon className="size-5" />
|
||||
{totalNotifications > 0 && (
|
||||
<span className="absolute top-0 right-0 size-2 rounded-full bg-danger-primary" />
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
isActive: isOpen,
|
||||
onClick: handleSidebarClick,
|
||||
}}
|
||||
/>
|
||||
</Popover.Button>
|
||||
<Popover.Panel side="bottom" align="start" positionerClassName={"z-30"} className={"h-[477px] w-[530px]"}>
|
||||
<NotificationsSidebarRoot
|
||||
viewMode="compact"
|
||||
onFullViewMode={() => setIsOpen(false)}
|
||||
onNotificationClick={() => setIsOpen(false)}
|
||||
onModeChange={(mode) => {
|
||||
if (mode === "full" && isOpen) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Popover.Panel>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
@@ -27,7 +27,7 @@ export const NotificationFilter = observer(function NotificationFilter() {
|
||||
data={translatedFilterTypeOptions}
|
||||
button={
|
||||
<Tooltip tooltipContent={t("notification.options.filters")} isMobile={isMobile} position="bottom">
|
||||
<IconButton size="base" variant="ghost" icon={ListFilter} />
|
||||
<IconButton size="base" variant="secondary" icon={ListFilter} />
|
||||
</Tooltip>
|
||||
}
|
||||
keyExtractor={(item: { label: string; value: ENotificationFilterType }) => item.value}
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ export const NotificationHeaderMenuOption = observer(function NotificationHeader
|
||||
return (
|
||||
<PopoverMenu
|
||||
data={popoverMenuOptions}
|
||||
button={<IconButton size="base" variant="ghost" icon={MoreVertical} />}
|
||||
button={<IconButton size="base" variant="secondary" icon={MoreVertical} />}
|
||||
keyExtractor={(item: TPopoverMenuOptions) => item.key}
|
||||
panelClassName="p-0 py-2 rounded-md border border-subtle bg-surface-1 space-y-1"
|
||||
render={(item: TPopoverMenuOptions) => <NotificationMenuOptionItem {...item} />}
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
import { observer } from "mobx-react";
|
||||
import { CheckCheck, RefreshCw } from "lucide-react";
|
||||
// plane imports
|
||||
import { ENotificationLoader, ENotificationQueryParamType } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
import { Spinner } from "@plane/ui";
|
||||
// hooks
|
||||
import { useWorkspaceNotifications } from "@/hooks/store/notifications";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// local imports
|
||||
import { NotificationFilter } from "../../filters/menu";
|
||||
import { NotificationHeaderMenuOption } from "./menu-option";
|
||||
import { IconButton } from "@plane/propel/icon-button";
|
||||
|
||||
type TNotificationSidebarHeaderOptions = {
|
||||
workspaceSlug: string;
|
||||
@@ -20,56 +10,8 @@ type TNotificationSidebarHeaderOptions = {
|
||||
export const NotificationSidebarHeaderOptions = observer(function NotificationSidebarHeaderOptions(
|
||||
props: TNotificationSidebarHeaderOptions
|
||||
) {
|
||||
const { workspaceSlug } = props;
|
||||
// hooks
|
||||
const { isMobile } = usePlatformOS();
|
||||
const { loader, getNotifications, markAllNotificationsAsRead } = useWorkspaceNotifications();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const refreshNotifications = async () => {
|
||||
if (loader) return;
|
||||
try {
|
||||
await getNotifications(workspaceSlug, ENotificationLoader.MUTATION_LOADER, ENotificationQueryParamType.CURRENT);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMarkAllNotificationsAsRead = async () => {
|
||||
// NOTE: We are using loader to prevent continues request when we are making all the notification to read
|
||||
if (loader) return;
|
||||
try {
|
||||
await markAllNotificationsAsRead(workspaceSlug);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative flex justify-center items-center gap-2 text-body-xs-medium">
|
||||
{/* mark all notifications as read*/}
|
||||
<Tooltip tooltipContent={t("notification.options.mark_all_as_read")} isMobile={isMobile} position="bottom">
|
||||
<IconButton
|
||||
size="base"
|
||||
variant="ghost"
|
||||
icon={loader === ENotificationLoader.MARK_ALL_AS_READY ? Spinner : CheckCheck}
|
||||
onClick={() => {
|
||||
handleMarkAllNotificationsAsRead();
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
{/* refetch current notifications */}
|
||||
<Tooltip tooltipContent={t("notification.options.refresh")} isMobile={isMobile} position="bottom">
|
||||
<IconButton
|
||||
size="base"
|
||||
variant="ghost"
|
||||
icon={RefreshCw}
|
||||
className={loader === ENotificationLoader.MUTATION_LOADER ? "animate-spin" : ""}
|
||||
onClick={refreshNotifications}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
{/* notification filters */}
|
||||
<NotificationFilter />
|
||||
|
||||
|
||||
@@ -16,10 +16,11 @@ import { NotificationOption } from "./options";
|
||||
type TNotificationItem = {
|
||||
workspaceSlug: string;
|
||||
notificationId: string;
|
||||
onNotificationClick?: () => void;
|
||||
};
|
||||
|
||||
export const NotificationItem = observer(function NotificationItem(props: TNotificationItem) {
|
||||
const { workspaceSlug, notificationId } = props;
|
||||
const { workspaceSlug, notificationId, onNotificationClick } = props;
|
||||
// hooks
|
||||
const { currentSelectedNotificationId, setCurrentSelectedNotificationId } = useWorkspaceNotifications();
|
||||
const { asJson: notification, markNotificationAsRead } = useNotification(notificationId);
|
||||
@@ -39,6 +40,8 @@ export const NotificationItem = observer(function NotificationItem(props: TNotif
|
||||
|
||||
const handleNotificationIssuePeekOverview = async () => {
|
||||
if (workspaceSlug && projectId && issueId && !isSnoozeStateModalOpen && !customSnoozeModal) {
|
||||
onNotificationClick?.();
|
||||
|
||||
setPeekIssue(undefined);
|
||||
setCurrentSelectedNotificationId(notificationId);
|
||||
|
||||
|
||||
@@ -1,115 +1,198 @@
|
||||
import { useCallback } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
// plane imports
|
||||
import type { TNotificationTab } from "@plane/constants";
|
||||
import { NOTIFICATION_TABS } from "@plane/constants";
|
||||
import { ENotificationLoader, ENotificationQueryParamType, NOTIFICATION_TABS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { Header, Row, ERowVariant, EHeaderVariant, ContentWrapper } from "@plane/ui";
|
||||
import { ContentWrapper, ERowVariant, Spinner, Tooltip } from "@plane/ui";
|
||||
import { cn, getNumberCount } from "@plane/utils";
|
||||
import type { TNotificationsViewMode } from "@plane/types";
|
||||
// components
|
||||
import { CountChip } from "@/components/common/count-chip";
|
||||
// hooks
|
||||
import { useWorkspaceNotifications } from "@/hooks/store/notifications";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
// plane web components
|
||||
import { NotificationListRoot } from "@/plane-web/components/workspace-notifications/list-root";
|
||||
// local imports
|
||||
|
||||
import { getIconButtonStyling, IconButton } from "@plane/propel/icon-button";
|
||||
import { Tabs } from "@plane/propel/tabs";
|
||||
import { CheckCheck, MoveDiagonal, RefreshCw } from "lucide-react";
|
||||
import { Link } from "react-router";
|
||||
import { NotificationEmptyState } from "./empty-state";
|
||||
import { AppliedFilters } from "./filters/applied-filter";
|
||||
import { NotificationSidebarHeader } from "./header";
|
||||
import { NotificationSidebarHeaderOptions } from "./header/options";
|
||||
import { NotificationsLoader } from "./loader";
|
||||
import { ViewModeSelector } from "./view-mode-selector";
|
||||
import { usePlatformOS } from "@plane/hooks";
|
||||
|
||||
export const NotificationsSidebarRoot = observer(function NotificationsSidebarRoot() {
|
||||
type NotificationsSidebarRootProps = {
|
||||
viewMode?: TNotificationsViewMode;
|
||||
onFullViewMode?: () => void;
|
||||
onNotificationClick?: () => void;
|
||||
onModeChange?: (mode: TNotificationsViewMode) => void;
|
||||
};
|
||||
|
||||
const getSidebarWidthClass = (viewMode: TNotificationsViewMode, hasSelection: boolean) => {
|
||||
if (viewMode === "compact") {
|
||||
return "w-full md:w-full rounded-xl border border-subtle shadow-raised-200";
|
||||
}
|
||||
|
||||
return hasSelection ? "w-0 md:w-3/12" : "w-full md:w-3/12";
|
||||
};
|
||||
|
||||
export const NotificationsSidebarRoot = observer(function NotificationsSidebarRoot({
|
||||
viewMode = "full",
|
||||
onFullViewMode,
|
||||
onNotificationClick,
|
||||
onModeChange,
|
||||
}: NotificationsSidebarRootProps) {
|
||||
const { workspaceSlug } = useParams();
|
||||
// hooks
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { isMobile } = usePlatformOS();
|
||||
|
||||
const { getWorkspaceBySlug } = useWorkspace();
|
||||
const notifications = useWorkspaceNotifications();
|
||||
const router = useRouter();
|
||||
if (!workspaceSlug) return null;
|
||||
|
||||
const workspace = getWorkspaceBySlug(workspaceSlug.toString());
|
||||
if (!workspace) return null;
|
||||
|
||||
const {
|
||||
currentSelectedNotificationId,
|
||||
unreadNotificationsCount,
|
||||
loader,
|
||||
notificationIdsByWorkspaceId,
|
||||
currentNotificationTab,
|
||||
setCurrentNotificationTab,
|
||||
} = useWorkspaceNotifications();
|
||||
setViewMode,
|
||||
viewMode: currentViewMode,
|
||||
loader,
|
||||
getNotifications,
|
||||
markAllNotificationsAsRead,
|
||||
} = notifications;
|
||||
|
||||
const { t } = useTranslation();
|
||||
// derived values
|
||||
const workspace = workspaceSlug ? getWorkspaceBySlug(workspaceSlug.toString()) : undefined;
|
||||
const notificationIds = workspace ? notificationIdsByWorkspaceId(workspace.id) : undefined;
|
||||
const notificationIds = notificationIdsByWorkspaceId(workspace.id);
|
||||
const sidebarWidthClass = getSidebarWidthClass(viewMode, Boolean(currentSelectedNotificationId));
|
||||
|
||||
const handleTabClick = useCallback(
|
||||
(tabValue: TNotificationTab) => {
|
||||
if (currentNotificationTab !== tabValue) {
|
||||
setCurrentNotificationTab(tabValue);
|
||||
}
|
||||
},
|
||||
[currentNotificationTab, setCurrentNotificationTab]
|
||||
);
|
||||
const refreshNotifications = async () => {
|
||||
if (loader) return;
|
||||
try {
|
||||
await getNotifications(workspaceSlug, ENotificationLoader.MUTATION_LOADER, ENotificationQueryParamType.CURRENT);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
if (!workspaceSlug || !workspace) return <></>;
|
||||
const handleMarkAllNotificationsAsRead = async () => {
|
||||
// NOTE: We are using loader to prevent continues request when we are making all the notification to read
|
||||
if (loader) return;
|
||||
try {
|
||||
await markAllNotificationsAsRead(workspaceSlug);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"relative border-0 md:border-r border-subtle z-[10] flex-shrink-0 bg-surface-1 h-full transition-all max-md:overflow-hidden",
|
||||
currentSelectedNotificationId ? "w-0 md:w-3/12" : "w-full md:w-3/12"
|
||||
"relative flex-shrink-0 bg-surface-1 h-full transition-all md:border-r border-subtle max-md:overflow-hidden",
|
||||
sidebarWidthClass
|
||||
)}
|
||||
>
|
||||
<div className="relative w-full h-full flex flex-col">
|
||||
<Row className="h-header border-b border-subtle flex flex-shrink-0">
|
||||
<NotificationSidebarHeader workspaceSlug={workspaceSlug.toString()} />
|
||||
</Row>
|
||||
<div className="flex h-full flex-col">
|
||||
<div className="px-3 py-2 border-b border-subtle flex items-center justify-between mb-2">
|
||||
<h4 className="text-18 font-medium">{t("notification.label")}</h4>
|
||||
<div className="flex items-center gap-1">
|
||||
<Tooltip tooltipContent={t("notification.options.mark_all_as_read")} isMobile={isMobile} position="bottom">
|
||||
<IconButton
|
||||
size="base"
|
||||
variant="ghost"
|
||||
icon={loader === ENotificationLoader.MARK_ALL_AS_READY ? Spinner : CheckCheck}
|
||||
onClick={() => {
|
||||
handleMarkAllNotificationsAsRead();
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<Header variant={EHeaderVariant.SECONDARY} className="justify-start">
|
||||
{NOTIFICATION_TABS.map((tab) => (
|
||||
<div
|
||||
key={tab.value}
|
||||
className="h-full px-3 relative cursor-pointer"
|
||||
onClick={() => handleTabClick(tab.value)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"relative h-full flex justify-center items-center gap-1 text-body-xs-medium transition-all",
|
||||
{
|
||||
"text-accent-primary": currentNotificationTab === tab.value,
|
||||
"text-primary hover:text-secondary": currentNotificationTab !== tab.value,
|
||||
}
|
||||
)}
|
||||
{/* refetch current notifications */}
|
||||
<Tooltip tooltipContent={t("notification.options.refresh")} isMobile={isMobile} position="bottom">
|
||||
<IconButton
|
||||
size="base"
|
||||
variant="ghost"
|
||||
icon={RefreshCw}
|
||||
className={loader === ENotificationLoader.MUTATION_LOADER ? "animate-spin" : ""}
|
||||
onClick={refreshNotifications}
|
||||
/>
|
||||
</Tooltip>
|
||||
{viewMode === "compact" && (
|
||||
<Link
|
||||
to={`/${workspaceSlug}/notifications/`}
|
||||
className={getIconButtonStyling("ghost", "base")}
|
||||
onClick={onFullViewMode}
|
||||
>
|
||||
<div className="font-medium">{t(tab.i18n_label)}</div>
|
||||
{tab.count(unreadNotificationsCount) > 0 && (
|
||||
<CountChip count={getNumberCount(tab.count(unreadNotificationsCount))} />
|
||||
)}
|
||||
</div>
|
||||
{currentNotificationTab === tab.value && (
|
||||
<div className="border absolute bottom-0 right-0 left-0 rounded-t-md border-accent-strong" />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</Header>
|
||||
<MoveDiagonal size={16} />
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{/* applied filters */}
|
||||
<AppliedFilters workspaceSlug={workspaceSlug.toString()} />
|
||||
|
||||
{/* rendering notifications */}
|
||||
{loader === "init-loader" ? (
|
||||
<div className="relative w-full h-full overflow-hidden">
|
||||
<NotificationsLoader />
|
||||
<ViewModeSelector
|
||||
value={currentViewMode}
|
||||
onChange={(mode) => {
|
||||
setViewMode(mode);
|
||||
if (mode === "full") {
|
||||
router.push(`/${workspaceSlug}/notifications/`);
|
||||
}
|
||||
onModeChange?.(mode);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{notificationIds && notificationIds.length > 0 ? (
|
||||
</div>
|
||||
|
||||
<Tabs
|
||||
defaultValue={currentNotificationTab}
|
||||
onValueChange={setCurrentNotificationTab}
|
||||
className={"overflow-y-hidden flex-1"}
|
||||
>
|
||||
<div className="flex items-center justify-between mx-3">
|
||||
<Tabs.List className="w-fit">
|
||||
{NOTIFICATION_TABS.map((tab) => (
|
||||
<Tabs.Trigger key={tab.value} value={tab.value} size="sm">
|
||||
<div className="flex items-center gap-1.5 px-1">
|
||||
{t(tab.i18n_label)}
|
||||
{tab.count(unreadNotificationsCount) > 0 && (
|
||||
<span>{getNumberCount(tab.count(unreadNotificationsCount))}</span>
|
||||
)}
|
||||
</div>
|
||||
</Tabs.Trigger>
|
||||
))}
|
||||
</Tabs.List>
|
||||
|
||||
<NotificationSidebarHeaderOptions workspaceSlug={workspaceSlug.toString()} />
|
||||
</div>
|
||||
|
||||
<Tabs.Content value={currentNotificationTab} className="py-2 overflow-y-auto flex-1">
|
||||
{loader === "init-loader" ? (
|
||||
<div className="relative w-full h-full overflow-hidden">
|
||||
<NotificationsLoader />
|
||||
</div>
|
||||
) : notificationIds && notificationIds.length > 0 ? (
|
||||
<ContentWrapper variant={ERowVariant.HUGGING}>
|
||||
<NotificationListRoot workspaceSlug={workspaceSlug.toString()} workspaceId={workspace?.id} />
|
||||
<NotificationListRoot
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
workspaceId={workspace.id}
|
||||
onNotificationClick={onNotificationClick}
|
||||
/>
|
||||
</ContentWrapper>
|
||||
) : (
|
||||
<div className="relative w-full h-full flex justify-center items-center">
|
||||
<div className="relative w-full h-full flex items-center justify-center">
|
||||
<NotificationEmptyState currentNotificationTab={currentNotificationTab} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Tabs.Content>
|
||||
</Tabs>
|
||||
|
||||
<AppliedFilters workspaceSlug={workspaceSlug.toString()} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { CenterPanelIcon, FullScreenPanelIcon } from "@plane/propel/icons";
|
||||
import type { TNotificationsViewMode } from "@/store/notifications/workspace-notifications.store";
|
||||
import { Menu } from "@plane/propel/menu";
|
||||
import { CheckIcon } from "lucide-react";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
|
||||
const VIEW_MODES = [
|
||||
{ key: "compact", icon: CenterPanelIcon, i18n_label: "notifications.compact" },
|
||||
{ key: "full", icon: FullScreenPanelIcon, i18n_label: "notifications.full" },
|
||||
] as const;
|
||||
|
||||
type ViewModeSelectorProps = {
|
||||
value: TNotificationsViewMode;
|
||||
onChange: (mode: TNotificationsViewMode) => void;
|
||||
};
|
||||
|
||||
export function ViewModeSelector({ value, onChange }: ViewModeSelectorProps) {
|
||||
const CurrentIcon = VIEW_MODES.find((m) => m.key === value)?.icon;
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Menu
|
||||
ariaLabel={t("notifications.select_default_view")}
|
||||
customButton={
|
||||
<span className="flex items-center justify-center">
|
||||
{CurrentIcon && <CurrentIcon className="h-4 w-4 text-tertiary hover:text-secondary" />}
|
||||
</span>
|
||||
}
|
||||
optionsClassName="p-1"
|
||||
>
|
||||
<div className="text-tertiary text-12 px-2 py-1">{t("notifications.select_default_view")}</div>
|
||||
{VIEW_MODES.map(({ key, icon: Icon, i18n_label }) => {
|
||||
const selected = key === value;
|
||||
return (
|
||||
<Menu.MenuItem key={key} onClick={() => onChange(key)}>
|
||||
<div className="flex items-center justify-between w-full px-1">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Icon className="h-4 w-4" />
|
||||
{t(i18n_label)}
|
||||
</div>
|
||||
{selected && <CheckIcon className="h-4 w-4" />}
|
||||
</div>
|
||||
</Menu.MenuItem>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { orderBy, isEmpty, update, set } from "lodash-es";
|
||||
import { action, makeObservable, observable, runInAction } from "mobx";
|
||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||
import { computedFn } from "mobx-utils";
|
||||
// plane imports
|
||||
import type { TNotificationTab } from "@plane/constants";
|
||||
@@ -10,6 +10,7 @@ import type {
|
||||
TNotificationLite,
|
||||
TNotificationPaginatedInfo,
|
||||
TNotificationPaginatedInfoQueryParams,
|
||||
TNotificationsViewMode,
|
||||
TUnreadNotificationsCount,
|
||||
} from "@plane/types";
|
||||
// helpers
|
||||
@@ -24,6 +25,8 @@ import type { CoreRootStore } from "@/store/root.store";
|
||||
type TNotificationLoader = ENotificationLoader | undefined;
|
||||
type TNotificationQueryParamType = ENotificationQueryParamType;
|
||||
|
||||
export type TGroupedNotifications = Record<string, TNotification[]>;
|
||||
|
||||
export interface IWorkspaceNotificationStore {
|
||||
// observables
|
||||
loader: TNotificationLoader;
|
||||
@@ -34,6 +37,7 @@ export interface IWorkspaceNotificationStore {
|
||||
paginationInfo: Omit<TNotificationPaginatedInfo, "results"> | undefined;
|
||||
filters: TNotificationFilter;
|
||||
// computed
|
||||
viewMode: TNotificationsViewMode;
|
||||
// computed functions
|
||||
notificationIdsByWorkspaceId: (workspaceId: string) => string[] | undefined;
|
||||
notificationLiteByNotificationId: (notificationId: string | undefined) => TNotificationLite;
|
||||
@@ -52,6 +56,7 @@ export interface IWorkspaceNotificationStore {
|
||||
queryCursorType?: TNotificationQueryParamType
|
||||
) => Promise<TNotificationPaginatedInfo | undefined>;
|
||||
markAllNotificationsAsRead: (workspaceId: string) => Promise<void>;
|
||||
setViewMode: (viewMode: TNotificationsViewMode) => void;
|
||||
}
|
||||
|
||||
export class WorkspaceNotificationStore implements IWorkspaceNotificationStore {
|
||||
@@ -89,6 +94,7 @@ export class WorkspaceNotificationStore implements IWorkspaceNotificationStore {
|
||||
paginationInfo: observable,
|
||||
filters: observable,
|
||||
// computed
|
||||
viewMode: computed,
|
||||
// helper actions
|
||||
setCurrentNotificationTab: action,
|
||||
setCurrentSelectedNotificationId: action,
|
||||
@@ -100,10 +106,21 @@ export class WorkspaceNotificationStore implements IWorkspaceNotificationStore {
|
||||
getUnreadNotificationsCount: action,
|
||||
getNotifications: action,
|
||||
markAllNotificationsAsRead: action,
|
||||
setViewMode: action,
|
||||
});
|
||||
}
|
||||
|
||||
setViewMode = (viewMode: TNotificationsViewMode): void => {
|
||||
if (this.store.user.userProfile.data) {
|
||||
this.store.user.userProfile.data.notification_view_mode = viewMode;
|
||||
}
|
||||
this.store.user.userProfile.updateUserProfile({ notification_view_mode: viewMode });
|
||||
};
|
||||
|
||||
// computed
|
||||
get viewMode(): TNotificationsViewMode {
|
||||
return this.store.user.userProfile.data?.notification_view_mode || "full";
|
||||
}
|
||||
|
||||
// computed functions
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// plane imports
|
||||
import type { ADDITIONAL_EXTENSIONS } from "@plane/utils";
|
||||
// helpers
|
||||
import type { TAssetMetaDataRecord } from "@/helpers/assets";
|
||||
// local imports
|
||||
import type { ADDITIONAL_EXTENSIONS } from "./extensions";
|
||||
|
||||
export const ADDITIONAL_ASSETS_META_DATA_RECORD: Partial<Record<ADDITIONAL_EXTENSIONS, TAssetMetaDataRecord>> = {};
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
export enum ADDITIONAL_EXTENSIONS {}
|
||||
|
||||
export const ADDITIONAL_BLOCK_NODE_TYPES = [];
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useCallback, useEffect, useRef } from "react";
|
||||
import { cn } from "@plane/utils";
|
||||
// constants
|
||||
import { DEFAULT_DISPLAY_CONFIG } from "@/constants/config";
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// components
|
||||
import type { TCollabValue } from "@/contexts";
|
||||
import { LinkContainer } from "@/plane-editor/components/link-container";
|
||||
|
||||
@@ -3,7 +3,7 @@ import { TableMap } from "@tiptap/pm/tables";
|
||||
import type { Editor } from "@tiptap/react";
|
||||
import { MoveHorizontal } from "lucide-react";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// types
|
||||
import type { BlockMenuOption } from "./block-menu";
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import { CopyIcon, TrashIcon } from "@plane/propel/icons";
|
||||
import type { ISvgIcons } from "@plane/propel/icons";
|
||||
import { cn } from "@plane/utils";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// types
|
||||
import type { IEditorProps } from "@/types";
|
||||
// components
|
||||
|
||||
@@ -6,7 +6,7 @@ import { LinkIcon, TrashIcon, CheckIcon } from "@plane/propel/icons";
|
||||
// plane imports
|
||||
import { cn } from "@plane/utils";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// helpers
|
||||
import { isValidHttpUrl } from "@/helpers/common";
|
||||
import { setLinkEditor, unsetLinkEditor } from "@/helpers/editor-commands";
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
} from "@/components/menus";
|
||||
// constants
|
||||
import { COLORS_LIST } from "@/constants/common";
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// extensions
|
||||
import { isCellSelection } from "@/extensions/table/table/utilities/helpers";
|
||||
// types
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import { LinkIcon } from "@plane/propel/icons";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// helpers
|
||||
import {
|
||||
insertHorizontalRule,
|
||||
|
||||
@@ -1,5 +1,49 @@
|
||||
// plane imports
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
export enum CORE_EXTENSIONS {
|
||||
BLOCKQUOTE = "blockquote",
|
||||
BOLD = "bold",
|
||||
BULLET_LIST = "bulletList",
|
||||
CALLOUT = "calloutComponent",
|
||||
CHARACTER_COUNT = "characterCount",
|
||||
CODE_BLOCK = "codeBlock",
|
||||
CODE_INLINE = "code",
|
||||
CUSTOM_COLOR = "customColor",
|
||||
CUSTOM_IMAGE = "imageComponent",
|
||||
CUSTOM_LINK = "link",
|
||||
DOCUMENT = "doc",
|
||||
DROP_CURSOR = "dropCursor",
|
||||
ENTER_KEY = "enterKey",
|
||||
GAP_CURSOR = "gapCursor",
|
||||
HARD_BREAK = "hardBreak",
|
||||
HEADING = "heading",
|
||||
HEADINGS_LIST = "headingsList",
|
||||
HISTORY = "history",
|
||||
HORIZONTAL_RULE = "horizontalRule",
|
||||
IMAGE = "image",
|
||||
ITALIC = "italic",
|
||||
LIST_ITEM = "listItem",
|
||||
MARKDOWN_CLIPBOARD = "markdownClipboard",
|
||||
MENTION = "mention",
|
||||
ORDERED_LIST = "orderedList",
|
||||
PARAGRAPH = "paragraph",
|
||||
PLACEHOLDER = "placeholder",
|
||||
SIDE_MENU = "editorSideMenu",
|
||||
SLASH_COMMANDS = "slash-command",
|
||||
STRIKETHROUGH = "strike",
|
||||
TABLE = "table",
|
||||
TABLE_CELL = "tableCell",
|
||||
TABLE_HEADER = "tableHeader",
|
||||
TABLE_ROW = "tableRow",
|
||||
TASK_ITEM = "taskItem",
|
||||
TASK_LIST = "taskList",
|
||||
TEXT_ALIGN = "textAlign",
|
||||
TEXT_STYLE = "textStyle",
|
||||
TYPOGRAPHY = "typography",
|
||||
UNDERLINE = "underline",
|
||||
UTILITY = "utility",
|
||||
WORK_ITEM_EMBED = "issue-embed-component",
|
||||
EMOJI = "emoji",
|
||||
UNIQUE_ID = "uniqueID",
|
||||
}
|
||||
|
||||
export const BLOCK_NODE_TYPES = [
|
||||
// Basic block nodes
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Node, mergeAttributes } from "@tiptap/core";
|
||||
import type { MarkdownSerializerState } from "@tiptap/pm/markdown";
|
||||
import type { Node as ProseMirrorNode } from "@tiptap/pm/model";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// types
|
||||
import { ECalloutAttributeNames } from "./types";
|
||||
import type { CustomCalloutExtensionType, TCalloutBlockAttributes } from "./types";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { findParentNodeClosestToPos, ReactNodeViewRenderer } from "@tiptap/react";
|
||||
import type { Predicate } from "@tiptap/react";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// helpers
|
||||
import { insertEmptyParagraphAtNodeBoundaries } from "@/helpers/insert-empty-paragraph-at-node-boundary";
|
||||
// local imports
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Mark, markInputRule, markPasteRule, mergeAttributes } from "@tiptap/core";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
|
||||
type InlineCodeOptions = {
|
||||
HTMLAttributes: Record<string, unknown>;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mergeAttributes, Node, textblockTypeInputRule } from "@tiptap/core";
|
||||
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
|
||||
export type CodeBlockOptions = {
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Mark, mergeAttributes } from "@tiptap/core";
|
||||
// constants
|
||||
import { COLORS_LIST } from "@/constants/common";
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
|
||||
declare module "@tiptap/core" {
|
||||
interface Commands<ReturnType> {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useCallback, useEffect, useMemo, useRef } from "react";
|
||||
import { cn } from "@plane/utils";
|
||||
// constants
|
||||
import { ACCEPTED_IMAGE_MIME_TYPES } from "@/constants/config";
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// helpers
|
||||
import type { EFileError } from "@/helpers/file";
|
||||
// hooks
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mergeAttributes } from "@tiptap/core";
|
||||
import { Image as BaseImageExtension } from "@tiptap/extension-image";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// local imports
|
||||
import { ECustomImageAttributeNames } from "./types";
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Mark, markPasteRule, mergeAttributes } from "@tiptap/core";
|
||||
import type { Plugin } from "@tiptap/pm/state";
|
||||
import { find, registerCustomProtocol, reset } from "linkifyjs";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// helpers
|
||||
import { isValidHttpUrl } from "@/helpers/common";
|
||||
// local imports
|
||||
|
||||
@@ -3,7 +3,7 @@ import { getNodeType, getNodeAtPosition, isAtEndOfNode, isAtStartOfNode, isNodeA
|
||||
import type { Node, NodeType } from "@tiptap/pm/model";
|
||||
import type { EditorState } from "@tiptap/pm/state";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
|
||||
const findListItemPos = (typeOrName: string | NodeType, state: EditorState) => {
|
||||
const { $from } = state.selection;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Extension } from "@tiptap/core";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// extensions
|
||||
import { handleBackspace, handleDelete } from "@/extensions/custom-list-keymap/list-helpers";
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { EmojiOptions, EmojiStorage } from "@tiptap/extension-emoji";
|
||||
import { ReactRenderer } from "@tiptap/react";
|
||||
import type { Editor } from "@tiptap/react";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// helpers
|
||||
import { updateFloatingUIFloaterPosition } from "@/helpers/floating-ui";
|
||||
import type { CommandListInstance } from "@/helpers/tippy";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Extension } from "@tiptap/core";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
|
||||
export const EnterKeyExtension = (onEnterKeyPress?: () => void) =>
|
||||
Extension.create({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Extension } from "@tiptap/core";
|
||||
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// types
|
||||
import type { IMarking } from "@/types";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { isNodeSelection, mergeAttributes, Node, nodeInputRule } from "@tiptap/core";
|
||||
import { NodeSelection, TextSelection } from "@tiptap/pm/state";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
|
||||
type HorizontalRuleOptions = {
|
||||
HTMLAttributes: Record<string, unknown>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ReactNodeViewRenderer } from "@tiptap/react";
|
||||
// plane imports
|
||||
import type { CORE_EXTENSIONS } from "@plane/utils";
|
||||
// constants
|
||||
import type { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// helpers
|
||||
import { insertEmptyParagraphAtNodeBoundaries } from "@/helpers/insert-empty-paragraph-at-node-boundary";
|
||||
// types
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { Transaction } from "@tiptap/pm/state";
|
||||
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
||||
import { canJoin } from "@tiptap/pm/transform";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
|
||||
declare module "@tiptap/core" {
|
||||
interface Commands<ReturnType> {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ReactRenderer } from "@tiptap/react";
|
||||
import type { Editor } from "@tiptap/react";
|
||||
import type { SuggestionOptions } from "@tiptap/suggestion";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// helpers
|
||||
import { updateFloatingUIFloaterPosition } from "@/helpers/floating-ui";
|
||||
import type { CommandListInstance } from "@/helpers/tippy";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Placeholder } from "@tiptap/extension-placeholder";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// types
|
||||
import type { IEditorProps } from "@/types";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Blockquote from "@tiptap/extension-blockquote";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
|
||||
export const CustomQuoteExtension = Blockquote.extend({
|
||||
addKeyboardShortcuts() {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Extension } from "@tiptap/core";
|
||||
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
||||
import type { EditorView } from "@tiptap/pm/view";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// plugins
|
||||
import { AIHandlePlugin } from "@/plugins/ai-handle";
|
||||
import { DragHandlePlugin, nodeDOMAtCoords } from "@/plugins/drag-handle";
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ReactRenderer } from "@tiptap/react";
|
||||
import Suggestion from "@tiptap/suggestion";
|
||||
import type { SuggestionOptions } from "@tiptap/suggestion";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// helpers
|
||||
import { updateFloatingUIFloaterPosition } from "@/helpers/floating-ui";
|
||||
import type { CommandListInstance } from "@/helpers/tippy";
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ChevronRightIcon } from "@plane/propel/icons";
|
||||
import { cn } from "@plane/utils";
|
||||
// constants
|
||||
import { COLORS_LIST } from "@/constants/common";
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
|
||||
// TODO: implement text color selector
|
||||
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
||||
// plane imports
|
||||
import { cn } from "@plane/utils";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// extensions
|
||||
import {
|
||||
findTable,
|
||||
|
||||
@@ -16,7 +16,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
||||
// plane imports
|
||||
import { cn } from "@plane/utils";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// extensions
|
||||
import {
|
||||
findTable,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Editor } from "@tiptap/core";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
import { CORE_EDITOR_META } from "@/constants/meta";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mergeAttributes, Node } from "@tiptap/core";
|
||||
import { TableMap } from "@tiptap/pm/tables";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// helpers
|
||||
import { findParentNodeOfType } from "@/helpers/common";
|
||||
// local imports
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { mergeAttributes, Node } from "@tiptap/core";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// local imports
|
||||
import { DEFAULT_COLUMN_WIDTH } from "./table";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { mergeAttributes, Node } from "@tiptap/core";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
|
||||
type TableRowOptions = {
|
||||
HTMLAttributes: Record<string, unknown>;
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
toggleHeaderCell,
|
||||
} from "@tiptap/pm/tables";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// local imports
|
||||
import { TableColumnDragHandlePlugin } from "../plugins/drag-handles/column/plugin";
|
||||
import { TableRowDragHandlePlugin } from "../plugins/drag-handles/row/plugin";
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { Node as ProseMirrorNode } from "@tiptap/pm/model";
|
||||
import type { CellSelection } from "@tiptap/pm/tables";
|
||||
import { TableMap } from "@tiptap/pm/tables";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// extensions
|
||||
import { isCellEmpty, isCellSelection } from "@/extensions/table/table/utilities/helpers";
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { EditorState, Selection, Transaction } from "@tiptap/pm/state";
|
||||
import { CellSelection, TableMap } from "@tiptap/pm/tables";
|
||||
import type { Rect } from "@tiptap/pm/tables";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
|
||||
/**
|
||||
* @description Check if the selection is a cell selection
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import type { KeyboardShortcutCommand } from "@tiptap/core";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// helpers
|
||||
import { findParentNodeOfType } from "@/helpers/common";
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import type { KeyboardShortcutCommand } from "@tiptap/core";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// helpers
|
||||
import { findParentNodeOfType } from "@/helpers/common";
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Extension } from "@tiptap/core";
|
||||
import type { NodeType, Node as ProseMirrorNode } from "@tiptap/pm/model";
|
||||
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
|
||||
function nodeEqualsType({ types, node }: { types: NodeType[]; node: ProseMirrorNode | null }) {
|
||||
// TODO: check this logic, might be wrong
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { InputRule } from "@tiptap/core";
|
||||
import { Extension } from "@tiptap/core";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
import type { TypographyOptions } from "./inputRules";
|
||||
import {
|
||||
emDash,
|
||||
|
||||
@@ -3,13 +3,12 @@ import { Extension } from "@tiptap/core";
|
||||
import type { Node as ProseMirrorNode } from "@tiptap/pm/model";
|
||||
import type { Transaction } from "@tiptap/pm/state";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
// plane imports
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
// constants
|
||||
import { BLOCK_NODE_TYPES } from "@/constants/extension";
|
||||
import { CORE_EXTENSIONS, BLOCK_NODE_TYPES } from "@/constants/extension";
|
||||
import { ADDITIONAL_BLOCK_NODE_TYPES } from "@/plane-editor/constants/extensions";
|
||||
import { createUniqueIDPlugin } from "./plugin";
|
||||
import { createIdsForView } from "./utils";
|
||||
// plane imports
|
||||
|
||||
const COMBINED_BLOCK_NODE_TYPES = [...BLOCK_NODE_TYPES, ...ADDITIONAL_BLOCK_NODE_TYPES];
|
||||
export type UniqueIDGenerationContext = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Extension } from "@tiptap/core";
|
||||
import codemark from "prosemirror-codemark";
|
||||
// helpers
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
import { restorePublicImages } from "@/helpers/image-helpers";
|
||||
// plugins
|
||||
import type { TAdditionalActiveDropbarExtensions } from "@/plane-editor/types/utils";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { mergeAttributes, Node } from "@tiptap/core";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
|
||||
export const WorkItemEmbedExtensionConfig = Node.create({
|
||||
name: CORE_EXTENSIONS.WORK_ITEM_EMBED,
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { EditorState, Selection } from "@tiptap/pm/state";
|
||||
// plane imports
|
||||
import { cn } from "@plane/utils";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
|
||||
type EditorClassNameArgs = {
|
||||
noBorder?: boolean;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Editor, Range } from "@tiptap/core";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// extensions
|
||||
import { replaceCodeWithText } from "@/extensions/code/utils/replace-code-block-with-text";
|
||||
import type { InsertImageComponentProps } from "@/extensions/custom-image/types";
|
||||
|
||||
@@ -7,7 +7,7 @@ import { convertHTMLToMarkdown } from "@plane/utils";
|
||||
// components
|
||||
import { getEditorMenuItems } from "@/components/menus";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
import { CORE_EDITOR_META } from "@/constants/meta";
|
||||
// types
|
||||
import type { EditorRefApi, IEditorProps, TEditorCommands } from "@/types";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Editor } from "@tiptap/core";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// types
|
||||
import type { TFileHandler } from "@/types";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { KeyboardShortcutCommand } from "@tiptap/core";
|
||||
import type { Node as ProseMirrorNode } from "@tiptap/pm/model";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
|
||||
type Direction = "up" | "down";
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Fragment, Slice } from "@tiptap/pm/model";
|
||||
import { NodeSelection } from "@tiptap/pm/state";
|
||||
import type { EditorView } from "@tiptap/pm/view";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@plane/utils";
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// extensions
|
||||
import type { SideMenuHandleOptions, SideMenuPluginProps } from "@/extensions";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// plane imports
|
||||
import type { CORE_EXTENSIONS } from "@plane/utils";
|
||||
// constants
|
||||
import type { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// plane editor imports
|
||||
import type { TAdditionalEditorAsset } from "@/plane-editor/types/asset";
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ export * from "@/constants/common";
|
||||
export * from "@/helpers/common";
|
||||
export * from "@/helpers/yjs-utils";
|
||||
|
||||
export { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
export { ADDITIONAL_EXTENSIONS } from "@/plane-editor/constants/extensions";
|
||||
|
||||
// types
|
||||
export * from "@/types";
|
||||
|
||||
|
||||
@@ -53,6 +53,13 @@ export interface IUserAccount {
|
||||
updated_at: Date;
|
||||
}
|
||||
|
||||
export const NOTIFICATION_VIEW_MODES = [
|
||||
{ key: "full", label: "Full" },
|
||||
{ key: "compact", label: "Compact" },
|
||||
] as const;
|
||||
|
||||
export type TNotificationsViewMode = (typeof NOTIFICATION_VIEW_MODES)[number]["key"];
|
||||
|
||||
export type TUserProfile = {
|
||||
id: string | undefined;
|
||||
user: string | undefined;
|
||||
@@ -76,6 +83,7 @@ export type TUserProfile = {
|
||||
created_at: Date | string;
|
||||
updated_at: Date | string;
|
||||
start_of_the_week: EStartOfTheWeek;
|
||||
notification_view_mode: TNotificationsViewMode;
|
||||
};
|
||||
|
||||
export interface IInstanceAdminStatus {
|
||||
|
||||
@@ -95,7 +95,6 @@ export enum CORE_EXTENSIONS {
|
||||
UTILITY = "utility",
|
||||
WORK_ITEM_EMBED = "issue-embed-component",
|
||||
EMOJI = "emoji",
|
||||
UNIQUE_ID = "uniqueID",
|
||||
}
|
||||
|
||||
export enum ADDITIONAL_EXTENSIONS {}
|
||||
|
||||
Reference in New Issue
Block a user