diff --git a/lib/utils/message-pinning-utils.js b/lib/utils/message-pinning-utils.js index 0383ee548..cab30ba76 100644 --- a/lib/utils/message-pinning-utils.js +++ b/lib/utils/message-pinning-utils.js @@ -1,32 +1,48 @@ // @flow import { isInvalidPinSourceForThread } from '../shared/message-utils.js'; -import { threadHasPermission } from '../shared/thread-utils.js'; +import { + threadHasPermission, + useThreadHasPermission, +} from '../shared/thread-utils.js'; import type { MessageInfo, RawMessageInfo } from '../types/message-types.js'; import type { ThreadInfo, RawThreadInfo, } from '../types/minimally-encoded-thread-permissions-types.js'; import { threadPermissions } from '../types/thread-permission-types.js'; import type { LegacyRawThreadInfo } from '../types/thread-types.js'; function canToggleMessagePin( messageInfo: RawMessageInfo | MessageInfo, threadInfo: LegacyRawThreadInfo | RawThreadInfo | ThreadInfo, ): boolean { const isValidMessage = !isInvalidPinSourceForThread(messageInfo, threadInfo); const hasManagePinsPermission = threadHasPermission( threadInfo, threadPermissions.MANAGE_PINS, ); return isValidMessage && hasManagePinsPermission; } +function useCanToggleMessagePin( + messageInfo: RawMessageInfo | MessageInfo, + threadInfo: ThreadInfo, +): boolean { + const isValidMessage = !isInvalidPinSourceForThread(messageInfo, threadInfo); + const hasManagePinsPermission = useThreadHasPermission( + threadInfo, + threadPermissions.MANAGE_PINS, + ); + + return isValidMessage && hasManagePinsPermission; +} + function pinnedMessageCountText(pinnedCount: number): string { const messageNoun = pinnedCount === 1 ? 'message' : 'messages'; return `${pinnedCount} pinned ${messageNoun}`; } -export { canToggleMessagePin, pinnedMessageCountText }; +export { canToggleMessagePin, useCanToggleMessagePin, pinnedMessageCountText }; diff --git a/native/chat/message.react.js b/native/chat/message.react.js index 2b748d36d..be1f1b4b4 100644 --- a/native/chat/message.react.js +++ b/native/chat/message.react.js @@ -1,162 +1,162 @@ // @flow import * as React from 'react'; import { LayoutAnimation, TouchableWithoutFeedback, PixelRatio, } from 'react-native'; import { messageKey } from 'lib/shared/message-utils.js'; -import { canToggleMessagePin } from 'lib/utils/message-pinning-utils.js'; +import { useCanToggleMessagePin } from 'lib/utils/message-pinning-utils.js'; import type { ChatNavigationProp } from './chat.react.js'; import MultimediaMessage from './multimedia-message.react.js'; import { RobotextMessage } from './robotext-message.react.js'; import { TextMessage } from './text-message.react.js'; import { messageItemHeight } from './utils.js'; import { KeyboardContext } from '../keyboard/keyboard-state.js'; import type { AppNavigationProp } from '../navigation/app-navigator.react'; import type { NavigationRoute } from '../navigation/route-names.js'; import type { ChatMessageInfoItemWithHeight } from '../types/chat-types.js'; import { type VerticalBounds } from '../types/layout-types.js'; import type { LayoutEvent } from '../types/react-native.js'; type Props = { +item: ChatMessageInfoItemWithHeight, +focused: boolean, +navigation: | ChatNavigationProp<'MessageList'> | AppNavigationProp<'TogglePinModal'> | ChatNavigationProp<'PinnedMessagesScreen'> | ChatNavigationProp<'MessageSearch'>, +route: | NavigationRoute<'MessageList'> | NavigationRoute<'TogglePinModal'> | NavigationRoute<'PinnedMessagesScreen'> | NavigationRoute<'MessageSearch'>, +toggleFocus: (messageKey: string) => void, +verticalBounds: ?VerticalBounds, shouldDisplayPinIndicator: boolean, }; function Message(props: Props): React.Node { const { focused, item, navigation, route, toggleFocus, verticalBounds, shouldDisplayPinIndicator, } = props; const focusedOrStartsConversation = focused || item.startsConversation; React.useEffect(() => { LayoutAnimation.easeInEaseOut(); }, [focusedOrStartsConversation]); const keyboardState = React.useContext(KeyboardContext); const dismissKeyboard = keyboardState?.dismissKeyboard; const onMessagePress = React.useCallback( () => dismissKeyboard?.(), [dismissKeyboard], ); const onLayout = React.useCallback( (event: LayoutEvent) => { if (focused) { return; } const measuredHeight = event.nativeEvent.layout.height; const expectedHeight = messageItemHeight(item); const pixelRatio = 1 / PixelRatio.get(); const distance = Math.abs(measuredHeight - expectedHeight); if (distance < pixelRatio) { return; } const approxMeasuredHeight = Math.round(measuredHeight * 100) / 100; const approxExpectedHeight = Math.round(expectedHeight * 100) / 100; console.log( `Message height for ${item.messageShapeType} ` + `${messageKey(item.messageInfo)} was expected to be ` + `${approxExpectedHeight} but is actually ${approxMeasuredHeight}. ` + "This means MessageList's FlatList isn't getting the right item " + 'height for some of its nodes, which is guaranteed to cause glitchy ' + 'behavior. Please investigate!!', ); }, [focused, item], ); - const canTogglePins = React.useMemo( - () => canToggleMessagePin(props.item.messageInfo, props.item.threadInfo), - [props.item.messageInfo, props.item.threadInfo], + const canTogglePins = useCanToggleMessagePin( + props.item.messageInfo, + props.item.threadInfo, ); const innerMessageNode = React.useMemo(() => { if (item.messageShapeType === 'text') { return ( ); } else if (item.messageShapeType === 'multimedia') { return ( ); } else { return ( ); } }, [ focused, item, navigation, route, shouldDisplayPinIndicator, toggleFocus, verticalBounds, canTogglePins, ]); const message = React.useMemo( () => ( {innerMessageNode} ), [innerMessageNode, onLayout, onMessagePress], ); return message; } export default Message;