bb-plane-fork/web/ce/components/pages/header/lock-control.tsx
Akshita Goyal cfe169c6d7
[WEB-4423] refactor: event trackers (#7289)
* feat: event tracker helper

* feat: track click events for `data-ph-element`

* fix: handled click events

* fix: handled name

* chore: tracker element updates

* chore: remove export

* chore: tracker element type

* chore: track element and event helper.

* chore: minor improvements

* chore: minor refactors

* fix: workspace events

* fix: added slug

* fix: changes nomenclature

* fix: nomenclature

* chore: update event tracker helper types

* fix: data id

* refactor: cycle events (#7290)

* chore: update event tracker helper types

* refactor: cycle events

* refactor: cycle events

* refactor: cycle event tracker

* chore: update tracker elements

* chore: check for closest element with data-ph-element attribute

---------

Co-authored-by: Prateek Shourya <prateekshourya@Prateeks-MacBook-Pro.local>

* Refactor module events (#7291)

* chore: update event tracker helper types

* refactor: cycle events

* refactor: cycle events

* refactor: cycle event tracker

* refactor: module tracker event and element

* chore: update tracker element

* chore: revert unnecessary changes

---------

Co-authored-by: Prateek Shourya <prateekshourya@Prateeks-MacBook-Pro.local>

* refactor: global views, product tour, notifications, onboarding, users and sidebar related events

* chore: member tracker events (#7302)

* chore: member-tracker-events

* fix: constants

* refactor: update event tracker constants

* refactor: auth related event trackers (#7306)

* Chore: state events (#7307)

* chore: state events

* fix: refactor

* chore: project events (#7305)

* chore: project-events

* fix: refactor

* fix: removed hardcoded values

* fix: github redirection event

* chore: project page tracker events (#7304)

* added events for most page events

* refactor: simplify lock button event handling in PageLockControl

---------

Co-authored-by: Palanikannan M <akashmalinimurugu@gmail.com>
Co-authored-by: M. Palanikannan <73993394+Palanikannan1437@users.noreply.github.com>

* chore: minor cleanup and import fixes

* refactor: added tracker elements for buttons (#7308)

Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>

* fix: event type

* refactor: posthog group event

* chore: removed instances of event tracker (#7309)

* refactor: remove event tracker stores and hooks

* refactor: remove event tracker store

* fix: build errors

* clean up event tracker payloads

* fix: coderabbit suggestions

---------

Co-authored-by: Prateek Shourya <prateekshourya@Prateeks-MacBook-Pro.local>
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
Co-authored-by: Palanikannan M <akashmalinimurugu@gmail.com>
Co-authored-by: M. Palanikannan <73993394+Palanikannan1437@users.noreply.github.com>
Co-authored-by: Vamsi Krishna <46787868+vamsikrishnamathala@users.noreply.github.com>
2025-07-02 15:23:18 +05:30

121 lines
4.1 KiB
TypeScript

"use client";
import { useState, useEffect, useRef } from "react";
import { observer } from "mobx-react";
import { LockKeyhole, LockKeyholeOpen } from "lucide-react";
// plane imports
import { PROJECT_PAGE_TRACKER_ELEMENTS } from "@plane/constants";
import { Tooltip } from "@plane/ui";
// helpers
import { captureClick } from "@/helpers/event-tracker.helper";
// hooks
import { usePageOperations } from "@/hooks/use-page-operations";
// store
import { TPageInstance } from "@/store/pages/base-page";
// Define our lock display states, renaming "icon-only" to "neutral"
type LockDisplayState = "neutral" | "locked" | "unlocked";
type Props = {
page: TPageInstance;
};
export const PageLockControl = observer(({ page }: Props) => {
// Initial state: if locked, then "locked", otherwise default to "neutral"
const [displayState, setDisplayState] = useState<LockDisplayState>(page.is_locked ? "locked" : "neutral");
// derived values
const { canCurrentUserLockPage, is_locked } = page;
// Ref for the transition timer
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
// Ref to store the previous value of isLocked for detecting transitions
const prevLockedRef = useRef(is_locked);
// page operations
const {
pageOperations: { toggleLock },
} = usePageOperations({
page,
});
// Cleanup any running timer on unmount
useEffect(
() => () => {
if (timerRef.current) clearTimeout(timerRef.current);
},
[]
);
// Update display state when isLocked changes
useEffect(() => {
// Clear any previous timer to avoid overlapping transitions
if (timerRef.current) {
clearTimeout(timerRef.current);
timerRef.current = null;
}
// Transition logic:
// If locked, ensure the display state is "locked"
// If unlocked after being locked, show "unlocked" briefly then revert to "neutral"
if (is_locked) {
setDisplayState("locked");
} else if (prevLockedRef.current === true) {
setDisplayState("unlocked");
timerRef.current = setTimeout(() => {
setDisplayState("neutral");
timerRef.current = null;
}, 600);
} else {
setDisplayState("neutral");
}
// Update the previous locked state
prevLockedRef.current = is_locked;
}, [is_locked]);
if (!canCurrentUserLockPage) return null;
// Render different UI based on the current display state
return (
<>
{displayState === "neutral" && (
<Tooltip tooltipContent="Lock" position="bottom">
<button
type="button"
onClick={toggleLock}
data-ph-element={PROJECT_PAGE_TRACKER_ELEMENTS.LOCK_BUTTON}
className="flex-shrink-0 size-6 grid place-items-center rounded text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-80 transition-colors"
aria-label="Lock"
>
<LockKeyhole className="size-3.5" />
</button>
</Tooltip>
)}
{displayState === "locked" && (
<button
type="button"
onClick={toggleLock}
data-ph-element={PROJECT_PAGE_TRACKER_ELEMENTS.LOCK_BUTTON}
className="h-6 flex items-center gap-1 px-2 rounded text-custom-primary-100 bg-custom-primary-100/20 hover:bg-custom-primary-100/30 transition-colors"
aria-label="Locked"
>
<LockKeyhole className="flex-shrink-0 size-3.5 animate-lock-icon" />
<span className="text-xs font-medium whitespace-nowrap overflow-hidden transition-all duration-500 ease-out animate-text-slide-in">
Locked
</span>
</button>
)}
{displayState === "unlocked" && (
<div
className="h-6 flex items-center gap-1 px-2 rounded text-custom-text-200 animate-fade-out"
aria-label="Unlocked"
>
<LockKeyholeOpen className="flex-shrink-0 size-3.5 animate-unlock-icon" />
<span className="text-xs font-medium whitespace-nowrap overflow-hidden transition-all duration-500 ease-out animate-text-slide-in animate-text-fade-out">
Unlocked
</span>
</div>
)}
</>
);
});