Changed the Edited text style in the post from (edited) to icon + Edited (#8937)
* Viewing Files in Edit mode in mobile with ability to delete and save * Added upload attachment to keyboard tracker view * using state instread of use ref * Minor * Added tests * intl extract * new function getFiles ById, batch file deletion and tests * Files fetching in edit options and tests * Removed DeviceEventEmitter and used React context * Added support to check minimum required version to show edit file attachments * resolve forward ref issue * Minor * memotized props for context and observe config with value * Ability to show quick action and add files to edit post * type safety for EditPostContext * Reverted back the post priority props * constant shift * Added test for QuickAction to show slashcomand * Added test for edit_post, upload_item and upload_remove * Added test for Edit_post_input and edit_post index * fix the height issue between attachment and keyboard due to safeArea * Minor: removed debugging border color * Changed the Edited text style in the post from (edited) to icon + Edited * Import fixes * Ability to show quick action and add files to edit post * type safety for EditPostContext * Reverted back the post priority props * constant shift * Added test for QuickAction to show slashcomand * Added test for edit_post, upload_item and upload_remove * Added test for Edit_post_input and edit_post index * fix the height issue between attachment and keyboard due to safeArea * Minor: removed debugging border color * Addressed dev review comments * Import fixes * Address UX comments * Fixed props for UploadItem and remove effective Edit mode * handled save button disabled state when uploading attachments * handled newly added and retry file removal without alert message * Test updated * Added test for input_quick_action index.tsx * Added test for not in edit mode for upload_item index * Added test for upload_remove component when not in edit mode * added tests for file_upload_error hook * Added test for calling callback when in edit mode * Test for edit post input for server version check * linter fixes * Changed font size from 16 to 14px for edited * separated common component and test * Removed the margin styles from the text as it is not been applied. * removed the duplicate code from rebase
This commit is contained in:
parent
a204494e41
commit
5cceaee7c5
6 changed files with 247 additions and 89 deletions
128
app/components/EditedIndicator/index.test.tsx
Normal file
128
app/components/EditedIndicator/index.test.tsx
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {Preferences} from '@constants';
|
||||
import {renderWithIntlAndTheme} from '@test/intl-test-helper';
|
||||
|
||||
import EditedIndicator from './index';
|
||||
|
||||
describe('components/EditedIndicator', () => {
|
||||
const baseProps = {
|
||||
baseTextStyle: {fontSize: 16, color: '#000'},
|
||||
theme: Preferences.THEMES.denim,
|
||||
context: ['paragraph'],
|
||||
testID: 'edited-indicator-test',
|
||||
};
|
||||
|
||||
it('should render with default props', () => {
|
||||
const {getByTestId} = renderWithIntlAndTheme(
|
||||
<EditedIndicator {...baseProps}/>,
|
||||
);
|
||||
|
||||
const indicator = getByTestId('edited-indicator-test');
|
||||
expect(indicator).toBeTruthy();
|
||||
expect(indicator.props.children).toEqual([
|
||||
' ', // spacer for paragraph context
|
||||
expect.objectContaining({
|
||||
props: expect.objectContaining({
|
||||
name: 'pencil-outline',
|
||||
size: 14, // default icon size
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
props: expect.objectContaining({
|
||||
id: 'post_message_view.edited',
|
||||
defaultMessage: 'Edited',
|
||||
}),
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should render with custom icon size', () => {
|
||||
const {getByTestId} = renderWithIntlAndTheme(
|
||||
<EditedIndicator
|
||||
{...baseProps}
|
||||
iconSize={20}
|
||||
/>,
|
||||
);
|
||||
|
||||
const indicator = getByTestId('edited-indicator-test');
|
||||
const icon = indicator.props.children[1];
|
||||
expect(icon.props.size).toBe(20);
|
||||
});
|
||||
|
||||
it('should render with custom testID', () => {
|
||||
const {getByTestId} = renderWithIntlAndTheme(
|
||||
<EditedIndicator
|
||||
{...baseProps}
|
||||
testID='custom-test-id'
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(getByTestId('custom-test-id')).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('component structure', () => {
|
||||
it('should render pencil-outline icon', () => {
|
||||
const {getByTestId} = renderWithIntlAndTheme(
|
||||
<EditedIndicator {...baseProps}/>,
|
||||
);
|
||||
|
||||
const indicator = getByTestId('edited-indicator-test');
|
||||
const icon = indicator.props.children[1];
|
||||
expect(icon.props.name).toBe('pencil-outline');
|
||||
});
|
||||
|
||||
it('should render "Edited" text with correct props', () => {
|
||||
const {getByTestId} = renderWithIntlAndTheme(
|
||||
<EditedIndicator {...baseProps}/>,
|
||||
);
|
||||
|
||||
const indicator = getByTestId('edited-indicator-test');
|
||||
const formattedText = indicator.props.children[2];
|
||||
expect(formattedText.props.id).toBe('post_message_view.edited');
|
||||
expect(formattedText.props.defaultMessage).toBe('Edited');
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should handle empty context array', () => {
|
||||
const {getByTestId} = renderWithIntlAndTheme(
|
||||
<EditedIndicator
|
||||
{...baseProps}
|
||||
context={[]}
|
||||
/>,
|
||||
);
|
||||
|
||||
const indicator = getByTestId('edited-indicator-test');
|
||||
expect(indicator.props.children[0]).toBe('');
|
||||
});
|
||||
|
||||
it('should handle undefined context elements', () => {
|
||||
const {getByTestId} = renderWithIntlAndTheme(
|
||||
<EditedIndicator
|
||||
{...baseProps}
|
||||
context={[undefined] as any}
|
||||
checkHeadings={true}
|
||||
/>,
|
||||
);
|
||||
|
||||
const indicator = getByTestId('edited-indicator-test');
|
||||
expect(indicator.props.children[0]).toBe('');
|
||||
});
|
||||
|
||||
it('should handle context with multiple elements', () => {
|
||||
const {getByTestId} = renderWithIntlAndTheme(
|
||||
<EditedIndicator
|
||||
{...baseProps}
|
||||
context={['paragraph', 'strong', 'em']}
|
||||
/>,
|
||||
);
|
||||
|
||||
const indicator = getByTestId('edited-indicator-test');
|
||||
expect(indicator.props.children[0]).toBe(' ');
|
||||
});
|
||||
});
|
||||
});
|
||||
83
app/components/EditedIndicator/index.tsx
Normal file
83
app/components/EditedIndicator/index.tsx
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {Platform, type StyleProp, Text, type TextStyle} from 'react-native';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import {blendColors, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
type EditedIndicatorProps = {
|
||||
baseTextStyle: StyleProp<TextStyle>;
|
||||
theme: Theme;
|
||||
context: string[];
|
||||
iconSize?: number;
|
||||
checkHeadings?: boolean;
|
||||
testID?: string;
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
||||
// Android has trouble giving text transparency depending on how it's nested,
|
||||
// so we calculate the resulting colour manually
|
||||
const editedOpacity = Platform.select({
|
||||
ios: 0.56,
|
||||
android: 1.0,
|
||||
});
|
||||
const editedColor = Platform.select({
|
||||
ios: theme.centerChannelColor,
|
||||
android: blendColors(theme.centerChannelBg, theme.centerChannelColor, 0.3),
|
||||
});
|
||||
|
||||
return {
|
||||
editedIndicatorText: {
|
||||
color: editedColor,
|
||||
opacity: editedOpacity,
|
||||
fontStyle: 'italic',
|
||||
...typography('Body', 25, 'Regular'),
|
||||
},
|
||||
editedText: {
|
||||
...typography('Body', 100, 'Regular'),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const EditedIndicator = ({
|
||||
baseTextStyle,
|
||||
theme,
|
||||
context,
|
||||
iconSize = 14,
|
||||
checkHeadings = false,
|
||||
testID = 'edited_indicator',
|
||||
}: EditedIndicatorProps) => {
|
||||
const style = getStyleSheet(theme);
|
||||
let spacer = '';
|
||||
const styles = [baseTextStyle, style.editedIndicatorText];
|
||||
|
||||
// Add space for paragraphs, and optionally for headings
|
||||
if (context[0] === 'paragraph' || (checkHeadings && context[0]?.startsWith('heading'))) {
|
||||
spacer = ' ';
|
||||
}
|
||||
|
||||
return (
|
||||
<Text
|
||||
style={styles}
|
||||
testID={testID}
|
||||
>
|
||||
{spacer}
|
||||
<CompassIcon
|
||||
name='pencil-outline'
|
||||
size={iconSize}
|
||||
color={theme.centerChannelColor}
|
||||
/>
|
||||
<FormattedText
|
||||
id='post_message_view.edited'
|
||||
defaultMessage='Edited'
|
||||
style={style.editedText}
|
||||
/>
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditedIndicator;
|
||||
|
|
@ -4,12 +4,11 @@
|
|||
import {Node, Parser} from 'commonmark';
|
||||
import Renderer from 'commonmark-react-renderer';
|
||||
import React, {type ReactElement, useRef} from 'react';
|
||||
import {Platform, type StyleProp, Text, type TextStyle, View} from 'react-native';
|
||||
import {type StyleProp, StyleSheet, Text, type TextStyle, View} from 'react-native';
|
||||
|
||||
import EditedIndicator from '@components/EditedIndicator';
|
||||
import Emoji from '@components/emoji';
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {blendColors, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
type JumboEmojiProps = {
|
||||
baseTextStyle: StyleProp<TextStyle>;
|
||||
|
|
@ -17,41 +16,23 @@ type JumboEmojiProps = {
|
|||
value: string;
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
||||
// Android has trouble giving text transparency depending on how it's nested,
|
||||
// so we calculate the resulting colour manually
|
||||
const editedOpacity = Platform.select({
|
||||
ios: 0.3,
|
||||
android: 1.0,
|
||||
});
|
||||
const editedColor = Platform.select({
|
||||
ios: theme.centerChannelColor,
|
||||
android: blendColors(theme.centerChannelBg, theme.centerChannelColor, 0.3),
|
||||
});
|
||||
|
||||
return {
|
||||
block: {
|
||||
alignItems: 'flex-start',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
editedIndicatorText: {
|
||||
color: editedColor,
|
||||
opacity: editedOpacity,
|
||||
},
|
||||
jumboEmoji: {
|
||||
fontSize: 50,
|
||||
lineHeight: 60,
|
||||
},
|
||||
newLine: {
|
||||
lineHeight: 60,
|
||||
},
|
||||
};
|
||||
const style = StyleSheet.create({
|
||||
block: {
|
||||
alignItems: 'flex-start',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
jumboEmoji: {
|
||||
fontSize: 50,
|
||||
lineHeight: 60,
|
||||
},
|
||||
newLine: {
|
||||
lineHeight: 60,
|
||||
},
|
||||
});
|
||||
|
||||
const JumboEmoji = ({baseTextStyle, isEdited, value}: JumboEmojiProps) => {
|
||||
const theme = useTheme();
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
const renderEmoji = ({emojiName, literal}: {context: string[]; emojiName: string; literal: string}) => {
|
||||
return (
|
||||
|
|
@ -81,27 +62,15 @@ const JumboEmoji = ({baseTextStyle, isEdited, value}: JumboEmojiProps) => {
|
|||
};
|
||||
|
||||
const renderEditedIndicator = ({context}: {context: string[]}) => {
|
||||
let spacer = '';
|
||||
if (context[0] === 'paragraph') {
|
||||
spacer = ' ';
|
||||
}
|
||||
|
||||
const styles = [
|
||||
baseTextStyle,
|
||||
style.editedIndicatorText,
|
||||
];
|
||||
|
||||
return (
|
||||
<Text
|
||||
style={styles}
|
||||
<EditedIndicator
|
||||
baseTextStyle={baseTextStyle}
|
||||
theme={theme}
|
||||
context={context}
|
||||
iconSize={14}
|
||||
checkHeadings={false}
|
||||
testID='edited_indicator'
|
||||
>
|
||||
{spacer}
|
||||
<FormattedText
|
||||
id='post_message_view.edited'
|
||||
defaultMessage='(edited)'
|
||||
/>
|
||||
</Text>
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,20 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
/* eslint-disable max-lines */
|
||||
|
||||
import {useManagedConfig} from '@mattermost/react-native-emm';
|
||||
import {Parser, Node} from 'commonmark';
|
||||
import Renderer from 'commonmark-react-renderer';
|
||||
import React, {type ReactElement, useMemo, useRef} from 'react';
|
||||
import {Dimensions, type GestureResponderEvent, Platform, type StyleProp, StyleSheet, Text, type TextStyle, View, type ViewStyle} from 'react-native';
|
||||
import {Dimensions, type GestureResponderEvent, type StyleProp, StyleSheet, Text, type TextStyle, View, type ViewStyle} from 'react-native';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import EditedIndicator from '@components/EditedIndicator';
|
||||
import Emoji from '@components/emoji';
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import {logError} from '@utils/log';
|
||||
import {computeTextStyle} from '@utils/markdown';
|
||||
import {blendColors, changeOpacity, concatStyles, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {changeOpacity, concatStyles, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
import {getScheme} from '@utils/url';
|
||||
|
||||
|
|
@ -81,27 +83,12 @@ type MarkdownProps = {
|
|||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
// Android has trouble giving text transparency depending on how it's nested,
|
||||
// so we calculate the resulting colour manually
|
||||
const editedOpacity = Platform.select({
|
||||
ios: 0.3,
|
||||
android: 1.0,
|
||||
});
|
||||
const editedColor = Platform.select({
|
||||
ios: theme.centerChannelColor,
|
||||
android: blendColors(theme.centerChannelBg, theme.centerChannelColor, 0.3),
|
||||
});
|
||||
|
||||
return {
|
||||
block: {
|
||||
alignItems: 'flex-start',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
editedIndicatorText: {
|
||||
color: editedColor,
|
||||
opacity: editedOpacity,
|
||||
},
|
||||
errorMessage: {
|
||||
color: theme.errorTextColor,
|
||||
...typography('Body', 100),
|
||||
|
|
@ -262,24 +249,15 @@ const Markdown = ({
|
|||
};
|
||||
|
||||
const renderEditedIndicator = ({context}: {context: string[]}) => {
|
||||
let spacer = '';
|
||||
const styles = [baseTextStyle, style.editedIndicatorText];
|
||||
|
||||
if (context[0] === 'paragraph') {
|
||||
spacer = ' ';
|
||||
}
|
||||
|
||||
return (
|
||||
<Text
|
||||
style={styles}
|
||||
<EditedIndicator
|
||||
baseTextStyle={baseTextStyle}
|
||||
theme={theme}
|
||||
context={context}
|
||||
iconSize={14}
|
||||
checkHeadings={true}
|
||||
testID='edited_indicator'
|
||||
>
|
||||
{spacer}
|
||||
<FormattedText
|
||||
id='post_message_view.edited'
|
||||
defaultMessage='(edited)'
|
||||
/>
|
||||
</Text>
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -638,7 +616,7 @@ const Markdown = ({
|
|||
if (isEdited) {
|
||||
const editIndicatorNode = new Node('edited_indicator');
|
||||
if (ast.lastChild && ['heading', 'paragraph'].includes(ast.lastChild.type)) {
|
||||
ast.appendChild(editIndicatorNode);
|
||||
ast.lastChild.appendChild(editIndicatorNode);
|
||||
} else {
|
||||
const node = new Node('paragraph');
|
||||
node.appendChild(editIndicatorNode);
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ const Body = ({
|
|||
value={post.message}
|
||||
/>
|
||||
);
|
||||
} else if (post.message.length) {
|
||||
} else if (post.message.length || isEdited) { // isEdited is added to handle the case where the post is edited and the message is empty
|
||||
message = (
|
||||
<Message
|
||||
highlight={highlight}
|
||||
|
|
|
|||
|
|
@ -1002,7 +1002,7 @@
|
|||
"post_info.edit": "Edit",
|
||||
"post_info.guest": "Guest",
|
||||
"post_info.system": "System",
|
||||
"post_message_view.edited": "(edited)",
|
||||
"post_message_view.edited": "Edited",
|
||||
"post_priority.button.acknowledge": "Acknowledge",
|
||||
"post_priority.label.important": "IMPORTANT",
|
||||
"post_priority.label.urgent": "URGENT",
|
||||
|
|
|
|||
Loading…
Reference in a new issue