PromucFlow_constructor/app/client/src/pages/Editor/ToggleModeButton.tsx

413 lines
12 KiB
TypeScript
Raw Normal View History

import React, { useCallback, useEffect, useState } from "react";
import styled from "styled-components";
import { useDispatch, useSelector } from "react-redux";
import TooltipComponent from "components/ads/Tooltip";
import TourTooltipWrapper from "components/ads/tour/TourTooltipWrapper";
import { ReactComponent as Pen } from "assets/icons/comments/pen.svg";
import { ReactComponent as Eye } from "assets/icons/comments/eye.svg";
import { ReactComponent as CommentModeUnread } from "assets/icons/comments/comment-mode-unread-indicator.svg";
import { ReactComponent as CommentMode } from "assets/icons/comments/chat.svg";
2021-06-11 15:01:32 +00:00
import { Indices } from "constants/Layers";
import {
setCommentMode as setCommentModeAction,
fetchApplicationCommentsRequest,
showCommentsIntroCarousel,
} from "actions/commentActions";
import {
commentModeSelector,
areCommentsEnabledForUserAndApp as areCommentsEnabledForUserAndAppSelector,
showUnreadIndicator as showUnreadIndicatorSelector,
} from "../../selectors/commentsSelectors";
import { getCurrentUser } from "selectors/usersSelectors";
import { useLocation } from "react-router";
import history from "utils/history";
import { Position } from "@blueprintjs/core/lib/esm/common/position";
import { TourType } from "entities/Tour";
import useProceedToNextTourStep, {
useIsTourStepActive,
} from "utils/hooks/useProceedToNextTourStep";
import { getCommentsIntroSeen } from "utils/storage";
import { ANONYMOUS_USERNAME, User } from "constants/userConstants";
import { AppState } from "reducers";
import { APP_MODE } from "reducers/entityReducers/appReducer";
import {
AUTH_LOGIN_URL,
matchBuilderPath,
matchViewerPath,
} from "constants/routes";
import { createMessage, UNREAD_MESSAGE } from "constants/messages";
2021-07-02 06:04:36 +00:00
import localStorage from "utils/localStorage";
import { getAppMode } from "selectors/applicationSelectors";
import { noop } from "lodash";
import {
commentsTourStepsEditModeTypes,
commentsTourStepsPublishedModeTypes,
} from "comments/tour/commentsTourSteps";
2021-07-02 06:04:36 +00:00
const getShowCommentsButtonToolTip = () => {
const flag = localStorage.getItem("ShowCommentsButtonToolTip");
return flag === null || !!flag;
};
const setShowCommentsButtonToolTip = (value = "") =>
localStorage.setItem("ShowCommentsButtonToolTip", value);
const ModeButton = styled.div<{
active: boolean;
showSelectedMode: boolean;
type: string;
}>`
position: relative;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
height: ${(props) => props.theme.smallHeaderHeight};
width: ${(props) => props.theme.smallHeaderHeight};
background: ${(props) =>
props.active && props.showSelectedMode
? props.theme.colors.comments.activeModeBackground
: "transparent"};
svg path {
fill: ${(props) =>
props.type !== "fill"
? "transparent"
: props.active
? props.theme.colors.comments.activeModeIcon
: props.theme.colors.comments.modeIcon};
stroke: ${(props) =>
props.type !== "stroke"
? "transparent"
: props.active
? props.theme.colors.comments.activeModeIcon
: props.theme.colors.comments.modeIcon};
}
svg rect:not(:first-child) {
fill: ${(props) =>
props.active
? props.theme.colors.comments.activeModeIcon
: props.theme.colors.comments.modeIcon};
}
svg circle {
stroke: ${(props) =>
props.active
? props.theme.colors.comments.activeModeIconCircleStroke
: props.theme.colors.comments.modeIconCircleStroke};
}
`;
const Container = styled.div`
display: flex;
flex: 1;
2021-06-11 15:01:32 +00:00
z-index: ${Indices.Layer1};
margin-left: ${(props) => props.theme.smallHeaderHeight};
`;
/**
* Sync comment mode in store with comment mode in URL
* Fetch app comments when comment mode is selected
*/
// eslint-disable-next-line
const useUpdateCommentMode = async (currentUser?: User) => {
const location = useLocation();
const dispatch = useDispatch();
const isCommentMode = useSelector(commentModeSelector);
const setCommentModeInStore = useCallback(
(updatedIsCommentMode) =>
dispatch(setCommentModeAction(updatedIsCommentMode)),
[],
);
const handleLocationUpdate = async () => {
if (!currentUser) return;
const searchParams = new URL(window.location.href).searchParams;
const isCommentMode = searchParams.get("isCommentMode");
const isCommentsIntroSeen = await getCommentsIntroSeen();
const updatedIsCommentMode = isCommentMode === "true" ? true : false;
const notLoggedId = currentUser?.username === ANONYMOUS_USERNAME;
if (notLoggedId && updatedIsCommentMode) {
const currentUrl = window.location.href;
const path = `${AUTH_LOGIN_URL}?redirectUrl=${encodeURIComponent(
currentUrl,
)}`;
history.push(path);
return;
}
if (updatedIsCommentMode && !isCommentsIntroSeen) {
dispatch(showCommentsIntroCarousel());
} else {
setCommentModeInStore(updatedIsCommentMode);
}
};
// sync comment mode in store with comment mode in URL
useEffect(() => {
if (window.location.href) {
handleLocationUpdate();
}
}, [location, !!currentUser]);
// fetch applications comments when comment mode is turned on
useEffect(() => {
if (isCommentMode) {
dispatch(fetchApplicationCommentsRequest());
}
}, [isCommentMode]);
};
export const setCommentModeInUrl = (isCommentMode: boolean) => {
const currentURL = new URL(window.location.href);
const searchParams = currentURL.searchParams;
searchParams.set("isCommentMode", `${isCommentMode}`);
// remove comment link params so that they don't get retriggered
// on toggling comment mode
searchParams.delete("commentId");
searchParams.delete("commentThreadId");
history.replace({
pathname: currentURL.pathname,
search: searchParams.toString(),
hash: currentURL.hash,
});
};
function EditModeReset() {
return (
<TooltipComponent
content={
<>
Edit Mode
<span style={{ color: "#fff", marginLeft: 20 }}>V</span>
</>
}
hoverOpenDelay={1000}
position={Position.BOTTOM}
>
<Pen />
</TooltipComponent>
);
}
function ViewModeReset() {
return (
<TooltipComponent
content={
<>
View Mode
<span style={{ color: "#fff", marginLeft: 20 }}>V</span>
</>
}
hoverOpenDelay={1000}
position={Position.BOTTOM}
>
<Eye />
</TooltipComponent>
);
}
2021-07-02 06:04:36 +00:00
const tourToolTipProps = {
hasOverlay: true,
modifiers: {
offset: { enabled: true, offset: "3, 20" },
arrow: {
enabled: true,
fn: (data: any) => ({
...data,
offsets: {
...data.offsets,
arrow: {
top: -8,
left: 80,
},
},
}),
},
},
pulseStyles: {
top: 20,
left: 28,
height: 30,
width: 30,
},
showPulse: true,
activeStepConfig: {
[TourType.COMMENTS_TOUR_EDIT_MODE]:
commentsTourStepsEditModeTypes.ENTER_COMMENTS_MODE,
[TourType.COMMENTS_TOUR_PUBLISHED_MODE]:
commentsTourStepsPublishedModeTypes.ENTER_COMMENTS_MODE,
},
2021-07-02 06:04:36 +00:00
};
function ViewOrEditMode({ mode }: { mode?: APP_MODE }) {
return mode === APP_MODE.EDIT ? <EditModeReset /> : <ViewModeReset />;
}
function CommentModeBtn({
handleSetCommentModeButton,
isCommentMode,
showSelectedMode,
2021-07-02 06:04:36 +00:00
showUnreadIndicator,
}: {
handleSetCommentModeButton: () => void;
isCommentMode: boolean;
showUnreadIndicator: boolean;
showSelectedMode: boolean;
2021-07-02 06:04:36 +00:00
}) {
const CommentModeIcon = showUnreadIndicator ? CommentModeUnread : CommentMode;
return (
<ModeButton
active={isCommentMode}
className="t--switch-comment-mode-on"
onClick={handleSetCommentModeButton}
showSelectedMode={showSelectedMode}
type="stroke"
>
2021-07-02 06:04:36 +00:00
<TooltipComponent
content={
<>
Comment Mode
<span style={{ color: "#fff", marginLeft: 20 }}>C</span>
</>
}
hoverOpenDelay={1000}
position={Position.BOTTOM}
>
<CommentModeIcon />
</TooltipComponent>
</ModeButton>
);
}
const useShowCommentDiscoveryTooltip = (): [boolean, typeof noop] => {
const currentUser = useSelector(getCurrentUser);
const appMode = useSelector(getAppMode);
const initShowCommentButtonDiscoveryTooltip =
getShowCommentsButtonToolTip() &&
appMode === APP_MODE.PUBLISHED &&
currentUser?.username !== ANONYMOUS_USERNAME;
const [
showCommentButtonDiscoveryTooltip,
setShowCommentButtonDiscoveryTooltipInState,
] = useState(initShowCommentButtonDiscoveryTooltip);
useEffect(() => {
setShowCommentButtonDiscoveryTooltipInState(
initShowCommentButtonDiscoveryTooltip,
);
}, [appMode, currentUser]);
return [
showCommentButtonDiscoveryTooltip,
setShowCommentButtonDiscoveryTooltipInState,
];
};
const useShouldHide = () => {
const [shouldHide, setShouldHide] = useState(false);
const location = useLocation();
useEffect(() => {
const pathName = window.location.pathname;
const shouldShow = matchBuilderPath(pathName) || matchViewerPath(pathName);
setShouldHide(!shouldShow);
}, [location]);
return shouldHide;
};
type ToggleCommentModeButtonProps = {
showSelectedMode?: boolean;
};
function ToggleCommentModeButton({
showSelectedMode = true,
}: ToggleCommentModeButtonProps) {
const commentsEnabled = useSelector(areCommentsEnabledForUserAndAppSelector);
const isCommentMode = useSelector(commentModeSelector);
const currentUser = useSelector(getCurrentUser);
2021-07-02 06:04:36 +00:00
const [
showCommentButtonDiscoveryTooltip,
setShowCommentButtonDiscoveryTooltipInState,
] = useShowCommentDiscoveryTooltip();
const showUnreadIndicator =
useSelector(showUnreadIndicatorSelector) ||
showCommentButtonDiscoveryTooltip;
useUpdateCommentMode(currentUser);
const activeStepConfig = {
[TourType.COMMENTS_TOUR_EDIT_MODE]:
commentsTourStepsEditModeTypes.ENTER_COMMENTS_MODE,
[TourType.COMMENTS_TOUR_PUBLISHED_MODE]:
commentsTourStepsPublishedModeTypes.ENTER_COMMENTS_MODE,
};
const proceedToNextTourStep = useProceedToNextTourStep(activeStepConfig);
const isTourStepActive = useIsTourStepActive(activeStepConfig);
const mode = useSelector((state: AppState) => state.entities.app.mode);
2021-07-02 06:04:36 +00:00
const handleSetCommentModeButton = useCallback(() => {
setCommentModeInUrl(true);
proceedToNextTourStep();
setShowCommentButtonDiscoveryTooltipInState(false);
setShowCommentsButtonToolTip();
}, [proceedToNextTourStep, setShowCommentButtonDiscoveryTooltipInState]);
// Show comment mode button only on the canvas editor and viewer
const shouldHide = useShouldHide();
if (!commentsEnabled || shouldHide) return null;
return (
<Container>
2021-07-02 06:04:36 +00:00
<TourTooltipWrapper {...tourToolTipProps}>
2021-06-11 15:01:32 +00:00
<div style={{ display: "flex" }}>
<ModeButton
active={!isCommentMode}
onClick={() => setCommentModeInUrl(false)}
showSelectedMode={showSelectedMode}
type="fill"
2021-06-11 15:01:32 +00:00
>
2021-07-02 06:04:36 +00:00
<ViewOrEditMode mode={mode} />
2021-06-11 15:01:32 +00:00
</ModeButton>
2021-07-02 06:04:36 +00:00
<TooltipComponent
content={createMessage(UNREAD_MESSAGE)}
2021-07-02 06:04:36 +00:00
isOpen={showCommentButtonDiscoveryTooltip}
>
2021-07-02 06:04:36 +00:00
<CommentModeBtn
{...{
handleSetCommentModeButton,
isCommentMode: isCommentMode || isTourStepActive, // Highlight the button during the tour
2021-07-02 06:04:36 +00:00
showUnreadIndicator,
showSelectedMode,
2021-07-02 06:04:36 +00:00
}}
/>
</TooltipComponent>
2021-06-11 15:01:32 +00:00
</div>
</TourTooltipWrapper>
</Container>
);
}
export default ToggleCommentModeButton;