mirror of
https://github.com/mattermost/mattermost-mobile.git
synced 2026-11-04 09:58:57 +00:00
* Expo-Router partial migration * Full migration to ExpoRouter * i18n * add tests * Merge branch 'main' into expo-router * align package-lock.json * i18n * feedback review * feedback review * android 16kb patch * Refactored emoji picker keyboard using a state machine * set android launch_screen in drawables instead of layout * no conditional hooks * remove duplicate call in test * address feedback * fix tests and address more feedback * small corrections * small corrections * small corrections * fix draft test * fix draft test 2 * remove navigateToScreen in draff test * fix nit in draft test * remove doc file * Feedback review * fix snapshots * improve test coverage * fix i18n * Upgrade t o RN 0.83, other dependencies and new architecture (#9737) * library hardware-keyboard android new arch support fix * library rnshare android new arch support fix * library rnutils android new arch support fix * library secure-pdf-viewer add new arch support * Enable new arch on iOS and Android * update emm and network client with new-arch * update intune to use new-arch * performance improvements * fix tests * fixes post input on new-arch * fix android emoji picker * remove doc * run tests in parallel * upgrade to React Native 0.83.9, Expo 55, React 19, and New Architecture Bumps core dependencies to their latest versions: - React Native 0.77.3 → 0.83.9 (New Architecture enabled) - React 18.3.1 → 19.2.5 - Expo SDK 52 → 55 - TypeScript 5.8.3 → 6.0.3 - Node.js 22.16.0 → 24.15.0 - react-native-reanimated 3.17.3 → 4.3.0 - Various other dependency upgrades Migrates iOS AppDelegate from Objective-C to Swift, updates Android build tooling and Gradle wrapper, refreshes all patches and font assets, and adapts app code, tests, and CI workflows throughout for API changes introduced by these upgrades. * remove unecessary dismissKeyboard in notifications * feedback review * update intune to use new-arch * update @mattermost deps * missing i18n string * additional fixes * update fastlane
163 lines
5.7 KiB
TypeScript
163 lines
5.7 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import React, {useCallback, useMemo, useState} from 'react';
|
|
import {Platform, View} from 'react-native';
|
|
|
|
import FormattedText from '@components/formatted_text';
|
|
import {Screens} from '@constants';
|
|
import {useTheme} from '@context/theme';
|
|
import {usePreventDoubleTap} from '@hooks/utils';
|
|
import BottomSheet from '@screens/bottom_sheet';
|
|
import {dismissBottomSheet} from '@screens/navigation';
|
|
import {FOOTER_HEIGHT} from '@screens/post_priority_picker/footer';
|
|
import ScheduledPostCoreOptions from '@screens/scheduled_post_options/core_options';
|
|
import CallbackStore from '@store/callback_store';
|
|
import {logDebug} from '@utils/log';
|
|
import {showScheduledPostCreationErrorSnackbar} from '@utils/snack_bar';
|
|
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
|
import {typography} from '@utils/typography';
|
|
import {getTimezone} from '@utils/user';
|
|
|
|
import ScheduledPostFooter from './footer';
|
|
|
|
import type {BottomSheetFooterProps} from '@gorhom/bottom-sheet';
|
|
|
|
const OPTIONS_PADDING = 12;
|
|
const OPTIONS_SEPARATOR_HEIGHT = 1;
|
|
const TITLE_HEIGHT = 54;
|
|
const ITEM_HEIGHT = 48;
|
|
const ERROR_HEIGHT = 16;
|
|
|
|
export const SCHEDULED_POST_OPTIONS_BUTTON = 'close-scheduled-post-options';
|
|
|
|
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
|
container: {
|
|
backgroundColor: theme.centerChannelBg,
|
|
height: 200,
|
|
},
|
|
titleContainer: {
|
|
alignItems: 'center',
|
|
flexDirection: 'row',
|
|
},
|
|
title: {
|
|
color: theme.centerChannelColor,
|
|
...typography('Heading', 600, 'SemiBold'),
|
|
},
|
|
optionsContainer: {
|
|
paddingTop: OPTIONS_PADDING,
|
|
},
|
|
optionsSeparator: {
|
|
backgroundColor: changeOpacity(theme.centerChannelColor, 0.08),
|
|
height: OPTIONS_SEPARATOR_HEIGHT,
|
|
},
|
|
errorText: {
|
|
color: theme.errorTextColor,
|
|
...typography('Heading', 25),
|
|
},
|
|
}));
|
|
|
|
type ScheduledPostOptionsProps = {
|
|
currentUserTimezone?: UserTimezone | null;
|
|
}
|
|
|
|
export function ScheduledPostOptions({currentUserTimezone}: ScheduledPostOptionsProps) {
|
|
const theme = useTheme();
|
|
const [isScheduling, setIsScheduling] = useState(false);
|
|
const [selectedTime, setSelectedTime] = useState<string | null>(null);
|
|
const [customTimeSelected, setCustomTimeSelected] = useState(false);
|
|
const [isError, setIsError] = useState(false);
|
|
const userTimezone = getTimezone(currentUserTimezone);
|
|
|
|
const style = getStyleSheet(theme);
|
|
|
|
const snapPoints = useMemo(() => {
|
|
const bottomSheetAdjust = Platform.select({ios: 0, default: 20});
|
|
|
|
// 9 items to display inline date-time picker, 4 items otherwise
|
|
const iosNumberOfItems = customTimeSelected ? 9 : 4;
|
|
const andriodNumberOfItems = customTimeSelected ? 5 : 4;
|
|
const errorHeight = isError ? ERROR_HEIGHT : 0;
|
|
const numberOfItems = Platform.select({ios: iosNumberOfItems, default: andriodNumberOfItems});
|
|
const componentHeight = TITLE_HEIGHT + (numberOfItems * ITEM_HEIGHT) + FOOTER_HEIGHT + errorHeight + bottomSheetAdjust;
|
|
return [1, componentHeight];
|
|
}, [customTimeSelected, isError]);
|
|
|
|
const onSelectTime = useCallback((selectedValue: string) => {
|
|
setIsError(false);
|
|
setSelectedTime(selectedValue);
|
|
}, []);
|
|
|
|
const handleOnSchedule = usePreventDoubleTap(useCallback(async () => {
|
|
if (!selectedTime) {
|
|
setIsError(true);
|
|
logDebug('ScheduledPostOptions', 'No time selected');
|
|
return;
|
|
}
|
|
|
|
setIsError(false);
|
|
setIsScheduling(true);
|
|
const schedulingInfo: SchedulingInfo = {
|
|
scheduled_at: parseInt(selectedTime, 10),
|
|
};
|
|
|
|
const onSchedule = CallbackStore.getCallback<((schedulingInfo: SchedulingInfo) => Promise<void | {data?: boolean; error?: unknown}>)>();
|
|
const response = await onSchedule?.(schedulingInfo);
|
|
setIsScheduling(false);
|
|
|
|
if (response?.error) {
|
|
const errorMessage = response.error as string;
|
|
showScheduledPostCreationErrorSnackbar(errorMessage);
|
|
return;
|
|
}
|
|
CallbackStore.removeCallback();
|
|
dismissBottomSheet();
|
|
}, [selectedTime]));
|
|
|
|
const renderContent = () => {
|
|
return (
|
|
<View style={style.container}>
|
|
<View style={style.titleContainer}>
|
|
<FormattedText
|
|
id='scheduled_post.picker.title'
|
|
defaultMessage='Schedule draft'
|
|
style={style.title}
|
|
/>
|
|
</View>
|
|
<View style={style.optionsContainer}>
|
|
<ScheduledPostCoreOptions
|
|
userTimezone={userTimezone}
|
|
onSelectOption={onSelectTime}
|
|
onCustomTimeSelected={setCustomTimeSelected}
|
|
/>
|
|
</View>
|
|
{isError &&
|
|
<FormattedText
|
|
id='scheduled_post_no_select_time'
|
|
defaultMessage={'Please select the time'}
|
|
style={style.errorText}
|
|
/>
|
|
}
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const renderFooter = (props: BottomSheetFooterProps) => (
|
|
<ScheduledPostFooter
|
|
{...props}
|
|
onSchedule={handleOnSchedule}
|
|
isScheduling={isScheduling}
|
|
/>
|
|
);
|
|
|
|
return (
|
|
<BottomSheet
|
|
renderContent={renderContent}
|
|
screen={Screens.SCHEDULED_POST_OPTIONS}
|
|
snapPoints={snapPoints}
|
|
testID='scheduled_post_options_bottom_sheet'
|
|
footerComponent={renderFooter}
|
|
/>
|
|
);
|
|
}
|