Compare commits

..
74 changed files with 465 additions and 278 deletions
@@ -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),
),
]
+1 -1
View File
@@ -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)
-3
View File
@@ -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 -1
View File
@@ -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}
@@ -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
/**
+2 -2
View File
@@ -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 -1
View File
@@ -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
@@ -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,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,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,
+1 -1
View File
@@ -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";
+2 -2
View File
@@ -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";
+3
View File
@@ -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";
+8
View File
@@ -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 {
-1
View File
@@ -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 {}