diff --git a/native/chat/subchannel-item.react.js b/native/chat/subchannel-item.react.js new file mode 100644 index 000000000..a91a47a66 --- /dev/null +++ b/native/chat/subchannel-item.react.js @@ -0,0 +1,114 @@ +// @flow + +import * as React from 'react'; +import { Text, View } from 'react-native'; + +import type { ChatThreadItem } from 'lib/selectors/chat-selectors'; +import { shortAbsoluteDate } from 'lib/utils/date-utils'; + +import { SingleLine } from '../components/single-line.react'; +import SWMansionIcon from '../components/swmansion-icon.react'; +import { useStyles } from '../themes/colors'; +import MessagePreview from './message-preview.react'; + +type Props = { + +subchannelInfo: ChatThreadItem, +}; +function SubchannelItem(props: Props): React.Node { + const { + lastUpdatedTime, + threadInfo, + mostRecentMessageInfo, + } = props.subchannelInfo; + + const lastActivity = shortAbsoluteDate(lastUpdatedTime); + + const styles = useStyles(unboundStyles); + const unreadStyle = threadInfo.currentUser.unread ? styles.unread : null; + + const lastMessage = React.useMemo(() => { + if (!mostRecentMessageInfo) { + return ( + + No messages + + ); + } + return ( + + ); + }, [mostRecentMessageInfo, threadInfo, styles]); + + return ( + + + + + + + {threadInfo.uiName} + + + + {lastMessage} + {lastActivity} + + + ); +} + +const unboundStyles = { + outerView: { + flex: 1, + flexDirection: 'column', + justifyContent: 'center', + paddingVertical: 8, + paddingHorizontal: 16, + height: 60, + }, + itemRowContainer: { + flexDirection: 'row', + height: 24, + alignItems: 'center', + }, + unread: { + color: 'listForegroundLabel', + fontWeight: 'bold', + }, + name: { + color: 'listForegroundSecondaryLabel', + flex: 1, + fontSize: 16, + paddingLeft: 3, + paddingBottom: 2, + }, + lastActivity: { + color: 'listForegroundTertiaryLabel', + fontSize: 14, + marginLeft: 10, + }, + iconWrapper: { + marginRight: 8, + alignItems: 'center', + }, + icon: { + fontSize: 22, + color: 'listForegroundSecondaryLabel', + alignItems: 'center', + height: 24, + }, + noMessages: { + color: 'listForegroundTertiaryLabel', + flex: 1, + fontSize: 14, + fontStyle: 'italic', + }, +}; + +export default SubchannelItem; diff --git a/native/chat/subchannels-list-modal.react.js b/native/chat/subchannels-list-modal.react.js new file mode 100644 index 000000000..6b8871247 --- /dev/null +++ b/native/chat/subchannels-list-modal.react.js @@ -0,0 +1,100 @@ +// @flow + +import * as React from 'react'; +import { View } from 'react-native'; + +import { useSearchSubchannels } from 'lib/hooks/search-threads'; +import type { ChatThreadItem } from 'lib/selectors/chat-selectors'; +import { type ThreadInfo } from 'lib/types/thread-types'; + +import Button from '../components/button.react'; +import type { RootNavigationProp } from '../navigation/root-navigator.react'; +import type { NavigationRoute } from '../navigation/route-names'; +import { useColors, useStyles } from '../themes/colors'; +import SubchannelItem from './subchannel-item.react'; +import ThreadListModal from './thread-list-modal.react'; + +export type SubchannelListModalParams = { + +threadInfo: ThreadInfo, +}; + +type Props = { + +navigation: RootNavigationProp<'SubchannelsListModal'>, + +route: NavigationRoute<'SubchannelsListModal'>, +}; +function SubchannelListModal(props: Props): React.Node { + const { + listData, + searchState, + setSearchState, + onChangeSearchInputText, + } = useSearchSubchannels(props.route.params.threadInfo); + + return ( + + ); +} + +const createRenderItem = ( + onPressItem: (threadInfo: ThreadInfo) => void, + // eslint-disable-next-line react/display-name +) => (row: { +item: ChatThreadItem, +index: number, ... }) => { + return ; +}; + +function Item(props: { + onPressItem: (threadInfo: ThreadInfo) => void, + subchannelInfo: ChatThreadItem, +}): React.Node { + const { onPressItem, subchannelInfo } = props; + const { threadInfo } = subchannelInfo; + + const onPressButton = React.useCallback(() => onPressItem(threadInfo), [ + onPressItem, + threadInfo, + ]); + + const colors = useColors(); + const styles = useStyles(unboundStyles); + + return ( + + ); +} + +const unboundStyles = { + subchannel: { + backgroundColor: 'listBackground', + paddingLeft: 0, + paddingRight: 5, + }, + subchannelItemContainer: { + flex: 1, + }, + subchannelRowContainer: { + flex: 1, + flexDirection: 'row', + }, +}; + +export default SubchannelListModal; diff --git a/native/navigation/root-navigator.react.js b/native/navigation/root-navigator.react.js index 951d8851b..80b2afad1 100644 --- a/native/navigation/root-navigator.react.js +++ b/native/navigation/root-navigator.react.js @@ -1,234 +1,241 @@ // @flow import { createNavigatorFactory, useNavigationBuilder, type StackNavigationState, type StackOptions, type StackNavigationEventMap, type StackNavigatorProps, type ExtraStackNavigatorProps, type ParamListBase, type StackNavigationHelpers, type StackNavigationProp, } from '@react-navigation/native'; import { StackView, TransitionPresets } from '@react-navigation/stack'; import * as React from 'react'; import { Platform } from 'react-native'; import { enableScreens } from 'react-native-screens'; import LoggedOutModal from '../account/logged-out-modal.react'; import TermsAndPrivacyModal from '../account/terms-and-privacy-modal.react'; import ThreadPickerModal from '../calendar/thread-picker-modal.react'; import ImagePasteModal from '../chat/image-paste-modal.react'; import AddUsersModal from '../chat/settings/add-users-modal.react'; import ColorSelectorModal from '../chat/settings/color-selector-modal.react'; import ComposeSubchannelModal from '../chat/settings/compose-subchannel-modal.react'; import SidebarListModal from '../chat/sidebar-list-modal.react'; +import SubchannelsListModal from '../chat/subchannels-list-modal.react'; import CustomServerModal from '../profile/custom-server-modal.react'; import AppNavigator from './app-navigator.react'; import { defaultStackScreenOptions } from './options'; import { RootNavigatorContext } from './root-navigator-context'; import RootRouter, { type RootRouterExtraNavigationHelpers, } from './root-router'; import { LoggedOutModalRouteName, AppRouteName, ThreadPickerModalRouteName, ImagePasteModalRouteName, AddUsersModalRouteName, CustomServerModalRouteName, ColorSelectorModalRouteName, ComposeSubchannelModalRouteName, SidebarListModalRouteName, + SubchannelsListModalRouteName, type ScreenParamList, type RootParamList, TermsAndPrivacyRouteName, } from './route-names'; enableScreens(); export type RootNavigationHelpers = { ...$Exact>, ...RootRouterExtraNavigationHelpers, ... }; type RootNavigatorProps = StackNavigatorProps>; function RootNavigator({ initialRouteName, children, screenOptions, defaultScreenOptions, screenListeners, id, ...rest }: RootNavigatorProps) { const [keyboardHandlingEnabled, setKeyboardHandlingEnabled] = React.useState( true, ); const mergedScreenOptions = React.useMemo(() => { if (typeof screenOptions === 'function') { return input => ({ ...screenOptions(input), keyboardHandlingEnabled, }); } return { ...screenOptions, keyboardHandlingEnabled, }; }, [screenOptions, keyboardHandlingEnabled]); const { state, descriptors, navigation } = useNavigationBuilder(RootRouter, { id, initialRouteName, children, screenOptions: mergedScreenOptions, defaultScreenOptions, screenListeners, }); const rootNavigationContext = React.useMemo( () => ({ setKeyboardHandlingEnabled }), [setKeyboardHandlingEnabled], ); return ( ); } const createRootNavigator = createNavigatorFactory< StackNavigationState, StackOptions, StackNavigationEventMap, RootNavigationHelpers<>, ExtraStackNavigatorProps, >(RootNavigator); const baseTransitionPreset = Platform.select({ ios: TransitionPresets.ModalSlideFromBottomIOS, default: TransitionPresets.FadeFromBottomAndroid, }); const transitionPreset = { ...baseTransitionPreset, cardStyleInterpolator: interpolatorProps => { const baseCardStyleInterpolator = baseTransitionPreset.cardStyleInterpolator( interpolatorProps, ); const overlayOpacity = interpolatorProps.current.progress.interpolate({ inputRange: [0, 1], outputRange: ([0, 0.7]: number[]), // Flow... extrapolate: 'clamp', }); return { ...baseCardStyleInterpolator, overlayStyle: [ baseCardStyleInterpolator.overlayStyle, { opacity: overlayOpacity }, ], }; }, }; const defaultScreenOptions = { ...defaultStackScreenOptions, ...transitionPreset, cardStyle: { backgroundColor: 'transparent' }, presentation: 'modal', headerShown: false, }; const disableGesturesScreenOptions = { gestureEnabled: false, }; const modalOverlayScreenOptions = { cardOverlayEnabled: true, presentation: 'transparentModal', }; const termsAndPrivacyModalScreenOptions = { gestureEnabled: false, cardOverlayEnabled: true, }; export type RootRouterNavigationProp< ParamList: ParamListBase = ParamListBase, RouteName: $Keys = $Keys, > = { ...StackNavigationProp, ...RootRouterExtraNavigationHelpers, }; export type RootNavigationProp< RouteName: $Keys = $Keys, > = { ...StackNavigationProp, ...RootRouterExtraNavigationHelpers, }; const Root = createRootNavigator< ScreenParamList, RootParamList, RootNavigationHelpers, >(); function RootComponent(): React.Node { return ( + ); } export default RootComponent; diff --git a/native/navigation/route-names.js b/native/navigation/route-names.js index 8a3c80d4c..70f69d202 100644 --- a/native/navigation/route-names.js +++ b/native/navigation/route-names.js @@ -1,181 +1,184 @@ // @flow import type { RouteProp } from '@react-navigation/native'; import type { TermsAndPrivacyModalParams } from '../account/terms-and-privacy-modal.react'; import type { ThreadPickerModalParams } from '../calendar/thread-picker-modal.react'; import type { ComposeSubchannelParams } from '../chat/compose-subchannel.react'; import type { ImagePasteModalParams } from '../chat/image-paste-modal.react'; import type { MessageListParams } from '../chat/message-list-types'; import type { MultimediaMessageTooltipModalParams } from '../chat/multimedia-message-tooltip-modal.react'; import type { RobotextMessageTooltipModalParams } from '../chat/robotext-message-tooltip-modal.react'; import type { AddUsersModalParams } from '../chat/settings/add-users-modal.react'; import type { ColorSelectorModalParams } from '../chat/settings/color-selector-modal.react'; import type { ComposeSubchannelModalParams } from '../chat/settings/compose-subchannel-modal.react'; import type { DeleteThreadParams } from '../chat/settings/delete-thread.react'; import type { ThreadSettingsMemberTooltipModalParams } from '../chat/settings/thread-settings-member-tooltip-modal.react'; import type { ThreadSettingsParams } from '../chat/settings/thread-settings.react'; import type { SidebarListModalParams } from '../chat/sidebar-list-modal.react'; +import type { SubchannelListModalParams } from '../chat/subchannels-list-modal.react'; import type { TextMessageTooltipModalParams } from '../chat/text-message-tooltip-modal.react'; import type { CameraModalParams } from '../media/camera-modal.react'; import type { ImageModalParams } from '../media/image-modal.react'; import type { VideoPlaybackModalParams } from '../media/video-playback-modal.react'; import type { CustomServerModalParams } from '../profile/custom-server-modal.react'; import type { RelationshipListItemTooltipModalParams } from '../profile/relationship-list-item-tooltip-modal.react'; import type { ActionResultModalParams } from './action-result-modal.react'; export const ActionResultModalRouteName = 'ActionResultModal'; export const AddUsersModalRouteName = 'AddUsersModal'; export const AppearancePreferencesRouteName = 'AppearancePreferences'; export const AppRouteName = 'App'; export const AppsRouteName = 'Apps'; export const BackgroundChatThreadListRouteName = 'BackgroundChatThreadList'; export const BlockListRouteName = 'BlockList'; export const BuildInfoRouteName = 'BuildInfo'; export const CalendarRouteName = 'Calendar'; export const CameraModalRouteName = 'CameraModal'; export const ChatRouteName = 'Chat'; export const ChatThreadListRouteName = 'ChatThreadList'; export const ColorSelectorModalRouteName = 'ColorSelectorModal'; export const ComposeSubchannelModalRouteName = 'ComposeSubchannelModal'; export const ComposeSubchannelRouteName = 'ComposeSubchannel'; export const CommunityDrawerNavigatorRouteName = 'CommunityDrawerNavigator'; export const CustomServerModalRouteName = 'CustomServerModal'; export const DefaultNotificationsPreferencesRouteName = 'DefaultNotifications'; export const DeleteAccountRouteName = 'DeleteAccount'; export const DeleteThreadRouteName = 'DeleteThread'; export const DevToolsRouteName = 'DevTools'; export const EditPasswordRouteName = 'EditPassword'; export const FriendListRouteName = 'FriendList'; export const HomeChatThreadListRouteName = 'HomeChatThreadList'; export const ImageModalRouteName = 'ImageModal'; export const ImagePasteModalRouteName = 'ImagePasteModal'; export const LoggedOutModalRouteName = 'LoggedOutModal'; export const MessageListRouteName = 'MessageList'; export const MultimediaMessageTooltipModalRouteName = 'MultimediaMessageTooltipModal'; export const PrivacyPreferencesRouteName = 'PrivacyPreferences'; export const ProfileRouteName = 'Profile'; export const ProfileScreenRouteName = 'ProfileScreen'; export const RelationshipListItemTooltipModalRouteName = 'RelationshipListItemTooltipModal'; export const RobotextMessageTooltipModalRouteName = 'RobotextMessageTooltipModal'; export const SidebarListModalRouteName = 'SidebarListModal'; +export const SubchannelsListModalRouteName = 'SubchannelsListModal'; export const TabNavigatorRouteName = 'TabNavigator'; export const TextMessageTooltipModalRouteName = 'TextMessageTooltipModal'; export const ThreadPickerModalRouteName = 'ThreadPickerModal'; export const ThreadSettingsMemberTooltipModalRouteName = 'ThreadSettingsMemberTooltipModal'; export const ThreadSettingsRouteName = 'ThreadSettings'; export const VideoPlaybackModalRouteName = 'VideoPlaybackModal'; export const TermsAndPrivacyRouteName = 'TermsAndPrivacyModal'; export type RootParamList = { +LoggedOutModal: void, +App: void, +ThreadPickerModal: ThreadPickerModalParams, +AddUsersModal: AddUsersModalParams, +CustomServerModal: CustomServerModalParams, +ColorSelectorModal: ColorSelectorModalParams, +ComposeSubchannelModal: ComposeSubchannelModalParams, +SidebarListModal: SidebarListModalParams, +ImagePasteModal: ImagePasteModalParams, +TermsAndPrivacyModal: TermsAndPrivacyModalParams, + +SubchannelsListModal: SubchannelListModalParams, }; export type MessageTooltipRouteNames = | typeof RobotextMessageTooltipModalRouteName | typeof MultimediaMessageTooltipModalRouteName | typeof TextMessageTooltipModalRouteName; export type TooltipModalParamList = { +MultimediaMessageTooltipModal: MultimediaMessageTooltipModalParams, +TextMessageTooltipModal: TextMessageTooltipModalParams, +ThreadSettingsMemberTooltipModal: ThreadSettingsMemberTooltipModalParams, +RelationshipListItemTooltipModal: RelationshipListItemTooltipModalParams, +RobotextMessageTooltipModal: RobotextMessageTooltipModalParams, }; export type OverlayParamList = { +CommunityDrawerNavigator: void, +ImageModal: ImageModalParams, +ActionResultModal: ActionResultModalParams, +CameraModal: CameraModalParams, +VideoPlaybackModal: VideoPlaybackModalParams, ...TooltipModalParamList, }; export type TabParamList = { +Calendar: void, +Chat: void, +Profile: void, +Apps: void, }; export type ChatParamList = { +ChatThreadList: void, +MessageList: MessageListParams, +ComposeSubchannel: ComposeSubchannelParams, +ThreadSettings: ThreadSettingsParams, +DeleteThread: DeleteThreadParams, }; export type ChatTopTabsParamList = { +HomeChatThreadList: void, +BackgroundChatThreadList: void, }; export type ProfileParamList = { +ProfileScreen: void, +EditPassword: void, +DeleteAccount: void, +BuildInfo: void, +DevTools: void, +AppearancePreferences: void, +PrivacyPreferences: void, +DefaultNotifications: void, +FriendList: void, +BlockList: void, }; export type CommunityDrawerParamList = { +TabNavigator: void }; export type ScreenParamList = { ...RootParamList, ...OverlayParamList, ...TabParamList, ...ChatParamList, ...ChatTopTabsParamList, ...ProfileParamList, ...CommunityDrawerParamList, }; export type NavigationRoute< RouteName: string = $Keys, > = RouteProp; export const accountModals = [LoggedOutModalRouteName]; export const scrollBlockingModals = [ ImageModalRouteName, MultimediaMessageTooltipModalRouteName, TextMessageTooltipModalRouteName, ThreadSettingsMemberTooltipModalRouteName, RelationshipListItemTooltipModalRouteName, RobotextMessageTooltipModalRouteName, VideoPlaybackModalRouteName, ]; export const chatRootModals = [ AddUsersModalRouteName, ColorSelectorModalRouteName, ComposeSubchannelModalRouteName, ]; export const threadRoutes = [ MessageListRouteName, ThreadSettingsRouteName, DeleteThreadRouteName, ComposeSubchannelRouteName, ];