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, @@ -32,6 +34,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; @@ -84,7 +87,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 @@ -23,6 +23,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'; @@ -80,6 +81,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'; @@ -100,6 +102,7 @@ +MessageReactionsModal: MessageReactionsModalParams, +Registration: void, +InviteLinkModal: InviteLinkModalParams, + +TogglePinModal: TogglePinModalParams, }; export type MessageTooltipRouteNames = @@ -121,6 +124,7 @@ +ActionResultModal: ActionResultModalParams, +ChatCameraModal: ChatCameraModalParams, +VideoPlaybackModal: VideoPlaybackModalParams, + +TogglePinModal: TogglePinModalParams, ...TooltipModalParamList, };