diff --git a/native/chat/message-result.react.js b/native/chat/message-result.react.js
new file mode 100644
--- /dev/null
+++ b/native/chat/message-result.react.js
@@ -0,0 +1,20 @@
+// @flow
+
+import * as React from 'react';
+import { View } from 'react-native';
+
+import { type ThreadInfo } from 'lib/types/thread-types.js';
+
+import type { ChatMessageInfoItemWithHeight } from '../types/chat-types.js';
+
+type MessageResultProps = {
+ +item: ChatMessageInfoItemWithHeight,
+ +threadInfo: ThreadInfo,
+};
+
+/* eslint-disable no-unused-vars */
+function MessageResult(props: MessageResultProps): React.Node {
+ return ;
+}
+
+export default MessageResult;
diff --git a/native/chat/multimedia-message-tooltip-modal.react.js b/native/chat/multimedia-message-tooltip-modal.react.js
--- a/native/chat/multimedia-message-tooltip-modal.react.js
+++ b/native/chat/multimedia-message-tooltip-modal.react.js
@@ -1,5 +1,6 @@
// @flow
+import { useNavigation } from '@react-navigation/native';
import * as React from 'react';
import { useOnPressReport } from './message-report-utils.js';
@@ -7,6 +8,7 @@
import { useAnimatedNavigateToSidebar } from './sidebar-navigation.js';
import CommIcon from '../components/comm-icon.react.js';
import SWMansionIcon from '../components/swmansion-icon.react.js';
+import { TogglePinModalRouteName } from '../navigation/route-names.js';
import {
createTooltip,
type TooltipParams,
@@ -25,8 +27,17 @@
props: TooltipMenuProps<'MultimediaMessageTooltipModal'>,
): React.Node {
const { route, tooltipItem: TooltipItem } = props;
+ const navigation = useNavigation();
- const onPressTogglePin = React.useCallback(() => {}, []);
+ const onPressTogglePin = React.useCallback(() => {
+ navigation.navigate<'TogglePinModal'>({
+ name: TogglePinModalRouteName,
+ params: {
+ threadInfo: route.params.item.threadInfo,
+ item: route.params.item,
+ },
+ });
+ }, [navigation, route.params.item]);
const renderPinIcon = React.useCallback(
style => ,
[],
diff --git a/native/chat/text-message-tooltip-modal.react.js b/native/chat/text-message-tooltip-modal.react.js
--- a/native/chat/text-message-tooltip-modal.react.js
+++ b/native/chat/text-message-tooltip-modal.react.js
@@ -1,6 +1,7 @@
// @flow
import Clipboard from '@react-native-clipboard/clipboard';
+import { useNavigation } from '@react-navigation/native';
import invariant from 'invariant';
import * as React from 'react';
@@ -13,6 +14,7 @@
import SWMansionIcon from '../components/swmansion-icon.react.js';
import { InputStateContext } from '../input/input-state.js';
import { displayActionResultModal } from '../navigation/action-result-modal.js';
+import { TogglePinModalRouteName } from '../navigation/route-names.js';
import {
createTooltip,
type TooltipParams,
@@ -31,6 +33,7 @@
props: TooltipMenuProps<'TextMessageTooltipModal'>,
): React.Node {
const { route, tooltipItem: TooltipItem } = props;
+ const navigation = useNavigation();
const inputState = React.useContext(InputStateContext);
const { text } = route.params.item.messageInfo;
@@ -67,7 +70,15 @@
[],
);
- const onPressTogglePin = React.useCallback(() => {}, []);
+ const onPressTogglePin = React.useCallback(() => {
+ navigation.navigate<'TogglePinModal'>({
+ name: TogglePinModalRouteName,
+ params: {
+ threadInfo: route.params.item.threadInfo,
+ item: route.params.item,
+ },
+ });
+ }, [navigation, route.params.item]);
const renderPinIcon = React.useCallback(
style => ,
[],
diff --git a/native/chat/toggle-pin-modal.react.js b/native/chat/toggle-pin-modal.react.js
new file mode 100644
--- /dev/null
+++ b/native/chat/toggle-pin-modal.react.js
@@ -0,0 +1,197 @@
+// @flow
+
+import invariant from 'invariant';
+import * as React from 'react';
+import { Text, View } from 'react-native';
+
+import {
+ toggleMessagePin,
+ toggleMessagePinActionTypes,
+} from 'lib/actions/thread-actions.js';
+import { type ThreadInfo } from 'lib/types/thread-types.js';
+import {
+ useServerCall,
+ useDispatchActionPromise,
+} from 'lib/utils/action-utils.js';
+
+import MessageResult from './message-result.react.js';
+import Button from '../components/button.react.js';
+import Modal from '../components/modal.react.js';
+import type { AppNavigationProp } from '../navigation/app-navigator.react.js';
+import type { NavigationRoute } from '../navigation/route-names.js';
+import { useStyles } from '../themes/colors.js';
+import type { ChatMessageInfoItemWithHeight } from '../types/chat-types';
+
+export type TogglePinModalParams = {
+ +item: ChatMessageInfoItemWithHeight,
+ +threadInfo: ThreadInfo,
+};
+
+type TogglePinModalProps = {
+ +navigation: AppNavigationProp<'TogglePinModal'>,
+ +route: NavigationRoute<'TogglePinModal'>,
+};
+
+function TogglePinModal(props: TogglePinModalProps): React.Node {
+ const { navigation, route } = props;
+ const { item, threadInfo } = route.params;
+ const { messageInfo, isPinned } = item;
+ const styles = useStyles(unboundStyles);
+
+ const callToggleMessagePin = useServerCall(toggleMessagePin);
+ const dispatchActionPromise = useDispatchActionPromise();
+
+ const modalInfo = React.useMemo(() => {
+ if (isPinned) {
+ return {
+ name: 'Remove Pinned Message',
+ action: 'unpin',
+ confirmationText: `Are you sure you want to remove this pinned message?`,
+ buttonText: 'Remove Pinned Message',
+ buttonStyle: styles.removePinButton,
+ };
+ }
+
+ return {
+ name: 'Pin Message',
+ action: 'pin',
+ confirmationText: `You may pin this message to the channel you are currently viewing. To unpin a message, select the pinned messages icon in the channel.`,
+ buttonText: 'Pin Message',
+ buttonStyle: styles.pinButton,
+ };
+ }, [isPinned, styles.pinButton, styles.removePinButton]);
+
+ const modifiedItem = React.useMemo(() => {
+ if (item.messageShapeType === 'robotext') {
+ return item;
+ }
+
+ if (item.messageShapeType === 'multimedia') {
+ return {
+ ...item,
+ threadCreatedFromMessage: undefined,
+ reactions: {},
+ startsConversation: false,
+ messageInfo: {
+ ...item.messageInfo,
+ creator: {
+ ...item.messageInfo.creator,
+ isViewer: false,
+ },
+ },
+ };
+ }
+
+ return {
+ ...item,
+ threadCreatedFromMessage: undefined,
+ reactions: {},
+ startsConversation: false,
+ messageInfo: {
+ ...item.messageInfo,
+ creator: {
+ ...item.messageInfo.creator,
+ isViewer: false,
+ },
+ },
+ };
+ }, [item]);
+
+ const onPress = React.useCallback(() => {
+ const createToggleMessagePinPromise = async () => {
+ invariant(messageInfo.id, 'messageInfo.id should be defined');
+ const result = await callToggleMessagePin({
+ messageID: messageInfo.id,
+ action: modalInfo.action,
+ });
+ return {
+ newMessageInfos: result.newMessageInfos,
+ threadID: result.threadID,
+ };
+ };
+
+ dispatchActionPromise(
+ toggleMessagePinActionTypes,
+ createToggleMessagePinPromise(),
+ );
+
+ navigation.goBack();
+ }, [
+ modalInfo,
+ callToggleMessagePin,
+ dispatchActionPromise,
+ messageInfo.id,
+ navigation,
+ ]);
+
+ const onCancel = React.useCallback(() => {
+ navigation.goBack();
+ }, [navigation]);
+
+ return (
+
+ {modalInfo.name}
+
+ {modalInfo.confirmationText}
+
+
+
+
+
+
+
+ );
+}
+
+const unboundStyles = {
+ modal: {
+ backgroundColor: 'modalForeground',
+ borderColor: 'modalForegroundBorder',
+ },
+ modalHeader: {
+ fontSize: 18,
+ color: 'modalForegroundLabel',
+ },
+ modalConfirmationText: {
+ fontSize: 12,
+ color: 'panelBackgroundLabel',
+ marginTop: 4,
+ },
+ buttonsContainer: {
+ flexDirection: 'column',
+ flex: 1,
+ justifyContent: 'flex-end',
+ marginBottom: 0,
+ height: 72,
+ paddingHorizontal: 16,
+ },
+ removePinButton: {
+ borderRadius: 5,
+ height: 48,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: 'vibrantRedButton',
+ },
+ pinButton: {
+ borderRadius: 5,
+ height: 48,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: 'purpleButton',
+ },
+ cancelButton: {
+ borderRadius: 5,
+ height: 48,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ textColor: {
+ color: 'white',
+ },
+};
+
+export default TogglePinModal;
diff --git a/native/navigation/app-navigator.react.js b/native/navigation/app-navigator.react.js
--- a/native/navigation/app-navigator.react.js
+++ b/native/navigation/app-navigator.react.js
@@ -26,11 +26,13 @@
CommunityDrawerNavigatorRouteName,
type ScreenParamList,
type OverlayParamList,
+ TogglePinModalRouteName,
} from './route-names.js';
import MultimediaMessageTooltipModal from '../chat/multimedia-message-tooltip-modal.react.js';
import RobotextMessageTooltipModal from '../chat/robotext-message-tooltip-modal.react.js';
import ThreadSettingsMemberTooltipModal from '../chat/settings/thread-settings-member-tooltip-modal.react.js';
import TextMessageTooltipModal from '../chat/text-message-tooltip-modal.react.js';
+import TogglePinModal from '../chat/toggle-pin-modal.react.js';
import KeyboardStateContainer from '../keyboard/keyboard-state-container.react.js';
import ChatCameraModal from '../media/chat-camera-modal.react.js';
import ImageModal from '../media/image-modal.react.js';
@@ -139,6 +141,7 @@
name={VideoPlaybackModalRouteName}
component={VideoPlaybackModal}
/>
+
{pushHandler}
diff --git a/native/navigation/route-names.js b/native/navigation/route-names.js
--- a/native/navigation/route-names.js
+++ b/native/navigation/route-names.js
@@ -22,6 +22,7 @@
import type { SidebarListModalParams } from '../chat/sidebar-list-modal.react.js';
import type { SubchannelListModalParams } from '../chat/subchannels-list-modal.react.js';
import type { TextMessageTooltipModalParams } from '../chat/text-message-tooltip-modal.react.js';
+import type { TogglePinModalParams } from '../chat/toggle-pin-modal.react.js';
import type { ChatCameraModalParams } from '../media/chat-camera-modal.react.js';
import type { ImageModalParams } from '../media/image-modal.react.js';
import type { VideoPlaybackModalParams } from '../media/video-playback-modal.react.js';
@@ -78,6 +79,7 @@
export const ThreadSettingsMemberTooltipModalRouteName =
'ThreadSettingsMemberTooltipModal';
export const ThreadSettingsRouteName = 'ThreadSettings';
+export const TogglePinModalRouteName = 'TogglePinModal';
export const VideoPlaybackModalRouteName = 'VideoPlaybackModal';
export const TermsAndPrivacyRouteName = 'TermsAndPrivacyModal';
export const RegistrationRouteName = 'Registration';
@@ -97,6 +99,7 @@
+SubchannelsListModal: SubchannelListModalParams,
+MessageReactionsModal: MessageReactionsModalParams,
+Registration: void,
+ +TogglePinModal: TogglePinModalParams,
};
export type MessageTooltipRouteNames =
@@ -118,6 +121,7 @@
+ActionResultModal: ActionResultModalParams,
+ChatCameraModal: ChatCameraModalParams,
+VideoPlaybackModal: VideoPlaybackModalParams,
+ +TogglePinModal: TogglePinModalParams,
...TooltipModalParamList,
};