diff --git a/web/CommIcon.react.js b/web/CommIcon.react.js
--- a/web/CommIcon.react.js
+++ b/web/CommIcon.react.js
@@ -15,7 +15,9 @@
| 'reply-filled'
| 'megaphone'
| 'copy-filled'
- | 'emote-smile-filled';
+ | 'emote-smile-filled'
+ | 'pin'
+ | 'unpin';
type CommIconProps = {
+icon: CommIcons,
diff --git a/web/modals/chat/toggle-pin-modal.css b/web/modals/chat/toggle-pin-modal.css
new file mode 100644
diff --git a/web/modals/chat/toggle-pin-modal.react.js b/web/modals/chat/toggle-pin-modal.react.js
new file mode 100644
--- /dev/null
+++ b/web/modals/chat/toggle-pin-modal.react.js
@@ -0,0 +1,18 @@
+// @flow
+
+import * as React from 'react';
+
+import type { ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
+import type { ThreadInfo } from 'lib/types/thread-types.js';
+
+type TogglePinModalProps = {
+ +item: ChatMessageInfoItem,
+ +threadInfo: ThreadInfo,
+};
+
+// eslint-disable-next-line no-unused-vars
+function TogglePinModal(props: TogglePinModalProps): React.Node {
+ return <>>;
+}
+
+export default TogglePinModal;
diff --git a/web/utils/tooltip-utils.js b/web/utils/tooltip-utils.js
--- a/web/utils/tooltip-utils.js
+++ b/web/utils/tooltip-utils.js
@@ -4,6 +4,7 @@
import _debounce from 'lodash/debounce.js';
import * as React from 'react';
+import { useModalContext } from 'lib/components/modal-provider.react.js';
import type { ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
import { createMessageReply } from 'lib/shared/message-utils.js';
import { useCanCreateReactionFromMessage } from 'lib/shared/reaction-utils.js';
@@ -30,6 +31,7 @@
import { useTooltipContext } from '../chat/tooltip-provider.js';
import CommIcon from '../CommIcon.react.js';
import { InputStateContext } from '../input/input-state.js';
+import TogglePinModal from '../modals/chat/toggle-pin-modal.react.js';
import {
useOnClickPendingSidebar,
useOnClickThread,
@@ -481,6 +483,38 @@
}, [canCreateReactionFromMessage, setShouldRenderEmojiKeyboard]);
}
+function useMessageTogglePinAction(
+ item: ChatMessageInfoItem,
+ threadInfo: ThreadInfo,
+): ?MessageTooltipAction {
+ const { pushModal } = useModalContext();
+ const { messageInfo, isPinned } = item;
+
+ const canTogglePin =
+ isComposableMessageType(messageInfo.type) &&
+ threadHasPermission(threadInfo, threadPermissions.MANAGE_PINS);
+
+ return React.useMemo(() => {
+ if (!canTogglePin) {
+ return null;
+ }
+
+ const iconName = isPinned ? 'unpin' : 'pin';
+
+ const buttonContent = ;
+
+ const onClickTogglePin = () => {
+ pushModal();
+ };
+
+ return {
+ actionButtonContent: buttonContent,
+ onClick: onClickTogglePin,
+ label: isPinned ? 'Unpin' : 'Pin',
+ };
+ }, [canTogglePin, isPinned, pushModal, item, threadInfo]);
+}
+
function useMessageTooltipActions(
item: ChatMessageInfoItem,
threadInfo: ThreadInfo,
@@ -489,9 +523,17 @@
const replyAction = useMessageTooltipReplyAction(item, threadInfo);
const copyAction = useMessageCopyAction(item);
const reactAction = useMessageReactAction(item, threadInfo);
+ const togglePinAction = useMessageTogglePinAction(item, threadInfo);
return React.useMemo(
- () => [replyAction, sidebarAction, copyAction, reactAction].filter(Boolean),
- [replyAction, sidebarAction, copyAction, reactAction],
+ () =>
+ [
+ replyAction,
+ sidebarAction,
+ copyAction,
+ reactAction,
+ togglePinAction,
+ ].filter(Boolean),
+ [replyAction, sidebarAction, copyAction, reactAction, togglePinAction],
);
}