Various fixes (#6268)

This commit is contained in:
Elias Nahum 2022-05-12 23:50:19 -04:00 committed by GitHub
parent 8ca26bde7d
commit d8c8893c49
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 139 additions and 30 deletions

View file

@ -142,17 +142,17 @@ const ChannelListItem = ({
styles.text,
isBright && styles.highlight,
isMuted && styles.muted,
isActive && !isInfo ? styles.textActive : null,
isActive && isTablet && !isInfo ? styles.textActive : null,
isInfo ? styles.textInfo : null,
], [isBright, styles, isMuted, isActive, isInfo]);
], [isBright, styles, isMuted, isActive, isInfo, isTablet]);
const containerStyle = useMemo(() => [
styles.container,
isActive && !isInfo && styles.activeItem,
isActive && isTablet && !isInfo && styles.activeItem,
isInfo && styles.infoItem,
{minHeight: height},
],
[height, isActive, isInfo, styles]);
[height, isActive, isTablet, isInfo, styles]);
if (!hasMember) {
return null;
@ -176,7 +176,7 @@ const ChannelListItem = ({
<View style={styles.wrapper}>
<ChannelIcon
hasDraft={hasDraft}
isActive={isInfo ? false : isActive}
isActive={isInfo ? false : isTablet && isActive}
isInfo={isInfo}
isUnread={isBright}
isArchived={channel.deleteAt > 0}

View file

@ -224,8 +224,8 @@ const FloatingTextInput = forwardRef<FloatingTextInputRef, FloatingTextInputProp
}
return {
top: withTiming(toValue, {duration: 250, easing: Easing.linear}),
fontSize: withTiming(toSize, {duration: 250, easing: Easing.linear}),
top: withTiming(toValue, {duration: 100, easing: Easing.linear}),
fontSize: withTiming(toSize, {duration: 100, easing: Easing.linear}),
backgroundColor: focusedLabel || inputText ? theme.centerChannelBg : 'transparent',
paddingHorizontal: focusedLabel || inputText ? 4 : 0,
color,

View file

@ -8,7 +8,6 @@ import {StyleProp, Text, TextStyle} from 'react-native';
import {joinChannel, switchToChannelById} from '@actions/remote/channel';
import {useServerUrl} from '@context/server';
import {t} from '@i18n';
import {dismissAllModals, popToRoot} from '@screens/navigation';
import {alertErrorWithFallback} from '@utils/draft';
import {preventDoubleTap} from '@utils/tap';
@ -87,8 +86,6 @@ const ChannelMention = ({
if (c?.id) {
switchToChannelById(serverUrl, c.id);
await dismissAllModals();
await popToRoot();
}
});

View file

@ -300,6 +300,7 @@ const Post = ({
testID={itemTestID}
onPress={handlePress}
onLongPress={showPostOptions}
delayLongPress={200}
underlayColor={changeOpacity(theme.centerChannelColor, 0.1)}
style={styles.touchableContainer}
>

View file

@ -11,6 +11,14 @@ exports[`Server Icon Server Icon Component should match snapshot 1`] = `
accessible={true}
collapsable={false}
focusable={false}
hitSlop={
Object {
"bottom": 5,
"left": 40,
"right": 20,
"top": 20,
}
}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
@ -44,6 +52,14 @@ exports[`Server Icon Server Icon Component should match snapshot with mentions 1
accessible={true}
collapsable={false}
focusable={false}
hitSlop={
Object {
"bottom": 5,
"left": 40,
"right": 20,
"top": 20,
}
}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
@ -112,6 +128,14 @@ exports[`Server Icon Server Icon Component should match snapshot with unreads 1`
accessible={true}
collapsable={false}
focusable={false}
hitSlop={
Object {
"bottom": 5,
"left": 40,
"right": 20,
"top": 20,
}
}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}

View file

@ -25,6 +25,8 @@ type Props = {
unreadStyle?: StyleProp<ViewStyle>;
}
const hitSlop = {top: 20, bottom: 5, left: 40, right: 20};
const styles = StyleSheet.create({
badge: {
left: 13,
@ -64,6 +66,7 @@ export default function ServerIcon({
onPress={onPress}
type='opacity'
testID={testID}
hitSlop={hitSlop}
>
<CompassIcon
size={size}

View file

@ -44,15 +44,15 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
},
mentionsOneDigit: {
top: 1,
left: 28,
left: 31,
},
mentionsTwoDigits: {
top: 1,
left: 26,
left: 30,
},
mentionsThreeDigits: {
top: 1,
left: 23,
left: 28,
},
};
});

View file

@ -8,6 +8,7 @@ export default keyMirror({
CHANNEL_DELETED: null,
CLOSE_BOTTOM_SHEET: null,
CONFIG_CHANGED: null,
FETCHING_POSTS: null,
FREEZE_SCREEN: null,
GALLERY_ACTIONS: null,
LEAVE_CHANNEL: null,

View file

@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import React, {useEffect, useRef, useState} from 'react';
import {DeviceEventEmitter, StyleSheet, View} from 'react-native';
import {BackHandler, DeviceEventEmitter, NativeEventSubscription, StyleSheet, View} from 'react-native';
import {KeyboardTrackingViewRef} from 'react-native-keyboard-tracking-view';
import {Edge, SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context';
@ -13,6 +13,7 @@ import {ACCESSORIES_CONTAINER_NATIVE_ID} from '@constants/post_draft';
import {useAppState, useIsTablet} from '@hooks/device';
import {useDefaultHeaderHeight} from '@hooks/header';
import {useTeamSwitch} from '@hooks/team_switch';
import {popTopScreen} from '@screens/navigation';
import ChannelPostList from './channel_post_list';
import ChannelHeader from './header';
@ -52,6 +53,18 @@ const Channel = ({channelId, componentId}: ChannelProps) => {
return () => listener.remove();
}, []);
useEffect(() => {
let back: NativeEventSubscription|undefined;
if (!isTablet && componentId) {
back = BackHandler.addEventListener('hardwareBackPress', () => {
popTopScreen(componentId);
return true;
});
}
return () => back?.remove();
}, [componentId, isTablet]);
const marginTop = defaultHeight + (isTablet ? insets.top : 0);
useEffect(() => {
// This is done so that the header renders

View file

@ -54,7 +54,12 @@ const ChannelPostList = ({
}
}, 500), [channelId, posts]);
const intro = <Intro channelId={channelId}/>;
const intro = (
<Intro
channelId={channelId}
hasPosts={posts.length > 0}
/>
);
const postList = (
<PostList

View file

@ -16,7 +16,7 @@ import type RoleModel from '@typings/database/models/servers/role';
type Props = {
channel: ChannelModel;
loading?: boolean;
hasPosts: boolean;
roles: RoleModel[];
}
@ -35,8 +35,8 @@ const styles = StyleSheet.create({
},
});
const Intro = ({channel, loading = false, roles}: Props) => {
const [fetching, setFetching] = useState(false);
const Intro = ({channel, hasPosts, roles}: Props) => {
const [fetching, setFetching] = useState(!hasPosts);
const theme = useTheme();
const element = useMemo(() => {
if (channel.type === General.OPEN_CHANNEL && channel.name === General.DEFAULT_CHANNEL) {
@ -78,7 +78,23 @@ const Intro = ({channel, loading = false, roles}: Props) => {
return () => listener.remove();
}, []);
if (loading || fetching) {
// We add a timeout to remove the loading indicator
// Even if the channel does not have any posts
useEffect(() => {
const time = setTimeout(() => {
if (!hasPosts && fetching) {
setFetching(false);
}
}, 1000);
return () => {
if (time) {
clearTimeout(time);
}
};
}, [hasPosts, fetching]);
if (fetching) {
return (
<ActivityIndicator
size='small'

View file

@ -274,7 +274,7 @@ const FilteredList = ({
});
useEffect(() => {
bounce.current = debounce(search, 250);
bounce.current = debounce(search, 500);
bounce.current();
return () => {
if (bounce.current) {

View file

@ -1,7 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import {debounce, DebouncedFunc} from 'lodash';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {DeviceEventEmitter, Keyboard, View} from 'react-native';
import {Navigation} from 'react-native-navigation';
@ -44,6 +45,7 @@ const FindChannels = ({closeButtonId, componentId}: Props) => {
const theme = useTheme();
const [term, setTerm] = useState('');
const [loading, setLoading] = useState(false);
const bounce = useRef<DebouncedFunc<() => void>>();
const styles = getStyleSheet(theme);
const color = useMemo(() => changeOpacity(theme.centerChannelColor, 0.72), [theme]);
const keyboardHeight = useKeyboardHeight();
@ -66,10 +68,18 @@ const FindChannels = ({closeButtonId, componentId}: Props) => {
}, []);
const onChangeText = useCallback((text) => {
setTerm(text);
if (!text) {
if (text) {
bounce.current?.cancel();
bounce.current = debounce(() => {
setTerm(text);
}, 100);
bounce.current();
} else {
setTerm(text);
setLoading(false);
}
return () => bounce.current?.cancel();
}, []);
useEffect(() => {

View file

@ -38,7 +38,7 @@ type Props = {
}
const AnimatedSafeAreaView = Animated.createAnimatedComponent(SafeAreaView);
const edges: Edge[] = ['left', 'right'];
const edges: Edge[] = ['left', 'right', 'bottom'];
const styles = StyleSheet.create({
container: {
alignItems: 'center',

View file

@ -2,17 +2,20 @@
// See LICENSE.txt for license information.
import {useManagedConfig} from '@mattermost/react-native-emm';
import {useIsFocused, useRoute} from '@react-navigation/native';
import React, {useEffect} from 'react';
import {StyleSheet} from 'react-native';
import {useIsFocused, useNavigation, useRoute} from '@react-navigation/native';
import React, {useCallback, useEffect} from 'react';
import {useIntl} from 'react-intl';
import {BackHandler, StyleSheet, ToastAndroid} from 'react-native';
import Animated, {useAnimatedStyle, withTiming} from 'react-native-reanimated';
import {Edge, SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context';
import FreezeScreen from '@components/freeze_screen';
import TeamSidebar from '@components/team_sidebar';
import {Screens} from '@constants';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';
import {resetToTeams} from '@screens/navigation';
import EphemeralStore from '@store/ephemeral_store';
import AdditionalTabletView from './additional_tablet_view';
import CategoriesList from './categories_list';
@ -34,17 +37,43 @@ const styles = StyleSheet.create({
},
});
let backPressedCount = 0;
let backPressTimeout: NodeJS.Timeout|undefined;
const ChannelListScreen = (props: ChannelProps) => {
const theme = useTheme();
const managedConfig = useManagedConfig<ManagedConfig>();
const intl = useIntl();
const isTablet = useIsTablet();
const route = useRoute();
const isFocused = useIsFocused();
const navigation = useNavigation();
const insets = useSafeAreaInsets();
const params = route.params as {direction: string};
const canAddOtherServers = managedConfig?.allowOtherServers !== 'false';
const handleBackPress = useCallback(() => {
const focused = navigation.isFocused() && EphemeralStore.getNavigationTopComponentId() === Screens.HOME;
if (!backPressedCount && focused) {
backPressedCount++;
ToastAndroid.show(intl.formatMessage({
id: 'mobile.android.back_handler_exit',
defaultMessage: 'Press back again to exit',
}), ToastAndroid.SHORT);
if (backPressTimeout) {
clearTimeout(backPressTimeout);
}
backPressTimeout = setTimeout(() => {
clearTimeout(backPressTimeout!);
backPressedCount = 0;
}, 2000);
return true;
}
return false;
}, [intl]);
const animated = useAnimatedStyle(() => {
if (!isFocused) {
let initial = 0;
@ -72,6 +101,11 @@ const ChannelListScreen = (props: ChannelProps) => {
}
}, [Boolean(props.teamsCount)]);
useEffect(() => {
const back = BackHandler.addEventListener('hardwareBackPress', handleBackPress);
return () => back.remove();
}, [handleBackPress]);
return (
<FreezeScreen freeze={!isFocused}>
{<Animated.View style={top}/>}

View file

@ -197,9 +197,9 @@ const ServerItem = ({
viewRef.current?.measureInWindow((x, y, w, h) => {
const bounds: TutorialItemBounds = {
startX: x - 20,
startY: y - 5,
startY: y,
endX: x + w + 20,
endY: y + h + 5,
endY: y + h,
};
setShowTutorial(true);
setItemBounds(bounds);
@ -343,7 +343,7 @@ const ServerItem = ({
useEffect(() => {
let time: NodeJS.Timeout;
if (highlight && !tutorialWatched) {
time = setTimeout(startTutorial, 300);
time = setTimeout(startTutorial, 650);
}
return () => clearTimeout(time);
}, [highlight, tutorialWatched]);

View file

@ -32,6 +32,7 @@ if (Platform.OS === 'ios') {
enableFreeze(true);
type HomeProps = LaunchProps & {
componentId: string;
time?: number;
};

View file

@ -17,6 +17,7 @@ import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
type Props = {
autoFocus?: boolean;
buttonDisabled: boolean;
connecting: boolean;
displayName?: string;
@ -71,6 +72,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
}));
const ServerForm = ({
autoFocus = false,
buttonDisabled,
connecting,
displayName = '',
@ -163,6 +165,7 @@ const ServerForm = ({
<FloatingTextInput
autoCorrect={false}
autoCapitalize={'none'}
autoFocus={autoFocus}
blurOnSubmit={false}
containerStyle={styles.enterServer}
enablesReturnKeyAutomatically={true}

View file

@ -341,6 +341,7 @@ const Server = ({
theme={theme}
/>
<ServerForm
autoFocus={launchType === LaunchType.AddServer}
buttonDisabled={buttonDisabled}
connecting={connecting}
displayName={displayName}