REALTIME - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: обновление доступов workspace

This commit is contained in:
DCCONSTRUCTIONS
2026-05-12 22:26:48 +03:00
parent 5e7c9e08a0
commit 268ab2c9b9
5 changed files with 119 additions and 3 deletions
@@ -10,8 +10,10 @@ import { useEffect, useState } from "react";
import { observer } from "mobx-react";
import { X } from "lucide-react";
// plane imports
import { ENotificationLoader, ENotificationQueryParamType } from "@plane/constants";
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
// hooks
import { useWorkspaceNotifications } from "@/hooks/store/notifications";
import { useWorkspace } from "@/hooks/store/use-workspace";
// local imports
import { NotificationsRoot } from "./root";
@@ -31,6 +33,7 @@ const getInitialOpenState = () => {
export const WorkspaceNotificationsModal = observer(function WorkspaceNotificationsModal() {
const [isOpen, setIsOpen] = useState(getInitialOpenState);
const { currentWorkspace } = useWorkspace();
const { getNotifications, setCurrentSelectedNotificationId } = useWorkspaceNotifications();
useEffect(() => {
const syncFromLocation = () => {
@@ -54,6 +57,17 @@ export const WorkspaceNotificationsModal = observer(function WorkspaceNotificati
};
}, []);
useEffect(() => {
if (!isOpen || !currentWorkspace?.slug) return;
setCurrentSelectedNotificationId(undefined);
void getNotifications(
currentWorkspace.slug,
ENotificationLoader.MUTATION_LOADER,
ENotificationQueryParamType.CURRENT
);
}, [currentWorkspace?.slug, getNotifications, isOpen, setCurrentSelectedNotificationId]);
const handleClose = () => closeWorkspaceNotificationsModal();
return (
@@ -44,10 +44,11 @@ type WorkspaceMenuStateSyncProps = {
sidebarPanelButtonRef: RefObject<HTMLButtonElement | null>;
onSidebarDropdownToggle: (value: boolean) => void;
onSidebarPanelPositionChange: (position: { left: number; top: number; width: number } | null) => void;
onOpen?: () => void;
};
function WorkspaceMenuStateSync(props: WorkspaceMenuStateSyncProps) {
const { open, variant, sidebarPanelButtonRef, onSidebarDropdownToggle, onSidebarPanelPositionChange } = props;
const { open, variant, sidebarPanelButtonRef, onOpen, onSidebarDropdownToggle, onSidebarPanelPositionChange } = props;
const updateSidebarPanelMenuPosition = useCallback(() => {
if (
@@ -72,6 +73,10 @@ function WorkspaceMenuStateSync(props: WorkspaceMenuStateSyncProps) {
onSidebarDropdownToggle(open);
}, [onSidebarDropdownToggle, open]);
useEffect(() => {
if (open) onOpen?.();
}, [onOpen, open]);
useLayoutEffect(() => {
if (!open || !["sidebar-panel", "toolbar", "expanded-toolbar"].includes(variant)) {
onSidebarPanelPositionChange(null);
@@ -102,7 +107,7 @@ export const WorkspaceMenuRoot = observer(function WorkspaceMenuRoot(props: Work
const { signOut } = useUser();
const { updateUserProfile } = useUserProfile();
const { currentWorkspace: activeWorkspace, workspaces } = useWorkspace();
const { data: nodedcWorkspacePolicy } = useSWR(currentUser ? "NODEDC_WORKSPACE_POLICY" : null, () =>
const { data: nodedcWorkspacePolicy, mutate: mutateNodeDCWorkspacePolicy } = useSWR(currentUser ? "NODEDC_WORKSPACE_POLICY" : null, () =>
workspaceService.getNodeDCWorkspacePolicy()
);
// derived values
@@ -118,6 +123,9 @@ export const WorkspaceMenuRoot = observer(function WorkspaceMenuRoot(props: Work
} | null>(null);
const sidebarPanelButtonRef = useRef<HTMLButtonElement>(null);
const handleWorkspaceMenuOpen = useCallback(() => {
void mutateNodeDCWorkspacePolicy();
}, [mutateNodeDCWorkspacePolicy]);
const handleWorkspaceNavigation = (workspace: IWorkspace) => updateUserProfile({ last_workspace_id: workspace?.id });
@@ -157,6 +165,7 @@ export const WorkspaceMenuRoot = observer(function WorkspaceMenuRoot(props: Work
variant={variant}
sidebarPanelButtonRef={sidebarPanelButtonRef}
onSidebarDropdownToggle={toggleAnySidebarDropdown}
onOpen={handleWorkspaceMenuOpen}
onSidebarPanelPositionChange={setSidebarPanelMenuPosition}
/>
{variant === "sidebar" && (
@@ -99,6 +99,7 @@ export const useNodeDCRealtimeEvents = (enabled: boolean, currentUserId?: string
const refreshWorkspaceScope = async (event: TNodeDCRealtimeEvent) => {
const workspaceSlug = event.workspace_slug;
if (!workspaceSlug) return;
const projectIds = [...new Set(event.project_ids?.filter(Boolean) ?? [])];
await Promise.allSettled([
workspaceRoot.fetchWorkspaces(),
@@ -106,6 +107,9 @@ export const useNodeDCRealtimeEvents = (enabled: boolean, currentUserId?: string
memberRoot.workspace.fetchWorkspaceMemberInvitations(workspaceSlug),
projectStore.fetchProjects(workspaceSlug),
userStore.permission.fetchUserProjectPermissions(workspaceSlug),
mutate("NODEDC_WORKSPACE_POLICY"),
mutate(`NODEDC_WORKSPACE_POLICY_${workspaceSlug}`),
...projectIds.map((projectId) => memberRoot.project.fetchProjectMembers(workspaceSlug, projectId, true)),
]);
if (
@@ -127,6 +131,8 @@ export const useNodeDCRealtimeEvents = (enabled: boolean, currentUserId?: string
memberRoot.workspace.fetchWorkspaceMembers(workspaceSlug),
projectStore.fetchProjects(workspaceSlug),
userStore.permission.fetchUserProjectPermissions(workspaceSlug),
mutate("NODEDC_WORKSPACE_POLICY"),
mutate(`NODEDC_WORKSPACE_POLICY_${workspaceSlug}`),
projectId ? memberRoot.project.fetchProjectMembers(workspaceSlug, projectId, true) : Promise.resolve(),
]);
@@ -140,6 +146,21 @@ export const useNodeDCRealtimeEvents = (enabled: boolean, currentUserId?: string
}
};
const refreshDeletedWorkspaceScope = async (event: TNodeDCRealtimeEvent) => {
const workspaceSlug = event.workspace_slug;
if (!workspaceSlug) return;
await Promise.allSettled([
workspaceRoot.fetchWorkspaces(),
mutate("NODEDC_WORKSPACE_POLICY"),
mutate(`NODEDC_WORKSPACE_POLICY_${workspaceSlug}`),
]);
if (isWorkspacePath(pathnameRef.current, workspaceSlug)) {
router.replace(workspaceRoot.getWorkspaceRedirectionUrl());
}
};
const refreshInviteScope = async (event: TNodeDCRealtimeEvent) => {
await Promise.allSettled([
mutate("USER_WORKSPACE_INVITATIONS_NOTICE"),
@@ -163,6 +184,11 @@ export const useNodeDCRealtimeEvents = (enabled: boolean, currentUserId?: string
const handleEvent = async (event: TNodeDCRealtimeEvent) => {
if (!rememberEvent(event.event_id)) return;
if (event.type === "workspace.deleted") {
await refreshDeletedWorkspaceScope(event);
return;
}
if (event.type?.startsWith("workspace_member.")) {
await refreshWorkspaceScope(event);
return;