diff --git a/app/components/floating_input/floating_autocomplete_selector/floating_autocomplete_selector.tsx b/app/components/floating_input/floating_autocomplete_selector/floating_autocomplete_selector.tsx
index 870cc285a..cacbacfb0 100644
--- a/app/components/floating_input/floating_autocomplete_selector/floating_autocomplete_selector.tsx
+++ b/app/components/floating_input/floating_autocomplete_selector/floating_autocomplete_selector.tsx
@@ -219,6 +219,7 @@ function AutoCompleteSelector({
diff --git a/app/components/floating_input/floating_input_container.tsx b/app/components/floating_input/floating_input_container.tsx
index 6e6edb2f8..f0792f845 100644
--- a/app/components/floating_input/floating_input_container.tsx
+++ b/app/components/floating_input/floating_input_container.tsx
@@ -123,7 +123,7 @@ const FloatingInputContainer = ({
testID,
}: Props) => {
const styles = getStyleSheet(theme);
- const positions = useMemo(() => getLabelPositions(styles.textInput, styles.label, styles.smallLabel), [styles]);
+ const positions = useMemo(() => getLabelPositions(styles.textInput, styles.bigLabel, styles.smallLabel), [styles]);
const errorIcon = 'alert-outline';
const shouldShowError = !focused && error;
diff --git a/app/components/floating_input/floating_text_input_label.tsx b/app/components/floating_input/floating_text_input_label.tsx
index c5e9b6a43..0e49aac40 100644
--- a/app/components/floating_input/floating_text_input_label.tsx
+++ b/app/components/floating_input/floating_text_input_label.tsx
@@ -7,6 +7,7 @@ import React, {useState, useRef, useImperativeHandle, forwardRef, useMemo, useCa
import {type LayoutChangeEvent, type NativeSyntheticEvent, type StyleProp, type TargetedEvent, TextInput, type TextInputFocusEventData, type TextInputProps, type TextStyle} from 'react-native';
import {changeOpacity, getKeyboardAppearanceFromTheme, makeStyleSheetFromTheme} from '@utils/theme';
+import {typography} from '@utils/typography';
import FloatingInputContainer from './floating_input_container';
import {onExecution} from './utils';
@@ -22,11 +23,10 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
paddingTop: 0,
paddingBottom: 0,
flexDirection: 'row',
- fontFamily: 'OpenSans',
- fontSize: 16,
color: theme.centerChannelColor,
borderColor: changeOpacity(theme.centerChannelColor, 0.16),
borderRadius: 4,
+ ...typography('Body', 200),
},
}));
diff --git a/app/components/floating_input/utils.test.ts b/app/components/floating_input/utils.test.ts
index 417e9f63d..6c4ec87ff 100644
--- a/app/components/floating_input/utils.test.ts
+++ b/app/components/floating_input/utils.test.ts
@@ -9,14 +9,13 @@ describe('getLabelPositions', () => {
paddingTop: 10,
paddingBottom: 10,
height: 50,
- fontSize: 14,
padding: 20,
};
const labelStyle = {fontSize: 15};
const smallLabelStyle = {fontSize: 11};
const result = getLabelPositions(style, labelStyle, smallLabelStyle);
- expect(result).toEqual([24.4, -6.5]);
+ expect(result).toEqual([25, -6.5]);
});
test('should return correct positions when label and smallLabels styles are missing', () => {
@@ -24,14 +23,13 @@ describe('getLabelPositions', () => {
paddingTop: 15,
paddingBottom: 15,
height: 50,
- fontSize: 14,
padding: 25,
};
const labelStyle = {};
const smallLabelStyle = {};
const result = getLabelPositions(style, labelStyle, smallLabelStyle);
- expect(result).toEqual([23.8, -6.5]);
+ expect(result).toEqual([23.2, -6.5]);
});
test('should return correct positions when all values are empty are provided', () => {
@@ -49,7 +47,6 @@ describe('getLabelPositions', () => {
paddingTop: 0,
paddingBottom: 0,
height: 0,
- fontSize: 0,
padding: 0,
};
const labelStyle = {fontSize: 0};
diff --git a/app/components/floating_input/utils.ts b/app/components/floating_input/utils.ts
index 14c993ea5..e60c74b8d 100644
--- a/app/components/floating_input/utils.ts
+++ b/app/components/floating_input/utils.ts
@@ -17,7 +17,7 @@ export const getLabelPositions = (style: TextStyle, labelStyle: TextStyle, small
const bottom: number = style.paddingBottom as number || 0;
const height: number = (style.height as number || (top + bottom) || style.padding as number) || 0;
- const textInputFontSize = style.fontSize || 13;
+ const textInputFontSize = labelStyle.fontSize || 13;
const labelFontSize = labelStyle.fontSize || 16;
const smallLabelFontSize = smallLabelStyle.fontSize || 10;
const fontSizeDiff = textInputFontSize - labelFontSize;
diff --git a/app/products/playbooks/screens/navigation.ts b/app/products/playbooks/screens/navigation.ts
index bec4b72fd..0c9dc06af 100644
--- a/app/products/playbooks/screens/navigation.ts
+++ b/app/products/playbooks/screens/navigation.ts
@@ -120,9 +120,10 @@ export async function goToSelectDate(
export async function goToSelectPlaybook(
intl: IntlShape,
theme: Theme,
+ channelId?: string,
) {
const title = intl.formatMessage({id: 'playbooks.select_playbook.title', defaultMessage: 'Start a run'});
- goToScreen(Screens.PLAYBOOKS_SELECT_PLAYBOOK, title, {}, {
+ goToScreen(Screens.PLAYBOOKS_SELECT_PLAYBOOK, title, {channelId}, {
topBar: {
subtitle: {
text: intl.formatMessage({id: 'playbooks.select_playbook.subtitle', defaultMessage: 'Select a playbook'}),
@@ -132,10 +133,10 @@ export async function goToSelectPlaybook(
});
}
-export async function goToStartARun(intl: IntlShape, theme: Theme, playbook: Playbook, onRunCreated: (run: PlaybookRun) => void) {
+export async function goToStartARun(intl: IntlShape, theme: Theme, playbook: Playbook, onRunCreated: (run: PlaybookRun) => void, channelId?: string) {
const title = intl.formatMessage({id: 'playbooks.start_a_run.title', defaultMessage: 'Start a run'});
const subtitle = playbook.title;
- goToScreen(Screens.PLAYBOOKS_START_A_RUN, title, {playbook, onRunCreated}, {
+ goToScreen(Screens.PLAYBOOKS_START_A_RUN, title, {playbook, onRunCreated, channelId}, {
topBar: {
subtitle: {
text: subtitle,
diff --git a/app/products/playbooks/screens/participant_playbooks/participant_playbooks.tsx b/app/products/playbooks/screens/participant_playbooks/participant_playbooks.tsx
index 078e018d9..bb6b5c6c8 100644
--- a/app/products/playbooks/screens/participant_playbooks/participant_playbooks.tsx
+++ b/app/products/playbooks/screens/participant_playbooks/participant_playbooks.tsx
@@ -7,7 +7,6 @@ import {defineMessages, useIntl} from 'react-intl';
import {View} from 'react-native';
import Loading from '@components/loading';
-import MenuDivider from '@components/menu_divider';
import SectionNotice from '@components/section_notice';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
@@ -83,6 +82,14 @@ const tabs: Array> = [
},
];
+const itemSeparatorStyle = {
+ height: 12,
+};
+
+const ItemSeparator = () => {
+ return ;
+};
+
const ParticipantPlaybooks = ({
currentUserId,
componentId,
@@ -202,7 +209,7 @@ const ParticipantPlaybooks = ({
data={data}
renderItem={renderItem}
contentContainerStyle={styles.container}
- ItemSeparatorComponent={MenuDivider}
+ ItemSeparatorComponent={ItemSeparator}
estimatedItemSize={CARD_HEIGHT}
onEndReached={loadMore}
onEndReachedThreshold={0.1}
diff --git a/app/products/playbooks/screens/playbook_run/status_update_indicator.test.tsx b/app/products/playbooks/screens/playbook_run/status_update_indicator.test.tsx
index 77e021b61..e5f9715d0 100644
--- a/app/products/playbooks/screens/playbook_run/status_update_indicator.test.tsx
+++ b/app/products/playbooks/screens/playbook_run/status_update_indicator.test.tsx
@@ -37,7 +37,7 @@ describe('StatusUpdateIndicator', () => {
/>,
);
- const text = getByText(/Status update due/);
+ const text = getByText(/Update due/);
expect(text).toHaveStyle({color: changeOpacity(Preferences.THEMES.denim.centerChannelColor, 0.72)});
const icon = getByTestId('compass-icon');
@@ -55,7 +55,7 @@ describe('StatusUpdateIndicator', () => {
/>,
);
- const text = getByText(/Status update overdue/);
+ const text = getByText(/Update overdue/);
expect(text).toHaveStyle({color: Preferences.THEMES.denim.dndIndicator});
const icon = getByTestId('compass-icon');
diff --git a/app/products/playbooks/screens/playbook_run/status_update_indicator.tsx b/app/products/playbooks/screens/playbook_run/status_update_indicator.tsx
index e956100ad..d0c4ab7ff 100644
--- a/app/products/playbooks/screens/playbook_run/status_update_indicator.tsx
+++ b/app/products/playbooks/screens/playbook_run/status_update_indicator.tsx
@@ -55,20 +55,23 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => ({
overdueText: {
color: theme.dndIndicator,
},
+ bold: {
+ ...typography('Body', 300, 'SemiBold'),
+ },
}));
const messages = defineMessages({
updateOverdue: {
id: 'playbooks.playbook_run.status_update_overdue',
- defaultMessage: 'Status update overdue {time}',
+ defaultMessage: 'Update overdue\n{time}',
},
updateDue: {
id: 'playbooks.playbook_run.status_update_due',
- defaultMessage: 'Status update due {time}',
+ defaultMessage: 'Update due\n{time}',
},
finished: {
id: 'playbooks.playbook_run.status_update_finished',
- defaultMessage: 'Run finished {time}',
+ defaultMessage: 'Run finished\n{time}',
},
update: {
id: 'playbooks.playbook_run.status_update',
@@ -86,13 +89,20 @@ const StatusUpdateIndicator = ({
const styles = getStyleSheet(theme);
const intl = useIntl();
- const values = {time: };
+ const values = useMemo(() => ({time: (
+
+ )}), [timestamp, styles]);
+
const readOnly = !isParticipant || isFinished;
+ const isOverdue = timestamp < Date.now();
let message = messages.updateDue;
if (isFinished) {
message = messages.finished;
- } else if (timestamp < Date.now()) {
+ } else if (isOverdue) {
message = messages.updateOverdue;
}
@@ -137,6 +147,7 @@ const StatusUpdateIndicator = ({
theme={theme}
size='lg'
disabled={readOnly}
+ isDestructive={isOverdue && !isFinished}
/>
);
diff --git a/app/products/playbooks/screens/playbooks_runs/playbook_runs.tsx b/app/products/playbooks/screens/playbooks_runs/playbook_runs.tsx
index 7f0b9acd3..2b9aff049 100644
--- a/app/products/playbooks/screens/playbooks_runs/playbook_runs.tsx
+++ b/app/products/playbooks/screens/playbooks_runs/playbook_runs.tsx
@@ -140,8 +140,8 @@ const PlaybookRuns = ({
}, []);
const startANewRun = useCallback(() => {
- goToSelectPlaybook(intl, theme);
- }, [intl, theme]);
+ goToSelectPlaybook(intl, theme, channelId);
+ }, [intl, theme, channelId]);
let content = ();
if (!isEmpty) {
@@ -162,6 +162,7 @@ const PlaybookRuns = ({
text={intl.formatMessage({id: 'playbooks.runs.start_a_new_run', defaultMessage: 'Start a new run'})}
size='lg'
theme={theme}
+ iconName='play-outline'
/>
>
diff --git a/app/products/playbooks/screens/post_update/post_update.tsx b/app/products/playbooks/screens/post_update/post_update.tsx
index f4c28746d..4991e4850 100644
--- a/app/products/playbooks/screens/post_update/post_update.tsx
+++ b/app/products/playbooks/screens/post_update/post_update.tsx
@@ -1,9 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {type ComponentProps, useCallback, useEffect, useMemo, useState} from 'react';
+import {type ComponentProps, useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
-import {Alert, Keyboard, Text, View} from 'react-native';
+import {Alert, Keyboard, Text} from 'react-native';
+import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
import {getPosts} from '@actions/local/post';
import FloatingAutocompleteSelector from '@components/floating_input/floating_autocomplete_selector';
@@ -13,7 +14,9 @@ import OptionItem from '@components/option_item';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import useAndroidHardwareBackHandler from '@hooks/android_back_handler';
+import {useAvoidKeyboard} from '@hooks/device';
import useNavButtonPressed from '@hooks/navigation_button_pressed';
+import SecurityManager from '@managers/security_manager';
import {fetchPlaybookRun, fetchPlaybookRunMetadata, postStatusUpdate} from '@playbooks/actions/remote/runs';
import {buildNavigationButton, popTopScreen, setButtons} from '@screens/navigation';
import {toSeconds} from '@utils/datetime';
@@ -35,7 +38,6 @@ type Props = {
const getStyles = makeStyleSheetFromTheme((theme: Theme) => ({
container: {
- flex: 1,
padding: 20,
gap: 12,
},
@@ -46,6 +48,9 @@ const getStyles = makeStyleSheetFromTheme((theme: Theme) => ({
introMessageBold: {
...typography('Body', 200, 'SemiBold'),
},
+ flex: {
+ flex: 1,
+ },
}));
const SAVE_BUTTON_ID = 'save-post-update';
@@ -84,6 +89,9 @@ const PostUpdate = ({
const [alsoMarkRunAsFinished, setAlsoMarkRunAsFinished] = useState(false);
const [canSave, setCanSave] = useState(false);
+ const keyboardAwareRef = useRef(null);
+ useAvoidKeyboard(keyboardAwareRef);
+
const [followersCount, setFollowersCount] = useState(0);
const [broadcastChannelCount, setBroadcastChannelCount] = useState(0);
@@ -272,7 +280,12 @@ const PostUpdate = ({
}
return (
-
+
{introMessage}
-
+
);
};
diff --git a/app/products/playbooks/screens/select_playbook/select_playbook.tsx b/app/products/playbooks/screens/select_playbook/select_playbook.tsx
index ba4dfd945..25b59e76a 100644
--- a/app/products/playbooks/screens/select_playbook/select_playbook.tsx
+++ b/app/products/playbooks/screens/select_playbook/select_playbook.tsx
@@ -39,6 +39,7 @@ export type Props = {
currentUserId: string;
componentId: AvailableScreens;
playbooksUsedInChannel: Set;
+ channelId?: string;
}
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
@@ -81,6 +82,10 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
flex: 0,
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
},
+ sectionHeaderContainer: {
+ backgroundColor: theme.centerChannelBg,
+ paddingVertical: 8,
+ },
sectionHeader: {
color: changeOpacity(theme.centerChannelColor, 0.56),
...typography('Body', 75, 'SemiBold'),
@@ -96,6 +101,7 @@ function SelectPlaybook({
currentUserId,
componentId,
playbooksUsedInChannel,
+ channelId,
}: Props) {
const serverUrl = useServerUrl();
const theme = useTheme();
@@ -185,7 +191,12 @@ function SelectPlaybook({
}, []);
const renderNoResults = useCallback((): JSX.Element | null => {
- if (searching || (loading && page.current === -1)) {
+ if (loading && page.current === -1) {
+ // Already handled by the loading component
+ return null;
+ }
+
+ if (searching) {
return (
{
- goToStartARun(intl, theme, playbook, onRunCreated);
- }, [intl, onRunCreated, theme]);
+ goToStartARun(intl, theme, playbook, onRunCreated, channelId);
+ }, [intl, onRunCreated, theme, channelId]);
const renderItem = useCallback(({item}: ListRenderItemInfo) => {
return (
@@ -242,9 +253,11 @@ function SelectPlaybook({
const renderSectionHeader = useCallback(({section}: {section: SectionListData}) => {
return (
- {section.title}
+
+ {section.title}
+
);
- }, [style.sectionHeader]);
+ }, [style]);
const sections: Array> = useMemo(() => {
type PlaybookSections = {
diff --git a/app/products/playbooks/screens/start_a_run/start_a_run.tsx b/app/products/playbooks/screens/start_a_run/start_a_run.tsx
index 6e53e7e0c..f0f80b084 100644
--- a/app/products/playbooks/screens/start_a_run/start_a_run.tsx
+++ b/app/products/playbooks/screens/start_a_run/start_a_run.tsx
@@ -32,6 +32,7 @@ export type Props = {
currentUserId: string;
currentTeamId: string;
onRunCreated: (run: PlaybookRun) => void;
+ channelId?: string;
}
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
@@ -86,6 +87,7 @@ function StartARun({
currentUserId,
currentTeamId,
onRunCreated,
+ channelId,
}: Props) {
const theme = useTheme();
const intl = useIntl();
@@ -105,7 +107,7 @@ function StartARun({
return '';
});
const [channelOption, setChannelOption] = useState('existing');
- const [channelId, setChannelId] = useState(undefined);
+ const [selectedChannelId, setSelectedChannelId] = useState(channelId);
const [createPublicChannel, setCreatePublicChannel] = useState(false);
const canSave = Boolean(runName.trim());
@@ -115,7 +117,7 @@ function StartARun({
return;
}
- const res = await createPlaybookRun(serverUrl, playbook.id, currentUserId, currentTeamId, runName.trim(), runDescription.trim(), channelId, channelOption === 'new' ? createPublicChannel : undefined);
+ const res = await createPlaybookRun(serverUrl, playbook.id, currentUserId, currentTeamId, runName.trim(), runDescription.trim(), selectedChannelId, channelOption === 'new' ? createPublicChannel : undefined);
if (res.error || !res.data) {
logDebug('error on createPlaybookRun', getFullErrorMessage(res.error));
showPlaybookErrorSnackbar();
@@ -123,7 +125,7 @@ function StartARun({
}
await popTopScreen(componentId);
onRunCreated(res.data);
- }, [runName, serverUrl, playbook.id, currentUserId, currentTeamId, runDescription, channelId, channelOption, createPublicChannel, componentId, onRunCreated]);
+ }, [runName, serverUrl, playbook.id, currentUserId, currentTeamId, runDescription, selectedChannelId, channelOption, createPublicChannel, componentId, onRunCreated]);
useEffect(() => {
async function asyncWrapper() {
@@ -174,7 +176,7 @@ function StartARun({
logDebug('on channel selected returned undefined, this should never happen');
return;
}
- setChannelId(value.value);
+ setSelectedChannelId(value.value);
}, []);
return (
@@ -232,7 +234,7 @@ function StartARun({
defaultMessage: 'Channel',
})}
dataSource='channels'
- selected={channelId}
+ selected={selectedChannelId}
onSelected={onChannelSelected}
testID='start_run.existing_channel_selector'
/>
diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json
index 8a14ba84b..ea48506c3 100644
--- a/assets/base/i18n/en.json
+++ b/assets/base/i18n/en.json
@@ -1040,9 +1040,9 @@
"playbooks.playbook_run.participants_title": "Run Participants",
"playbooks.playbook_run.run_details": "Run details",
"playbooks.playbook_run.status_update": "Post update",
- "playbooks.playbook_run.status_update_due": "Status update due {time}",
- "playbooks.playbook_run.status_update_finished": "Run finished {time}",
- "playbooks.playbook_run.status_update_overdue": "Status update overdue {time}",
+ "playbooks.playbook_run.status_update_due": "Update due\n{time}",
+ "playbooks.playbook_run.status_update_finished": "Run finished\n{time}",
+ "playbooks.playbook_run.status_update_overdue": "Update overdue\n{time}",
"playbooks.playbook_run.tasks": "Tasks",
"playbooks.playbook_run.title": "Playbook run",
"playbooks.playbooks_runs.title": "Playbook runs",