diff --git a/native/navigation/community-drawer-content.react.js b/native/navigation/community-drawer-content.react.js
index 8feef8d59..4219eea7e 100644
--- a/native/navigation/community-drawer-content.react.js
+++ b/native/navigation/community-drawer-content.react.js
@@ -1,128 +1,130 @@
// @flow
import * as React from 'react';
-import { View, FlatList } from 'react-native';
+import { FlatList } from 'react-native';
+import { SafeAreaView } from 'react-native-safe-area-context';
import { useSelector } from 'react-redux';
import {
childThreadInfos,
communityThreadSelector,
} from 'lib/selectors/thread-selectors';
import { type ThreadInfo, communitySubthreads } from 'lib/types/thread-types';
import { useNavigateToThread } from '../chat/message-list-types';
import { useStyles } from '../themes/colors';
import CommunityDrawerItemCommunity from './community-drawer-item-cummunity.react';
const maxDepth = 2;
+const safeAreaEdges = ['top'];
function CommunityDrawerContent(): React.Node {
const communities = useSelector(communityThreadSelector);
const communitiesSuffixed = React.useMemo(() => appendSuffix(communities), [
communities,
]);
const styles = useStyles(unboundStyles);
const [openCommunity, setOpenCommunity] = React.useState(
communitiesSuffixed.length === 1 ? communitiesSuffixed[0].id : null,
);
const navigateToThread = useNavigateToThread();
const childThreadInfosMap = useSelector(childThreadInfos);
const setOpenCommunnityOrClose = React.useCallback((index: string) => {
setOpenCommunity(open => (open === index ? null : index));
}, []);
const renderItem = React.useCallback(
({ item }) => {
const itemData = {
threadInfo: item.threadInfo,
itemChildren: item.itemChildren,
};
return (
);
},
[navigateToThread, openCommunity, setOpenCommunnityOrClose],
);
const drawerItemsData = React.useMemo(
() =>
createRecursiveDrawerItemsData(childThreadInfosMap, communitiesSuffixed),
[childThreadInfosMap, communitiesSuffixed],
);
return (
-
+
-
+
);
}
function createRecursiveDrawerItemsData(
childThreadInfosMap: { +[id: string]: $ReadOnlyArray },
communities: $ReadOnlyArray,
) {
const result = communities.map(community => ({
key: community.id,
threadInfo: community,
itemChildren: [],
}));
let queue = result.map(item => [item, 0]);
for (let i = 0; i < queue.length; i++) {
const [item, lvl] = queue[i];
const itemChildThreadInfos = childThreadInfosMap[item.threadInfo.id] ?? [];
if (lvl < maxDepth) {
item.itemChildren = itemChildThreadInfos
.filter(childItem => communitySubthreads.includes(childItem.type))
.map(childItem => ({
threadInfo: childItem,
itemChildren: [],
}));
queue = queue.concat(
item.itemChildren.map(childItem => [childItem, lvl + 1]),
);
}
}
return result;
}
function appendSuffix(chats: $ReadOnlyArray): ThreadInfo[] {
const result = [];
const names = new Map();
for (const chat of chats) {
let name = chat.uiName;
const numberOfOccurrences = names.get(name);
names.set(name, (numberOfOccurrences ?? 0) + 1);
if (numberOfOccurrences) {
name = `${name} (${numberOfOccurrences.toString()})`;
}
result.push({ ...chat, uiName: name });
}
return result;
}
const unboundStyles = {
drawerContent: {
flex: 1,
paddingRight: 8,
- paddingTop: 52,
+ paddingTop: 8,
backgroundColor: 'drawerBackgroud',
},
};
const MemoizedCommunityDrawerContent: React.ComponentType<{}> = React.memo(
CommunityDrawerContent,
);
export default MemoizedCommunityDrawerContent;
diff --git a/native/navigation/community-drawer-item.react.js b/native/navigation/community-drawer-item.react.js
index f0af969d5..63ea5023c 100644
--- a/native/navigation/community-drawer-item.react.js
+++ b/native/navigation/community-drawer-item.react.js
@@ -1,129 +1,137 @@
// @flow
import * as React from 'react';
-import { View, Text, FlatList, TouchableOpacity } from 'react-native';
+import { View, FlatList, TouchableOpacity } from 'react-native';
import type { ThreadInfo } from 'lib/types/thread-types';
import type { MessageListParams } from '../chat/message-list-types';
+import { SingleLine } from '../components/single-line.react';
import { useStyles } from '../themes/colors';
import { ExpandButton, ExpandButtonDisabled } from './expand-buttons.react';
export type CommunityDrawerItemData = {
+threadInfo: ThreadInfo,
+itemChildren?: $ReadOnlyArray,
};
export type DrawerItemProps = {
+itemData: CommunityDrawerItemData,
+toggleExpanded: (threadID: string) => void,
+expanded: boolean,
+navigateToThread: (params: MessageListParams) => void,
};
function CommunityDrawerItem(props: DrawerItemProps): React.Node {
const {
itemData: { threadInfo, itemChildren },
navigateToThread,
expanded,
toggleExpanded,
} = props;
const styles = useStyles(unboundStyles);
const renderItem = React.useCallback(
({ item }) => (
),
[navigateToThread],
);
const children = React.useMemo(() => {
if (!expanded) {
return null;
}
return ;
}, [expanded, itemChildren, renderItem]);
const onExpandToggled = React.useCallback(() => {
toggleExpanded(threadInfo.id);
}, [toggleExpanded, threadInfo.id]);
const itemExpandButton = React.useMemo(() => {
if (!itemChildren?.length) {
return ;
}
return ;
}, [itemChildren?.length, expanded, onExpandToggled]);
const onPress = React.useCallback(() => {
navigateToThread({ threadInfo });
}, [navigateToThread, threadInfo]);
return (
{itemExpandButton}
-
- {threadInfo.uiName}
+
+ {threadInfo.uiName}
{children}
);
}
const unboundStyles = {
chatView: {
marginLeft: 16,
marginVertical: 6,
- overflow: 'hidden',
},
threadEntry: {
flexDirection: 'row',
},
title: {
color: 'drawerItemLabel',
fontSize: 16,
lineHeight: 24,
+ fontWeight: '500',
+ },
+ textTouchableWrapper: {
+ flex: 1,
},
};
export type CommunityDrawerItemChatProps = {
+itemData: CommunityDrawerItemData,
+navigateToThread: (params: MessageListParams) => void,
};
function CommunityDrawerItemChat(
props: CommunityDrawerItemChatProps,
): React.Node {
const [expanded, setExpanded] = React.useState(false);
const styles = useStyles(unboundStyles);
const toggleExpanded = React.useCallback(() => {
setExpanded(isExpanded => !isExpanded);
}, []);
return (
);
}
const MemoizedCommunityDrawerItemChat: React.ComponentType = React.memo(
CommunityDrawerItemChat,
);
const MemoizedCommunityDrawerItem: React.ComponentType = React.memo(
CommunityDrawerItem,
);
export default MemoizedCommunityDrawerItem;
diff --git a/native/navigation/community-drawer-navigator.react.js b/native/navigation/community-drawer-navigator.react.js
index f18b15fca..5cf1a21b6 100644
--- a/native/navigation/community-drawer-navigator.react.js
+++ b/native/navigation/community-drawer-navigator.react.js
@@ -1,72 +1,72 @@
// @flow
import {
createDrawerNavigator,
type DrawerNavigationHelpers,
type DrawerNavigationProp,
} from '@react-navigation/drawer';
import * as React from 'react';
-import { Dimensions, View } from 'react-native';
+import { View } from 'react-native';
import { useStyles } from '../themes/colors';
import type { AppNavigationProp } from './app-navigator.react';
import CommunityDrawerContent from './community-drawer-content.react';
import { TabNavigatorRouteName } from './route-names';
import type {
NavigationRoute,
ScreenParamList,
CommunityDrawerParamList,
} from './route-names';
import TabNavigator from './tab-navigator.react';
const communityDrawerContent = () => ;
export type CommunityDrawerNavigationProp<
RouteName: $Keys = $Keys,
> = DrawerNavigationProp;
const Drawer = createDrawerNavigator<
ScreenParamList,
CommunityDrawerParamList,
DrawerNavigationHelpers,
>();
type Props = {
+navigation: AppNavigationProp<'CommunityDrawerNavigator'>,
+route: NavigationRoute<'CommunityDrawerNavigator'>,
};
// eslint-disable-next-line no-unused-vars
function CommunityDrawerNavigator(props: Props): React.Node {
const styles = useStyles(unboundStyles);
const screenOptions = React.useMemo(
() => ({
drawerStyle: styles.drawerStyle,
headerShown: false,
}),
[styles.drawerStyle],
);
return (
);
}
const unboundStyles = {
drawerView: {
flex: 1,
},
drawerStyle: {
- width: Dimensions.get('window').width - 36,
+ width: '80%',
},
};
export { CommunityDrawerNavigator };
diff --git a/native/navigation/expand-buttons.react.js b/native/navigation/expand-buttons.react.js
index ee32f7277..4c1460c06 100644
--- a/native/navigation/expand-buttons.react.js
+++ b/native/navigation/expand-buttons.react.js
@@ -1,56 +1,74 @@
// @flow
import Icon from '@expo/vector-icons/FontAwesome';
import * as React from 'react';
-import { TouchableOpacity } from 'react-native';
+import { TouchableOpacity, View } from 'react-native';
import { useStyles } from '../themes/colors';
-const ICON_SIZE = 20;
-const PADDING_HORIZONTAL = 7.5;
+const iconSize = 12;
+const buttonSize = 24;
+const hitSlopValue = 6;
+const padding = (buttonSize - iconSize) / 2;
+
+const hitSlop = {
+ bottom: hitSlopValue,
+ left: hitSlopValue,
+ right: hitSlopValue,
+ top: hitSlopValue,
+};
type Props = {
+onPress: () => void,
+expanded: boolean,
};
function ExpandButton(props: Props): React.Node {
const styles = useStyles(unboundStyles);
const style = props.expanded
? styles.expandButtonExpanded
: styles.expandButton;
const icon = props.expanded ? 'caret-down' : 'caret-right';
+
return (
-
-
+
+
);
}
function ExpandButtonDisabled(): React.Node {
const styles = useStyles(unboundStyles);
return (
-
+
+
+
);
}
const unboundStyles = {
expandButton: {
color: 'drawerExpandButton',
- paddingHorizontal: PADDING_HORIZONTAL,
},
expandButtonDisabled: {
color: 'drawerExpandButtonDisabled',
- paddingHorizontal: PADDING_HORIZONTAL,
},
expandButtonExpanded: {
color: 'drawerExpandButton',
- paddingHorizontal: PADDING_HORIZONTAL,
+ },
+ wrapper: {
+ width: buttonSize,
+ alignItems: 'center',
+ padding: padding,
},
};
export { ExpandButton, ExpandButtonDisabled };
diff --git a/native/themes/colors.js b/native/themes/colors.js
index 877ebf6e4..d48380804 100644
--- a/native/themes/colors.js
+++ b/native/themes/colors.js
@@ -1,316 +1,316 @@
// @flow
import * as React from 'react';
import { StyleSheet } from 'react-native';
import { createSelector } from 'reselect';
import { selectBackgroundIsDark } from '../navigation/nav-selectors';
import { NavContext } from '../navigation/navigation-context';
import { useSelector } from '../redux/redux-utils';
import type { AppState } from '../redux/state-types';
import type { GlobalTheme } from '../types/themes';
const light = Object.freeze({
blockQuoteBackground: '#D3D3D3',
blockQuoteBorder: '#C0C0C0',
codeBackground: '#DCDCDC',
disabledButton: '#D3D3D3',
disconnectedBarBackground: '#F5F5F5',
editButton: '#A4A4A2',
floatingButtonBackground: '#999999',
floatingButtonLabel: '#EEEEEE',
greenButton: '#6EC472',
greenText: 'green',
headerChevron: '#0A0A0A',
inlineSidebarBackground: '#E0E0E0',
inlineSidebarLabel: '#000000',
link: '#036AFF',
listBackground: 'white',
listBackgroundLabel: 'black',
listBackgroundSecondaryLabel: '#444444',
listBackgroundTernaryLabel: '#999999',
listChatBubble: '#F1F0F5',
listForegroundLabel: 'black',
listForegroundQuaternaryLabel: '#AAAAAA',
listForegroundSecondaryLabel: '#333333',
listForegroundTertiaryLabel: '#666666',
listInputBackground: '#F5F5F5',
listInputBar: '#E2E2E2',
listInputBorder: '#AAAAAAAA',
listInputButton: '#8E8D92',
listIosHighlightUnderlay: '#DDDDDDDD',
listSearchBackground: '#F5F5F5',
listSearchIcon: '#8E8D92',
listSeparator: '#EEEEEE',
listSeparatorLabel: '#555555',
mintButton: '#44CC99',
modalBackground: '#EEEEEE',
modalBackgroundLabel: '#333333',
modalBackgroundSecondaryLabel: '#AAAAAA',
modalButton: '#BBBBBB',
modalButtonLabel: 'black',
modalContrastBackground: 'black',
modalContrastForegroundLabel: 'white',
modalContrastOpacity: 0.7,
modalForeground: 'white',
modalForegroundBorder: '#CCCCCC',
modalForegroundLabel: 'black',
modalForegroundSecondaryLabel: '#888888',
modalForegroundTertiaryLabel: '#AAAAAA',
modalIosHighlightUnderlay: '#CCCCCCDD',
modalSubtext: '#CCCCCC',
modalSubtextLabel: '#555555',
navigationCard: '#FFFFFF',
navigationChevron: '#BAB9BE',
panelBackground: '#F5F5F5',
panelBackgroundLabel: '#888888',
panelForeground: 'white',
panelForegroundBorder: '#CCCCCC',
panelForegroundLabel: 'black',
panelForegroundSecondaryLabel: '#333333',
panelForegroundTertiaryLabel: '#888888',
panelIosHighlightUnderlay: '#EEEEEEDD',
panelSecondaryForeground: '#F5F5F5',
panelSecondaryForegroundBorder: '#D1D1D6',
purpleLink: '#7E57C2',
purpleButton: '#7E57C2',
redButton: '#BB8888',
redText: '#FF4444',
spoiler: '#33332C',
tabBarAccent: '#7E57C2',
tabBarBackground: '#F5F5F5',
tabBarActiveTintColor: '#7E57C2',
vibrantGreenButton: '#00C853',
vibrantRedButton: '#F53100',
tooltipBackground: '#E0E0E0',
logInSpacer: '#FFFFFF33',
logInText: 'white',
siweButton: 'white',
siweButtonText: '#1F1F1F',
drawerExpandButton: '#808080',
- drawerExpandButtonDisabled: '#404040',
- drawerItemLabel: '#CCCCCC',
- drawerOpenCommunityBackground: '#333333',
- drawerBackgroud: '#404040',
+ drawerExpandButtonDisabled: '#CCCCCC',
+ drawerItemLabel: '#0A0A0A',
+ drawerOpenCommunityBackground: '#F5F5F5',
+ drawerBackgroud: '#FFFFFF',
});
export type Colors = $Exact;
const dark: Colors = Object.freeze({
blockQuoteBackground: '#A9A9A9',
blockQuoteBorder: '#808080',
codeBackground: '#0A0A0A',
disabledButton: '#444444',
disconnectedBarBackground: '#1D1D1D',
editButton: '#5B5B5D',
floatingButtonBackground: '#666666',
floatingButtonLabel: 'white',
greenButton: '#43A047',
greenText: '#44FF44',
headerChevron: '#FFFFFF',
inlineSidebarBackground: '#666666',
inlineSidebarLabel: '#FFFFFF',
link: '#129AFF',
listBackground: '#0A0A0A',
listBackgroundLabel: '#C7C7CC',
listBackgroundSecondaryLabel: '#BBBBBB',
listBackgroundTernaryLabel: '#888888',
listChatBubble: '#26252A',
listForegroundLabel: 'white',
listForegroundQuaternaryLabel: '#555555',
listForegroundSecondaryLabel: '#CCCCCC',
listForegroundTertiaryLabel: '#999999',
listInputBackground: '#1D1D1D',
listInputBar: '#555555',
listInputBorder: '#333333',
listInputButton: '#AAAAAA',
listIosHighlightUnderlay: '#BBBBBB88',
listSearchBackground: '#1D1D1D',
listSearchIcon: '#AAAAAA',
listSeparator: '#3A3A3C',
listSeparatorLabel: '#EEEEEE',
mintButton: '#44CC99',
modalBackground: '#0A0A0A',
modalBackgroundLabel: '#CCCCCC',
modalBackgroundSecondaryLabel: '#555555',
modalButton: '#666666',
modalButtonLabel: 'white',
modalContrastBackground: 'white',
modalContrastForegroundLabel: 'black',
modalContrastOpacity: 0.85,
modalForeground: '#1C1C1E',
modalForegroundBorder: '#1C1C1E',
modalForegroundLabel: 'white',
modalForegroundSecondaryLabel: '#AAAAAA',
modalForegroundTertiaryLabel: '#666666',
modalIosHighlightUnderlay: '#AAAAAA88',
modalSubtext: '#444444',
modalSubtextLabel: '#AAAAAA',
navigationCard: '#2A2A2A',
navigationChevron: '#5B5B5D',
panelBackground: '#0A0A0A',
panelBackgroundLabel: '#C7C7CC',
panelForeground: '#1D1D1D',
panelForegroundBorder: '#2C2C2E',
panelForegroundLabel: 'white',
panelForegroundSecondaryLabel: '#CCCCCC',
panelForegroundTertiaryLabel: '#AAAAAA',
panelIosHighlightUnderlay: '#313035',
panelSecondaryForeground: '#333333',
panelSecondaryForegroundBorder: '#666666',
purpleLink: '#AE94DB',
purpleButton: '#7E57C2',
redButton: '#FF4444',
redText: '#FF4444',
spoiler: '#33332C',
tabBarAccent: '#AE94DB',
tabBarBackground: '#0A0A0A',
tabBarActiveTintColor: '#AE94DB',
vibrantGreenButton: '#00C853',
vibrantRedButton: '#F53100',
tooltipBackground: '#1F1F1F',
logInSpacer: '#FFFFFF33',
logInText: 'white',
siweButton: 'white',
siweButtonText: '#1F1F1F',
drawerExpandButton: '#808080',
drawerExpandButtonDisabled: '#404040',
drawerItemLabel: '#CCCCCC',
- drawerOpenCommunityBackground: '#333333',
- drawerBackgroud: '#404040',
+ drawerOpenCommunityBackground: '#191919',
+ drawerBackgroud: '#1F1F1F',
});
const colors = { light, dark };
const colorsSelector: (state: AppState) => Colors = createSelector(
(state: AppState) => state.globalThemeInfo.activeTheme,
(theme: ?GlobalTheme) => {
const explicitTheme = theme ? theme : 'light';
return colors[explicitTheme];
},
);
const magicStrings = new Set();
for (const theme in colors) {
for (const magicString in colors[theme]) {
magicStrings.add(magicString);
}
}
type Styles = { [name: string]: { [field: string]: mixed } };
type ReplaceField = (input: any) => any;
export type StyleSheetOf = $ObjMap;
function stylesFromColors(
obj: IS,
themeColors: Colors,
): StyleSheetOf {
const result = {};
for (const key in obj) {
const style = obj[key];
const filledInStyle = { ...style };
for (const styleKey in style) {
const styleValue = style[styleKey];
if (typeof styleValue !== 'string') {
continue;
}
if (magicStrings.has(styleValue)) {
const mapped = themeColors[styleValue];
if (mapped) {
filledInStyle[styleKey] = mapped;
}
}
}
result[key] = filledInStyle;
}
return StyleSheet.create(result);
}
function styleSelector(
obj: IS,
): (state: AppState) => StyleSheetOf {
return createSelector(colorsSelector, (themeColors: Colors) =>
stylesFromColors(obj, themeColors),
);
}
function useStyles(obj: IS): StyleSheetOf {
const ourColors = useColors();
return React.useMemo(() => stylesFromColors(obj, ourColors), [
obj,
ourColors,
]);
}
function useOverlayStyles(obj: IS): StyleSheetOf {
const navContext = React.useContext(NavContext);
const navigationState = navContext && navContext.state;
const theme = useSelector(
(state: AppState) => state.globalThemeInfo.activeTheme,
);
const backgroundIsDark = React.useMemo(
() => selectBackgroundIsDark(navigationState, theme),
[navigationState, theme],
);
const syntheticTheme = backgroundIsDark ? 'dark' : 'light';
return React.useMemo(() => stylesFromColors(obj, colors[syntheticTheme]), [
obj,
syntheticTheme,
]);
}
function useColors(): Colors {
return useSelector(colorsSelector);
}
function getStylesForTheme(
obj: IS,
theme: GlobalTheme,
): StyleSheetOf {
return stylesFromColors(obj, colors[theme]);
}
export type IndicatorStyle = 'white' | 'black';
function useIndicatorStyle(): IndicatorStyle {
const theme = useSelector(
(state: AppState) => state.globalThemeInfo.activeTheme,
);
return theme && theme === 'dark' ? 'white' : 'black';
}
const indicatorStyleSelector: (
state: AppState,
) => IndicatorStyle = createSelector(
(state: AppState) => state.globalThemeInfo.activeTheme,
(theme: ?GlobalTheme) => {
return theme && theme === 'dark' ? 'white' : 'black';
},
);
export type KeyboardAppearance = 'default' | 'light' | 'dark';
const keyboardAppearanceSelector: (
state: AppState,
) => KeyboardAppearance = createSelector(
(state: AppState) => state.globalThemeInfo.activeTheme,
(theme: ?GlobalTheme) => {
return theme && theme === 'dark' ? 'dark' : 'light';
},
);
function useKeyboardAppearance(): KeyboardAppearance {
return useSelector(keyboardAppearanceSelector);
}
export {
colors,
colorsSelector,
styleSelector,
useStyles,
useOverlayStyles,
useColors,
getStylesForTheme,
useIndicatorStyle,
indicatorStyleSelector,
useKeyboardAppearance,
};