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,
];