Skip to content

Commit

Permalink
Move starred messages to global state
Browse files Browse the repository at this point in the history
Co-Authored-By: Tom Richards <[email protected]>
  • Loading branch information
frederickobrien and twrichards committed Oct 27, 2023
1 parent 834c9a7 commit ea1dc50
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 84 deletions.
34 changes: 23 additions & 11 deletions client/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import { getAgateFontFaceIfApplicable } from "../fontNormaliser";
import { Global } from "@emotion/react";
import { TourStateProvider } from "./tour/tourState";
import { demoMentionableUsers, demoUser } from "./tour/tourConstants";
import { STARRED_MESSAGES_HTML_TAG } from "./starred/starredMessages";
import { StarredMessagesPortal, STARRED_MESSAGES_HTML_TAG } from "./starred/starredMessages";

const PRESELECT_PINBOARD_HTML_TAG = "pinboard-preselect";
const PRESET_UNREAD_NOTIFICATIONS_COUNT_HTML_TAG = "pinboard-bubble-preset";
Expand All @@ -65,7 +65,7 @@ export const PinBoardApp = ({
null
);
const [assetHandles, setAssetHandles] = useState<HTMLElement[]>([]);
const [starredMessagesArea, setStarredMessagesArea] =
const [maybeStarredMessagesArea, setMaybeStarredMessagesArea] =
useState<Element | null>(null);

const [workflowPinboardElements, setWorkflowPinboardElements] = useState<
Expand All @@ -86,7 +86,7 @@ export const PinBoardApp = ({
);

const refreshStarredMessagesAreaNodes = () =>
setStarredMessagesArea(document.querySelector(STARRED_MESSAGES_HTML_TAG));
setMaybeStarredMessagesArea(document.querySelector(STARRED_MESSAGES_HTML_TAG));

const refreshWorkflowPinboardElements = () =>
setWorkflowPinboardElements(
Expand Down Expand Up @@ -335,10 +335,10 @@ export const PinBoardApp = ({
const newTags =
preSelectedComposerId && composerSection
? {
composerId: preSelectedComposerId,
composerSection,
...(tags || {}),
}
composerId: preSelectedComposerId,
composerSection,
...(tags || {}),
}
: tags;
basicSendTelemetryEvent?.(type, newTags, value);
};
Expand All @@ -348,9 +348,9 @@ export const PinBoardApp = ({
PINBOARD_TELEMETRY_TYPE.PINBOARD_LOADED,
preSelectedComposerId && composerSection
? {
composerId: preSelectedComposerId,
composerSection: composerSection,
}
composerId: preSelectedComposerId,
composerSection: composerSection,
}
: {}
);
}, [preSelectedComposerId, composerSection]);
Expand All @@ -359,6 +359,9 @@ export const PinBoardApp = ({

const hasApolloAuthError = useReactiveVar(hasApolloAuthErrorVar);

const [starredMessages, setStarredMessages] = useState<Item[]>([]);
const [maybeScrollToItem, setMaybeScrollToItem] = useState<(itemId: string) => void>();

return (
<TelemetryContext.Provider value={sendTelemetryEvent}>
<ApolloProvider client={apolloClient}>
Expand All @@ -382,7 +385,8 @@ export const PinBoardApp = ({
}
hasEverUsedTour={me?.hasEverUsedTour}
visitTourStep={visitTourStep}
maybeStarredMessagesArea={starredMessagesArea}
setStarredMessages={setStarredMessages}
setMaybeScrollToItem={setMaybeScrollToItem}
>
<TourStateProvider>
<Global styles={agateFontFaceIfApplicable} />
Expand Down Expand Up @@ -438,6 +442,14 @@ export const PinBoardApp = ({
expand={() => setIsExpanded(true)}
/>
))}
{maybeStarredMessagesArea && (
<StarredMessagesPortal
node={maybeStarredMessagesArea}
starredMessages={starredMessages}
userLookup={userLookup}
maybeScrollToItem={maybeScrollToItem}
/>
)}
</TourStateProvider>
</GlobalStateProvider>
</ApolloProvider>
Expand Down
107 changes: 55 additions & 52 deletions client/src/globalState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ interface GlobalStateContextShape {
boundedPositionTranslation: ControlPosition;
updateBoundedPositionTranslation: (newPosition: ControlPosition) => void;

maybeStarredMessagesArea: Element | null;
setStarredMessages: (starredMessages: Item[]) => void;
setMaybeScrollToItem: (func: (itemId: string) => void) => void;
}
const GlobalStateContext = React.createContext<GlobalStateContextShape | null>(
null
Expand Down Expand Up @@ -126,7 +127,8 @@ interface GlobalStateProviderProps {
presetUnreadNotificationCount: number | undefined;
hasEverUsedTour: boolean | undefined;
visitTourStep: (tourStepId: string) => void;
maybeStarredMessagesArea: Element | null;
setStarredMessages: (starredMessages: Item[]) => void;
setMaybeScrollToItem: (func: (itemId: string) => void) => void;
}
export const GlobalStateProvider: React.FC<GlobalStateProviderProps> = ({
hasApolloAuthError,
Expand All @@ -147,7 +149,8 @@ export const GlobalStateProvider: React.FC<GlobalStateProviderProps> = ({
hasEverUsedTour,
visitTourStep,
children,
maybeStarredMessagesArea,
setStarredMessages,
setMaybeScrollToItem,
}) => {
const [activeTab, setActiveTab] = useState<Tab>(ChatTab);

Expand Down Expand Up @@ -209,16 +212,16 @@ export const GlobalStateProvider: React.FC<GlobalStateProviderProps> = ({
const activePinboardIds = isDemoSelectedPinboard
? [demoPinboardData.id]
: [
...(preselectedPinboardId ? [preselectedPinboardId] : []),
...(maybeOpenPinboardIdBasedOnQueryParam
? [maybeOpenPinboardIdBasedOnQueryParam]
: []),
...manuallyOpenedPinboardIds?.filter(
(_) =>
_ !== preselectedPinboardId &&
_ !== maybeOpenPinboardIdBasedOnQueryParam
),
];
...(preselectedPinboardId ? [preselectedPinboardId] : []),
...(maybeOpenPinboardIdBasedOnQueryParam
? [maybeOpenPinboardIdBasedOnQueryParam]
: []),
...manuallyOpenedPinboardIds?.filter(
(_) =>
_ !== preselectedPinboardId &&
_ !== maybeOpenPinboardIdBasedOnQueryParam
),
];

const pinboardDataQuery = useQuery<{
getPinboardsByIds: PinboardData[];
Expand Down Expand Up @@ -273,14 +276,14 @@ export const GlobalStateProvider: React.FC<GlobalStateProviderProps> = ({
(
isDemo: false // this asks the compiler to ensure we never call this in demo mode
) =>
(pinboardId: string, maybeEmailOverride?: string) =>
!isDemo &&
addManuallyOpenedPinboardIds({
variables: {
pinboardId,
maybeEmailOverride,
},
});
(pinboardId: string, maybeEmailOverride?: string) =>
!isDemo &&
addManuallyOpenedPinboardIds({
variables: {
pinboardId,
maybeEmailOverride,
},
});

const [interTabChannel] = useState<BroadcastChannel>(
new BroadcastChannel("pinboard-inter-tab-communication")
Expand Down Expand Up @@ -312,13 +315,12 @@ export const GlobalStateProvider: React.FC<GlobalStateProviderProps> = ({
const hostname = window.location.hostname;
const composerDomain =
hostname.includes(".local.") ||
hostname.includes(".code.") ||
hostname.includes(".test.")
hostname.includes(".code.") ||
hostname.includes(".test.")
? "code.dev-gutools.co.uk"
: "gutools.co.uk";
const composerUrl = `https://composer.${composerDomain}/content/${
pinboardData.composerId || ".."
}?${EXPAND_PINBOARD_QUERY_PARAM}=true`;
const composerUrl = `https://composer.${composerDomain}/content/${pinboardData.composerId || ".."
}?${EXPAND_PINBOARD_QUERY_PARAM}=true`;

window?.open(composerUrl, "_blank")?.focus();
}, 500);
Expand All @@ -330,7 +332,7 @@ export const GlobalStateProvider: React.FC<GlobalStateProviderProps> = ({
clearTimeout(openInNewTabTimeoutId);
alert(
"The composer file you want to see is already open in another tab.\n\n" +
"You can see an alert message on that tab too to make it easier to find but, unfortunately, you’ll need to select the tab manually."
"You can see an alert message on that tab too to make it easier to find but, unfortunately, you’ll need to select the tab manually."
);
}
},
Expand All @@ -345,26 +347,26 @@ export const GlobalStateProvider: React.FC<GlobalStateProviderProps> = ({

const openPinboard =
(isDemo: boolean) =>
(pinboardData: PinboardData, isOpenInNewTab: boolean) => {
if (!isDemo && !activePinboardIds.includes(pinboardData.id)) {
addManuallyOpenedPinboardId(isDemo)(pinboardData.id).then(
(result) =>
result.data
? setManuallyOpenedPinboardIds(
(pinboardData: PinboardData, isOpenInNewTab: boolean) => {
if (!isDemo && !activePinboardIds.includes(pinboardData.id)) {
addManuallyOpenedPinboardId(isDemo)(pinboardData.id).then(
(result) =>
result.data
? setManuallyOpenedPinboardIds(
result.data.addManuallyOpenedPinboardIds
)
: console.error(
: console.error(
"addManuallyOpenedPinboardIds did not return any data"
) // TODO probably report to Sentry
);
}
);
}

if (isOpenInNewTab) {
openPinboardInNewTab(pinboardData);
} else {
setSelectedPinboardId(pinboardData.id);
}
};
if (isOpenInNewTab) {
openPinboardInNewTab(pinboardData);
} else {
setSelectedPinboardId(pinboardData.id);
}
};

const [errors, setErrors] = useState<PerPinboard<ApolloError>>({});

Expand Down Expand Up @@ -419,11 +421,11 @@ export const GlobalStateProvider: React.FC<GlobalStateProviderProps> = ({
(result) =>
result.data
? setManuallyOpenedPinboardIds(
result.data.removeManuallyOpenedPinboardIds
)
result.data.removeManuallyOpenedPinboardIds
)
: console.error(
"removeManuallyOpenedPinboardIds did not return any data"
) // TODO probably report to Sentry
"removeManuallyOpenedPinboardIds did not return any data"
) // TODO probably report to Sentry
);
}
setSelectedPinboardId(null);
Expand Down Expand Up @@ -465,13 +467,13 @@ export const GlobalStateProvider: React.FC<GlobalStateProviderProps> = ({
x: isTooFarLeft
? 10 + floatySize - viewportWidth
: isTooFarRight
? -10
: positionTranslation.x,
? -10
: positionTranslation.x,
y: isTooHigh
? top + floatySize - viewportHeight
: isTooLow
? -10
: positionTranslation.y,
? -10
: positionTranslation.y,
};
};

Expand Down Expand Up @@ -502,7 +504,7 @@ export const GlobalStateProvider: React.FC<GlobalStateProviderProps> = ({
useEffect(() => {
const savedExplicitPositionTranslation = JSON.parse(
window.localStorage.getItem(LOCAL_STORAGE_KEY_EXPLICIT_POSITION) ||
JSON.stringify({ x: 0 - right, y: 0 - bottom })
JSON.stringify({ x: 0 - right, y: 0 - bottom })
);
setExplicitPositionTranslation(savedExplicitPositionTranslation);
updateBoundedPositionTranslation(savedExplicitPositionTranslation);
Expand Down Expand Up @@ -586,7 +588,8 @@ export const GlobalStateProvider: React.FC<GlobalStateProviderProps> = ({
boundedPositionTranslation,
updateBoundedPositionTranslation,

maybeStarredMessagesArea,
setStarredMessages,
setMaybeScrollToItem,
};

return (
Expand Down
24 changes: 11 additions & 13 deletions client/src/pinboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import { maybeConstructPayloadAndType } from "./types/PayloadAndType";
import { useTourProgress, useTourStepRef } from "./tour/tourState";
import { Reply } from "./reply";
import { isPinboardData } from "shared/graphql/extraTypes";
import { StarredMessagesPortal } from "./starred/starredMessages";

export interface ItemsMap {
[id: string]: Item | PendingItem;
Expand Down Expand Up @@ -68,8 +67,8 @@ export const Pinboard: React.FC<PinboardProps> = ({

addManuallyOpenedPinboardId,

maybeStarredMessagesArea,
preselectedPinboard,
setStarredMessages,
} = useGlobalStateContext();

const sendTelemetryEvent = useContext(TelemetryContext);
Expand Down Expand Up @@ -149,6 +148,14 @@ export const Pinboard: React.FC<PinboardProps> = ({
const lastItemIndex = items.length - 1;
const lastItem = items[lastItemIndex];

useEffect(() => {
if (isPinboardData(preselectedPinboard) && preselectedPinboard.id === pinboardId) {
setStarredMessages(items.filter(
(item) => item.isStarred && !item.deletedAt
));
}
}, [items, preselectedPinboard]);

const initialLastItemSeenByUsersQuery = useQuery(
gqlGetLastItemSeenByUsers(pinboardId),
{
Expand Down Expand Up @@ -231,8 +238,8 @@ export const Pinboard: React.FC<PinboardProps> = ({
setError(
pinboardId,
initialItemsQuery.error ||
itemSubscription.error ||
claimSubscription.error
itemSubscription.error ||
claimSubscription.error
),
[initialItemsQuery.error, itemSubscription.error, claimSubscription.error]
);
Expand Down Expand Up @@ -397,15 +404,6 @@ export const Pinboard: React.FC<PinboardProps> = ({
/>
</div>
)}
{isPinboardData(preselectedPinboard) &&
preselectedPinboard.id === pinboardId &&
maybeStarredMessagesArea && (
<StarredMessagesPortal
node={maybeStarredMessagesArea}
items={items}
userLookup={userLookup}
/>
)}
</React.Fragment>
);
};
21 changes: 18 additions & 3 deletions client/src/scrollableItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import { PendingItem } from "./types/PendingItem";
import { UserLookup } from "./types/UserLookup";
import { PINBOARD_ITEM_ID_QUERY_PARAM } from "../../shared/constants";
import { useTourProgress } from "./tour/tourState";
import { isPinboardData } from "shared/graphql/extraTypes";
import { StarredMessagesPortal } from "./starred/starredMessages";
import { useGlobalStateContext } from "./globalState";

interface ScrollableItemsProps {
items: Array<PendingItem | Item>;
Expand Down Expand Up @@ -75,6 +78,8 @@ export const ScrollableItems = ({
setMaybeEditingItemId,
setMaybeReplyingToItemId,
}: ScrollableItemsProps) => {
const { setMaybeScrollToItem } = useGlobalStateContext();

const [isScrolledToBottom, setIsScrolledToBottom] = useState(true);

const lastItemSeenByUsersForItemIDLookup = useMemo(
Expand Down Expand Up @@ -218,13 +223,23 @@ export const ScrollableItems = ({
const scrollToItem = useCallback(
(itemID: string) => {
const targetElement = refMap.current[itemID];
targetElement?.scrollIntoView({ behavior: "smooth" });
targetElement.style.animation = "highlight-item 0.5s linear infinite"; // see panel.tsx for definition of 'highlight-item' animation
setTimeout(() => (targetElement.style.animation = ""), 2500);
if (targetElement) {
targetElement.scrollIntoView({ behavior: "smooth" });
targetElement.style.animation = "highlight-item 0.5s linear infinite"; // see panel.tsx for definition of 'highlight-item' animation
setTimeout(() => (targetElement.style.animation = ""), 2500);
} else {
console.error(
"Tried to scroll to item with ID",
itemID,
"but could not find it in the refMap"
);
}
},
[refMap.current]
);

useEffect(() => setMaybeScrollToItem(scrollToItem), [scrollToItem]);

useLayoutEffect(() => {
if (
!hasProcessedItemIdInURL &&
Expand Down
Loading

0 comments on commit ea1dc50

Please sign in to comment.