diff --git a/app/components/chips/base_chip.test.tsx b/app/components/chips/base_chip.test.tsx
index d10deb547..638959c19 100644
--- a/app/components/chips/base_chip.test.tsx
+++ b/app/components/chips/base_chip.test.tsx
@@ -108,4 +108,15 @@ describe('BaseChip', () => {
expect(getByTestId('base_chip').props.entering).toBeUndefined();
expect(getByTestId('base_chip').props.exiting).toBeUndefined();
});
+
+ it('should not have a touchable if onPress is not provided', () => {
+ const {queryByTestId} = renderWithIntlAndTheme(
+ ,
+ );
+
+ expect(queryByTestId('base_chip.chip_button')).toBeNull();
+ });
});
diff --git a/app/components/chips/base_chip.tsx b/app/components/chips/base_chip.tsx
index a1fe166c3..2344289de 100644
--- a/app/components/chips/base_chip.tsx
+++ b/app/components/chips/base_chip.tsx
@@ -1,25 +1,27 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import React from 'react';
-import {Platform, Text, TouchableOpacity, useWindowDimensions} from 'react-native';
+import React, {useMemo} from 'react';
+import {Platform, Text, TouchableOpacity} from 'react-native';
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated';
import CompassIcon from '@components/compass_icon';
import {useTheme} from '@context/theme';
+import {useWindowDimensions} from '@hooks/device';
import {nonBreakingString} from '@utils/strings';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
-import {CHIP_BOTTOM_MARGIN, CHIP_HEIGHT} from './constants';
+import {CHIP_HEIGHT} from './constants';
type SelectedChipProps = {
- onPress: () => void;
+ onPress?: () => void;
testID?: string;
showRemoveOption?: boolean;
showAnimation?: boolean;
label: string;
prefix?: JSX.Element;
+ maxWidth?: number;
}
const FADE_DURATION = 100;
@@ -33,18 +35,20 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
borderRadius: 16,
height: CHIP_HEIGHT,
backgroundColor: changeOpacity(theme.centerChannelColor, 0.08),
- marginBottom: CHIP_BOTTOM_MARGIN,
- marginRight: 8,
- paddingHorizontal: 7,
+ padding: 2,
},
text: {
- marginLeft: 8,
color: theme.centerChannelColor,
- ...typography('Body', 100, 'SemiBold'),
+ ...typography('Body', 100),
},
remove: {
justifyContent: 'center',
- marginLeft: 7,
+ marginLeft: 5,
+ marginRight: 4,
+ },
+ chipContent: {
+ flexDirection: 'row',
+ alignItems: 'center',
},
};
});
@@ -56,16 +60,25 @@ export default function BaseChip({
showAnimation,
label,
prefix,
+ maxWidth,
}: SelectedChipProps) {
const theme = useTheme();
const style = getStyleFromTheme(theme);
const dimensions = useWindowDimensions();
+ const textStyle = useMemo(() => {
+ // We set the max width to 70% of the screen width to make sure
+ // text like names get ellipsized correctly.
+ const textMaxWidth = maxWidth || dimensions.width * 0.70;
+ const marginRight = showRemoveOption ? undefined : 7;
+ const marginLeft = prefix ? 5 : 7;
+ return [style.text, {maxWidth: textMaxWidth, marginRight, marginLeft}];
+ }, [maxWidth, dimensions.width, showRemoveOption, style.text, prefix]);
const chipContent = (
<>
{prefix}
@@ -74,7 +87,7 @@ export default function BaseChip({
>
);
- let content;
+ let content = chipContent;
if (showRemoveOption) {
content = (
<>
@@ -86,16 +99,16 @@ export default function BaseChip({
>
>
);
- } else {
+ } else if (onPress) {
content = (
diff --git a/app/components/chips/constants.ts b/app/components/chips/constants.ts
index a8bdf3dc4..8dc10e6a5 100644
--- a/app/components/chips/constants.ts
+++ b/app/components/chips/constants.ts
@@ -1,5 +1,4 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-export const CHIP_HEIGHT = 32;
-export const CHIP_BOTTOM_MARGIN = 8;
+export const CHIP_HEIGHT = 24;
diff --git a/app/components/chips/user_chip.tsx b/app/components/chips/user_chip.tsx
index 651a30456..eda03a302 100644
--- a/app/components/chips/user_chip.tsx
+++ b/app/components/chips/user_chip.tsx
@@ -41,6 +41,7 @@ export default function UserChip({
size={20}
iconSize={20}
testID={`${testID}.profile_picture`}
+ showStatus={false}
/>
), [testID, user]);
diff --git a/app/components/floating_text_chips_input/index.tsx b/app/components/floating_text_chips_input/index.tsx
index d977a48b5..1092c507d 100644
--- a/app/components/floating_text_chips_input/index.tsx
+++ b/app/components/floating_text_chips_input/index.tsx
@@ -224,6 +224,7 @@ const FloatingTextChipsInput = forwardRef[(({
res.push({
borderWidth: focusedLabel ? BORDER_FOCUSED_WIDTH : BORDER_DEFAULT_WIDTH,
minHeight: (CHIP_HEIGHT * 2.5) + ((focusedLabel ? BORDER_FOCUSED_WIDTH : BORDER_DEFAULT_WIDTH) * 2),
+ gap: 8,
});
if (focused) {
diff --git a/app/components/post_list/post/header/display_name/index.tsx b/app/components/post_list/post/header/display_name/index.tsx
index 3a7b4e576..bd67117e8 100644
--- a/app/components/post_list/post/header/display_name/index.tsx
+++ b/app/components/post_list/post/header/display_name/index.tsx
@@ -34,7 +34,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
displayName: {
color: theme.centerChannelColor,
flexGrow: 1,
- marginRight: 5,
...typography('Body', 200, 'SemiBold'),
},
displayNameCustomEmojiWidth: {
diff --git a/app/components/post_list/post/header/header.tsx b/app/components/post_list/post/header/header.tsx
index df5080f99..824c6ecb0 100644
--- a/app/components/post_list/post/header/header.tsx
+++ b/app/components/post_list/post/header/header.tsx
@@ -59,24 +59,19 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
wrapper: {
flex: 1,
flexDirection: 'row',
+ alignItems: 'center',
+ gap: 5,
},
time: {
color: theme.centerChannelColor,
- marginTop: 5,
opacity: 0.5,
...typography('Body', 75, 'Regular'),
},
visibleToYou: {
color: theme.centerChannelColor,
- marginTop: 5,
- marginLeft: 5,
opacity: 0.5,
...typography('Body', 75, 'Regular'),
},
- postPriority: {
- alignSelf: 'center',
- marginLeft: 6,
- },
};
});
@@ -142,11 +137,9 @@ const Header = (props: HeaderProps) => {
/>
)}
{showPostPriority && post.metadata?.priority?.priority && (
-
-
-
+
)}
{!isCRTEnabled && showReply && commentCount > 0 &&
{
return {
replyWrapper: {
flex: 1,
- justifyContent: 'flex-end',
},
replyIconContainer: {
flexDirection: 'row',
- alignItems: 'flex-start',
+ alignItems: 'center',
justifyContent: 'flex-end',
- minWidth: 40,
paddingTop: 2,
flex: 1,
},
@@ -47,10 +45,10 @@ const HeaderReply = ({commentCount, location, post, theme}: HeaderReplyProps) =>
const style = getStyleSheet(theme);
const serverUrl = useServerUrl();
- const onPress = useCallback(preventDoubleTap(() => {
+ const onPress = usePreventDoubleTap(useCallback(() => {
const rootId = post.rootId || post.id;
fetchAndSwitchToThread(serverUrl, rootId);
- }), [serverUrl]);
+ }, [post.id, post.rootId, serverUrl]));
return (
{
if (isAutomation) {
return (
-
+
);
} else if (showGuestTag) {
return (
-
+
);
} else if (isAutoResponder) {
return (
);
}
diff --git a/app/components/post_priority/post_priority_label.test.tsx b/app/components/post_priority/post_priority_label.test.tsx
new file mode 100644
index 000000000..476429008
--- /dev/null
+++ b/app/components/post_priority/post_priority_label.test.tsx
@@ -0,0 +1,48 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React from 'react';
+
+import Tag from '@components/tag';
+import {PostPriorityType} from '@constants/post';
+import {renderWithIntlAndTheme} from '@test/intl-test-helper';
+
+import PostPriorityLabel from './post_priority_label';
+
+jest.mock('@components/tag', () => ({
+ __esModule: true,
+ default: jest.fn(),
+}));
+jest.mocked(Tag).mockImplementation((props) => React.createElement('Tag', {...props}));
+
+describe('PostPriorityLabel', () => {
+ it('should return null for standard priority', () => {
+ const {toJSON} = renderWithIntlAndTheme(
+ );
+ expect(toJSON()).toBeNull();
+ });
+
+ it('should render urgent priority label correctly', () => {
+ const {getByTestId} = renderWithIntlAndTheme(
+ );
+
+ const tag = getByTestId('urgent_post_priority_label');
+ expect(tag.props.message.id).toBe('post_priority.label.urgent');
+ expect(tag.props.icon).toBe('alert-outline');
+ expect(tag.props.type).toBe('danger');
+ expect(tag.props.size).toBe('xs');
+ expect(tag.props.uppercase).toBe(true);
+ });
+
+ it('should render important priority label correctly', () => {
+ const {getByTestId} = renderWithIntlAndTheme(
+ );
+
+ const tag = getByTestId('important_post_priority_label');
+ expect(tag.props.message.id).toBe('post_priority.label.important');
+ expect(tag.props.icon).toBe('alert-circle-outline');
+ expect(tag.props.type).toBe('info');
+ expect(tag.props.size).toBe('xs');
+ expect(tag.props.uppercase).toBe(true);
+ });
+});
diff --git a/app/components/post_priority/post_priority_label.tsx b/app/components/post_priority/post_priority_label.tsx
index 8218757e5..fefe19516 100644
--- a/app/components/post_priority/post_priority_label.tsx
+++ b/app/components/post_priority/post_priority_label.tsx
@@ -2,68 +2,42 @@
// See LICENSE.txt for license information.
import React from 'react';
-import {useIntl} from 'react-intl';
-import {type StyleProp, StyleSheet, Text, View, type ViewStyle} from 'react-native';
+import {defineMessages} from 'react-intl';
-import CompassIcon from '@components/compass_icon';
-import {PostPriorityColors, PostPriorityType} from '@constants/post';
-import {typography} from '@utils/typography';
-
-const style = StyleSheet.create({
- container: {
- alignSelf: 'flex-start',
- flexDirection: 'row',
- borderRadius: 4,
- alignItems: 'center',
- paddingHorizontal: 4,
- },
- urgent: {
- backgroundColor: PostPriorityColors.URGENT,
- },
- important: {
- backgroundColor: PostPriorityColors.IMPORTANT,
- },
- label: {
- color: '#fff',
- ...typography('Body', 25, 'SemiBold'),
- },
- icon: {
- color: '#fff',
- fontSize: 12,
- marginRight: 4,
- },
-});
+import Tag from '@components/tag';
+import {PostPriorityType} from '@constants/post';
type Props = {
label: PostPriority['priority'];
};
-const PostPriorityLabel = ({label}: Props) => {
- const intl = useIntl();
+const messages = defineMessages({
+ urgent: {
+ id: 'post_priority.label.urgent',
+ defaultMessage: 'URGENT',
+ },
+ important: {
+ id: 'post_priority.label.important',
+ defaultMessage: 'IMPORTANT',
+ },
+});
- const containerStyle: StyleProp = [style.container];
- let iconName = '';
- let labelText = '';
- if (label === PostPriorityType.URGENT) {
- containerStyle.push(style.urgent);
- iconName = 'alert-outline';
- labelText = intl.formatMessage({id: 'post_priority.label.urgent', defaultMessage: 'URGENT'});
- } else if (label === PostPriorityType.IMPORTANT) {
- containerStyle.push(style.important);
- iconName = 'alert-circle-outline';
- labelText = intl.formatMessage({id: 'post_priority.label.important', defaultMessage: 'IMPORTANT'});
+const PostPriorityLabel = ({label}: Props) => {
+ if (label === PostPriorityType.STANDARD) {
+ return null;
}
+
+ const isUrgent = label === PostPriorityType.URGENT; // else it is important
+
return (
-
-
- {labelText}
-
+ uppercase={true}
+ />
);
};
diff --git a/app/components/section_notice/__snapshots__/index.test.tsx.snap b/app/components/section_notice/__snapshots__/index.test.tsx.snap
index f29160dab..7eeffbc31 100644
--- a/app/components/section_notice/__snapshots__/index.test.tsx.snap
+++ b/app/components/section_notice/__snapshots__/index.test.tsx.snap
@@ -5,7 +5,6 @@ exports[`Section notice match snapshot 1`] = `
style={
[
{
- "borderRadius": 4,
"borderStyle": "solid",
"borderWidth": 1,
},
@@ -13,7 +12,9 @@ exports[`Section notice match snapshot 1`] = `
"backgroundColor": "rgba(93,137,234,0.08)",
"borderColor": "rgba(93,137,234,0.16)",
},
- undefined,
+ {
+ "borderRadius": 4,
+ },
]
}
testID="sectionNoticeContainer"
diff --git a/app/components/section_notice/index.tsx b/app/components/section_notice/index.tsx
index 5410c9dc8..c07287169 100644
--- a/app/components/section_notice/index.tsx
+++ b/app/components/section_notice/index.tsx
@@ -2,10 +2,11 @@
// See LICENSE.txt for license information.
import React, {useMemo} from 'react';
-import {Pressable, Text, View, type StyleProp, type ViewStyle} from 'react-native';
+import {Pressable, Text, View} from 'react-native';
import Tag from '@components/tag';
import {useTheme} from '@context/theme';
+import {getMarkdownBlockStyles, getMarkdownTextStyles} from '@utils/markdown';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
@@ -27,10 +28,8 @@ type Props = {
onDismissClick?: () => void;
location: AvailableScreens;
tags?: string[];
- tagsVariant?: 'default' | 'subtle';
testID?: string;
- containerStyle?: StyleProp;
- iconSize?: number;
+ squareCorners?: boolean;
}
const iconByType = {
@@ -47,6 +46,8 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
container: {
borderWidth: 1,
borderStyle: 'solid',
+ },
+ roundCorners: {
borderRadius: 4,
},
content: {
@@ -150,6 +151,7 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
tagsContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
+ gap: 4,
},
};
@@ -166,10 +168,8 @@ const SectionNotice = ({
type = 'info',
location,
tags,
- tagsVariant = 'default',
+ squareCorners,
testID,
- containerStyle,
- iconSize = 20,
}: Props) => {
const theme = useTheme();
const styles = getStyleFromTheme(theme);
@@ -182,9 +182,9 @@ const SectionNotice = ({
const combinedContainerStyle = useMemo(() => [
styles.container,
styles[`${type}Container`],
- containerStyle,
- ], [type, containerStyle]);
- const iconStyle = useMemo(() => styles[`${type}Icon`], [type]);
+ !squareCorners && styles.roundCorners,
+ ], [styles, type, squareCorners]);
+ const iconStyle = useMemo(() => styles[`${type}Icon`], [styles, type]);
return (
)}
@@ -206,6 +206,8 @@ const SectionNotice = ({
theme={theme}
location={location}
baseTextStyle={styles.baseText}
+ textStyles={getMarkdownTextStyles(theme)}
+ blockStyles={getMarkdownBlockStyles(theme)}
value={text}
/>
)}
@@ -214,9 +216,8 @@ const SectionNotice = ({
{tags.map((tag) => (
))}
diff --git a/app/components/selected_users/index.tsx b/app/components/selected_users/index.tsx
index 1cb4d3550..a6238234d 100644
--- a/app/components/selected_users/index.tsx
+++ b/app/components/selected_users/index.tsx
@@ -7,7 +7,7 @@ import Animated, {useAnimatedStyle, useDerivedValue, useSharedValue, withTiming}
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import Button from '@components/button';
-import {CHIP_BOTTOM_MARGIN, CHIP_HEIGHT} from '@components/chips/constants';
+import {CHIP_HEIGHT} from '@components/chips/constants';
import SelectedUserChip from '@components/chips/selected_user_chip';
import Toast from '@components/toast';
import {useTheme} from '@context/theme';
@@ -83,7 +83,8 @@ type Props = {
}
const BUTTON_HEIGHT = 48;
-const CHIP_HEIGHT_WITH_MARGIN = CHIP_HEIGHT + CHIP_BOTTOM_MARGIN;
+const CHIP_GAP = 8;
+const CHIP_HEIGHT_WITH_MARGIN = CHIP_HEIGHT + CHIP_GAP;
const EXPOSED_CHIP_HEIGHT = 0.33 * CHIP_HEIGHT;
const MAX_CHIP_ROWS = 2;
const SCROLL_MARGIN_TOP = 20;
@@ -125,6 +126,7 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
flexDirection: 'row',
flexGrow: 1,
flexWrap: 'wrap',
+ gap: CHIP_GAP,
},
message: {
color: theme.centerChannelBg,
diff --git a/app/components/tag/base_tag.test.tsx b/app/components/tag/base_tag.test.tsx
new file mode 100644
index 000000000..34deba26a
--- /dev/null
+++ b/app/components/tag/base_tag.test.tsx
@@ -0,0 +1,49 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React from 'react';
+
+import {renderWithIntlAndTheme} from '@test/intl-test-helper';
+
+import Tag from './base_tag';
+
+describe('Tag', () => {
+ const defaultProps = {
+ message: 'Test Tag',
+ };
+
+ it('should render correctly', () => {
+ const {getByText} = renderWithIntlAndTheme();
+ expect(getByText('Test Tag')).toBeDefined();
+ });
+
+ it('should render icon correctly', () => {
+ const {getByTestId, rerender, queryByTestId} = renderWithIntlAndTheme(
+ ,
+ );
+ expect(getByTestId('test-tag.icon')).toBeDefined();
+
+ rerender(
+ ,
+ );
+ expect(queryByTestId('test-tag.icon')).toBeNull();
+ });
+
+ it('should render uppercase correctly', () => {
+ const {getByText} = renderWithIntlAndTheme(
+ ,
+ );
+ expect(getByText('Test Tag')).toBeDefined();
+ expect(getByText('Test Tag')).toHaveStyle({textTransform: 'uppercase'});
+ });
+});
diff --git a/app/components/tag/base_tag.tsx b/app/components/tag/base_tag.tsx
new file mode 100644
index 000000000..6e179f2c8
--- /dev/null
+++ b/app/components/tag/base_tag.tsx
@@ -0,0 +1,154 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {useMemo} from 'react';
+import {Text, View} from 'react-native';
+
+import CompassIcon from '@components/compass_icon';
+import FormattedText from '@components/formatted_text';
+import {useTheme} from '@context/theme';
+import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
+import {typography, type FontSizes} from '@utils/typography';
+
+import type {MessageDescriptor} from 'react-intl';
+
+type TagProps = {
+ message: MessageDescriptor | string;
+ icon?: string;
+ type?: 'general' | 'info' | 'danger' | 'success' | 'warning' | 'infoDim';
+ size?: 'xs' | 's' | 'm';
+ uppercase?: boolean;
+ testID?: string;
+}
+
+const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => {
+ return {
+ container: {
+ borderRadius: 4,
+ paddingVertical: 2,
+ paddingHorizontal: 4,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ generalContainer: {
+ backgroundColor: changeOpacity(theme.centerChannelColor, 0.08),
+ },
+ generalText: {
+ color: theme.centerChannelColor,
+ },
+ infoContainer: {
+ backgroundColor: theme.buttonBg,
+ },
+ infoText: {
+ color: theme.buttonColor,
+ },
+ dangerContainer: {
+ backgroundColor: theme.dndIndicator,
+ },
+ dangerText: {
+ color: theme.buttonColor,
+ },
+ successContainer: {
+ backgroundColor: theme.onlineIndicator,
+ },
+ successText: {
+ color: theme.buttonColor,
+ },
+ warningContainer: {
+ backgroundColor: theme.awayIndicator,
+ },
+ warningText: {
+ color: theme.buttonColor,
+ },
+ infoDimContainer: {
+ backgroundColor: changeOpacity(theme.buttonBg, 0.12),
+ },
+ infoDimText: {
+ color: theme.buttonBg,
+ },
+ };
+});
+
+const textTypographyPerSize: Record['size'], FontSizes> = {
+ xs: 25,
+ s: 75,
+ m: 100,
+};
+
+const iconSizePerSize: Record['size'], number> = {
+ xs: 10,
+ s: 12,
+ m: 14,
+};
+
+const Tag = ({
+ message,
+ icon,
+ type = 'general',
+ size = 's',
+ uppercase = false,
+ testID,
+}: TagProps) => {
+ const theme = useTheme();
+ const styles = getStyleFromTheme(theme);
+
+ const textStyle = useMemo(() => {
+ const sizeRelated = typography('Heading', textTypographyPerSize[size]);
+ const colorRelated = styles[`${type}Text`];
+ const uppercaseRelated = uppercase ? {textTransform: 'uppercase' as const} : {};
+ return [sizeRelated, colorRelated, uppercaseRelated];
+ }, [size, styles, type, uppercase]);
+
+ const containerStyle = useMemo(() => {
+ const colorRelated = styles[`${type}Container`];
+ return [styles.container, colorRelated];
+ }, [styles, type]);
+
+ let iconComponent;
+ if (icon) {
+ iconComponent = (
+
+ );
+ }
+ let textComponent;
+ if (typeof message === 'string') {
+ textComponent = (
+
+ {message}
+
+ );
+ } else {
+ textComponent = (
+
+ );
+ }
+
+ return (
+
+ {/* We wrap the icon and text in a Text component to avoid
+ the ellipsis to go out of the box on iOS */}
+
+ {iconComponent}
+ {' '}
+ {textComponent}
+
+
+ );
+};
+
+export default Tag;
diff --git a/app/components/tag/bot_tag.test.tsx b/app/components/tag/bot_tag.test.tsx
new file mode 100644
index 000000000..a7d29b79e
--- /dev/null
+++ b/app/components/tag/bot_tag.test.tsx
@@ -0,0 +1,46 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React from 'react';
+
+import {renderWithIntlAndTheme} from '@test/intl-test-helper';
+
+import Tag from './base_tag';
+import BotTag from './bot_tag';
+
+jest.mock('./base_tag', () => ({
+ __esModule: true,
+ default: jest.fn(),
+}));
+jest.mocked(Tag).mockImplementation((props) => React.createElement('Tag', {...props}));
+
+describe('BotTag', () => {
+ it('should render with the correct props', () => {
+ const {getByTestId} = renderWithIntlAndTheme(
+ ,
+ );
+
+ const tag = getByTestId('bot-tag');
+ expect(tag.props.message.id).toBe('post_info.bot');
+ expect(tag.props.message.defaultMessage).toBe('Bot');
+ expect(tag.props.uppercase).toBe(true);
+ expect(tag.props.size).toBe('m');
+ });
+
+ it('should render with default props', () => {
+ const {getByTestId} = renderWithIntlAndTheme(
+ ,
+ );
+
+ const tag = getByTestId('bot-tag');
+ expect(tag.props.message.id).toBe('post_info.bot');
+ expect(tag.props.message.defaultMessage).toBe('Bot');
+ expect(tag.props.uppercase).toBe(true);
+ expect(tag.props.size).toBeUndefined();
+ });
+});
diff --git a/app/components/tag/bot_tag.tsx b/app/components/tag/bot_tag.tsx
new file mode 100644
index 000000000..b18dfd4fe
--- /dev/null
+++ b/app/components/tag/bot_tag.tsx
@@ -0,0 +1,30 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {type ComponentProps} from 'react';
+import {defineMessage} from 'react-intl';
+
+import Tag from './base_tag';
+
+const botMessage = defineMessage({
+ id: 'post_info.bot',
+ defaultMessage: 'Bot',
+});
+
+type BotTagProps = Omit, 'message' | 'icon' | 'type'>;
+
+const BotTag = ({
+ size,
+ testID,
+}: BotTagProps) => {
+ return (
+
+ );
+};
+
+export default BotTag;
diff --git a/app/components/tag/guest_tag.test.tsx b/app/components/tag/guest_tag.test.tsx
new file mode 100644
index 000000000..433c3fba3
--- /dev/null
+++ b/app/components/tag/guest_tag.test.tsx
@@ -0,0 +1,46 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React from 'react';
+
+import {renderWithIntlAndTheme} from '@test/intl-test-helper';
+
+import Tag from './base_tag';
+import GuestTag from './guest_tag';
+
+jest.mock('./base_tag', () => ({
+ __esModule: true,
+ default: jest.fn(),
+}));
+jest.mocked(Tag).mockImplementation((props) => React.createElement('Tag', {...props}));
+
+describe('GuestTag', () => {
+ it('should render with the correct props', () => {
+ const {getByTestId} = renderWithIntlAndTheme(
+ ,
+ );
+
+ const tag = getByTestId('guest-tag');
+ expect(tag.props.message.id).toBe('post_info.guest');
+ expect(tag.props.message.defaultMessage).toBe('Guest');
+ expect(tag.props.uppercase).toBe(true);
+ expect(tag.props.size).toBe('m');
+ });
+
+ it('should render with default props', () => {
+ const {getByTestId} = renderWithIntlAndTheme(
+ ,
+ );
+
+ const tag = getByTestId('guest-tag');
+ expect(tag.props.message.id).toBe('post_info.guest');
+ expect(tag.props.message.defaultMessage).toBe('Guest');
+ expect(tag.props.uppercase).toBe(true);
+ expect(tag.props.size).toBeUndefined();
+ });
+});
diff --git a/app/components/tag/guest_tag.tsx b/app/components/tag/guest_tag.tsx
new file mode 100644
index 000000000..180a5e970
--- /dev/null
+++ b/app/components/tag/guest_tag.tsx
@@ -0,0 +1,30 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {type ComponentProps} from 'react';
+import {defineMessage} from 'react-intl';
+
+import Tag from './base_tag';
+
+const botMessage = defineMessage({
+ id: 'post_info.guest',
+ defaultMessage: 'Guest',
+});
+
+type GuestTagProps = Omit, 'message' | 'icon' | 'type'>;
+
+const GuestTag = ({
+ testID,
+ size,
+}: GuestTagProps) => {
+ return (
+
+ );
+};
+
+export default GuestTag;
diff --git a/app/components/tag/index.tsx b/app/components/tag/index.tsx
index aa6dba441..eff56bebf 100644
--- a/app/components/tag/index.tsx
+++ b/app/components/tag/index.tsx
@@ -1,116 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import React from 'react';
-import {type StyleProp, type TextStyle, View, type ViewStyle} from 'react-native';
-
-import FormattedText from '@components/formatted_text';
-import {useTheme} from '@context/theme';
-import {t} from '@i18n';
-import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
-
-type TagProps = {
- id: string;
- defaultMessage: string;
- inTitle?: boolean;
- show?: boolean;
- style?: StyleProp;
- testID?: string;
- textStyle?: StyleProp;
- variant?: 'default' | 'subtle';
-}
-
-const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => {
- return {
- container: {
- alignSelf: 'center',
- backgroundColor: changeOpacity(theme.centerChannelColor, 0.08),
- borderRadius: 4,
- paddingVertical: 2,
- paddingHorizontal: 4,
- },
- text: {
- color: theme.centerChannelColor,
- fontFamily: 'OpenSans-SemiBold',
- fontSize: 10,
- textTransform: 'uppercase',
- },
- title: {
- backgroundColor: changeOpacity(theme.sidebarHeaderTextColor, 0.15),
- color: changeOpacity(theme.sidebarHeaderTextColor, 0.6),
- },
-
- // Variant styles
- subtleContainer: {
- borderRadius: 6,
- paddingVertical: 4,
- paddingHorizontal: 8,
- marginRight: 8,
- marginBottom: 8,
- },
- subtleText: {
- fontSize: 12,
- textTransform: 'none',
- },
- };
-});
-
-export function BotTag(props: Omit) {
- const id = t('post_info.bot');
- const defaultMessage = 'Bot';
-
- return (
-
- );
-}
-
-export function GuestTag(props: Omit) {
- const id = t('post_info.guest');
- const defaultMessage = 'Guest';
-
- return (
-
- );
-}
-
-const Tag = ({id, defaultMessage, inTitle, show = true, style, testID, textStyle, variant = 'default'}: TagProps) => {
- const theme = useTheme();
-
- if (!show) {
- return null;
- }
-
- const styles = getStyleFromTheme(theme);
-
- return (
-
-
-
- );
-};
+import Tag from './base_tag';
+import BotTag from './bot_tag';
+import GuestTag from './guest_tag';
export default Tag;
+export {
+ GuestTag,
+ BotTag,
+};
diff --git a/app/components/user_item/user_item.tsx b/app/components/user_item/user_item.tsx
index a3f4a9e9a..3fe23d2f0 100644
--- a/app/components/user_item/user_item.tsx
+++ b/app/components/user_item/user_item.tsx
@@ -76,16 +76,12 @@ const nonThemedStyles = StyleSheet.create({
rowInfoContainer: {
flex: 1,
flexDirection: 'row',
- },
- icon: {
- marginLeft: 4,
+ alignItems: 'center',
+ gap: 4,
},
profile: {
marginRight: 12,
},
- tag: {
- marginLeft: 6,
- },
flex: {
flex: 1,
},
@@ -201,29 +197,19 @@ const UserItem = ({
)}
]
{showBadges && bot && (
-
+
)}
{showBadges && guest && !hideGuestTags && (
-
+
)}
{Boolean(isCustomStatusEnabled && !bot && customStatus?.emoji && !customStatusExpired) && (
-
+
)}
{shared && (
)}
diff --git a/app/components/user_list/__snapshots__/index.test.tsx.snap b/app/components/user_list/__snapshots__/index.test.tsx.snap
index a829e03ba..babe43ddc 100644
--- a/app/components/user_list/__snapshots__/index.test.tsx.snap
+++ b/app/components/user_list/__snapshots__/index.test.tsx.snap
@@ -814,8 +814,10 @@ exports[`components/channel_list_row should show results and tutorial 1`] = `
@@ -1134,8 +1136,10 @@ exports[`components/channel_list_row should show results no tutorial 1`] = `
@@ -1492,8 +1496,10 @@ exports[`components/channel_list_row should show results no tutorial 2 users 1`]
@@ -1711,8 +1717,10 @@ exports[`components/channel_list_row should show results no tutorial 2 users 1`]
diff --git a/app/products/calls/screens/participants_list/participant.tsx b/app/products/calls/screens/participants_list/participant.tsx
index 373d3d572..92c1704a0 100644
--- a/app/products/calls/screens/participants_list/participant.tsx
+++ b/app/products/calls/screens/participants_list/participant.tsx
@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import React from 'react';
-import {useIntl} from 'react-intl';
+import {defineMessage, useIntl} from 'react-intl';
import {Platform, Text, TouchableOpacity, View} from 'react-native';
import {useCurrentCall} from '@calls/state';
@@ -65,9 +65,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
unmutedIcon: {
color: changeOpacity(theme.centerChannelColor, 0.56),
},
- hostTag: {
- paddingVertical: 4,
- },
raiseHandIcon: {
color: theme.awayIndicator,
},
@@ -76,6 +73,11 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
},
}));
+const hostMessage = defineMessage({
+ id: 'mobile.calls_host',
+ defaultMessage: 'host',
+});
+
export const Participant = ({sess, teammateNameDisplay, onPress}: Props) => {
const intl = useIntl();
const currentCall = useCurrentCall();
@@ -122,9 +124,8 @@ export const Participant = ({sess, teammateNameDisplay, onPress}: Props) => {
}
{sess.userId === currentCall.hostId &&
}
diff --git a/app/screens/channel/channel_post_list/intro/direct_channel/direct_channel.tsx b/app/screens/channel/channel_post_list/intro/direct_channel/direct_channel.tsx
index a2c8d4df2..bf31d989e 100644
--- a/app/screens/channel/channel_post_list/intro/direct_channel/direct_channel.tsx
+++ b/app/screens/channel/channel_post_list/intro/direct_channel/direct_channel.tsx
@@ -34,13 +34,10 @@ type Props = {
}
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
- botContainer: {
- alignSelf: 'flex-end',
- bottom: 7.5,
- height: 20,
- marginBottom: 0,
- marginLeft: 4,
- paddingVertical: 0,
+ displayNameContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: 4,
},
botText: {
fontSize: 14,
@@ -164,7 +161,7 @@ const DirectChannel = ({
{getGMIntroMessageSpecificPart(userNotifyProps, channelNotifyProps, styles.boldText)}
);
- }, [channel.displayName, theme, channelNotifyProps, userNotifyProps]);
+ }, [channel.type, channel.displayName, hasGMasDMFeature, styles.message, styles.boldText, userNotifyProps, channelNotifyProps]);
const profiles = useMemo(() => {
if (channel.type === General.DM_CHANNEL) {
@@ -195,14 +192,14 @@ const DirectChannel = ({
userIds={channelMembers.map((cm) => cm.userId)}
/>
);
- }, [members, theme]);
+ }, [channel.id, channel.name, channel.type, currentUserId, members, theme]);
return (
{profiles}
-
+
{isBot &&
-
+
}
{message}
diff --git a/app/screens/channel_add_members/channel_add_members.tsx b/app/screens/channel_add_members/channel_add_members.tsx
index 29a35837f..2b93f866e 100644
--- a/app/screens/channel_add_members/channel_add_members.tsx
+++ b/app/screens/channel_add_members/channel_add_members.tsx
@@ -288,9 +288,7 @@ export default function ChannelAddMembers({
onDismissClick={handleDismissBanner}
location={Screens.CHANNEL_ADD_MEMBERS}
testID={`${TEST_ID}.notice`}
- containerStyle={style.flatBottomBanner}
- iconSize={24}
- tagsVariant='subtle'
+ squareCorners={true}
/>
)}
diff --git a/app/screens/channel_info/title/direct_message/direct_message.tsx b/app/screens/channel_info/title/direct_message/direct_message.tsx
index a58eb5692..2d72362cb 100644
--- a/app/screens/channel_info/title/direct_message/direct_message.tsx
+++ b/app/screens/channel_info/title/direct_message/direct_message.tsx
@@ -25,18 +25,13 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
},
displayName: {
flexDirection: 'row',
+ gap: 12,
+ alignItems: 'center',
},
position: {
color: changeOpacity(theme.centerChannelColor, 0.72),
...typography('Body', 200),
},
- tagContainer: {
- marginLeft: 12,
- },
- tag: {
- color: theme.centerChannelColor,
- ...typography('Body', 100, 'SemiBold'),
- },
titleContainer: {
flex: 1,
marginLeft: 16,
@@ -81,15 +76,13 @@ const DirectMessage = ({
{user?.isGuest && !hideGuestTags &&
}
{user?.isBot &&
}
diff --git a/app/screens/component_library/button.cl.tsx b/app/screens/component_library/button.cl.tsx
index 1b924b4b2..905210067 100644
--- a/app/screens/component_library/button.cl.tsx
+++ b/app/screens/component_library/button.cl.tsx
@@ -14,8 +14,6 @@ const propPossibilities = {};
const buttonSizeValues = ['xs', 's', 'm', 'lg'];
const buttonEmphasisValues = ['primary', 'secondary', 'tertiary', 'link'];
-const buttonTypeValues = ['default', 'destructive', 'inverted', 'disabled'];
-const buttonStateValues = ['default', 'hover', 'active', 'focus'];
const onPress = () => Alert.alert('Button pressed!');
@@ -25,28 +23,32 @@ const ButtonComponentLibrary = () => {
const [disabled, disabledSelector] = useBooleanProp('disabled', false);
const [buttonSize, buttonSizePosibilities, buttonSizeSelector] = useDropdownProp('size', 'm', buttonSizeValues, true);
const [buttonEmphasis, buttonEmphasisPosibilities, buttonEmphasisSelector] = useDropdownProp('emphasis', 'primary', buttonEmphasisValues, true);
- const [buttonType, buttonTypePosibilities, buttonTypeSelector] = useDropdownProp('buttonType', 'default', buttonTypeValues, true);
- const [buttonState, buttonStatePosibilities, buttonStateSelector] = useDropdownProp('buttonState', 'default', buttonStateValues, true);
+ const [iconName, iconNameSelector] = useStringProp('iconName', '', true);
+ const [isIconOnTheRight, isIconOnTheRightSelector] = useBooleanProp('isIconOnTheRight', false);
+ const [showLoader, showLoaderSelector] = useBooleanProp('showLoader', false);
+ const [isInverted, isInvertedSelector] = useBooleanProp('isInverted', false);
+ const [isDestructive, isDestructiveSelector] = useBooleanProp('isDestructive', false);
const components = useMemo(
() => buildComponent(Button, propPossibilities, [
buttonSizePosibilities,
buttonEmphasisPosibilities,
- buttonTypePosibilities,
- buttonStatePosibilities,
], [
text,
disabled,
buttonSize,
buttonEmphasis,
- buttonType,
- buttonState,
+ iconName,
+ isIconOnTheRight,
+ showLoader,
+ isInverted,
+ isDestructive,
{
theme,
onPress,
},
]),
- [buttonEmphasis, buttonEmphasisPosibilities, buttonSize, buttonSizePosibilities, buttonState, buttonStatePosibilities, buttonType, buttonTypePosibilities, disabled, text, theme],
+ [buttonEmphasis, buttonEmphasisPosibilities, buttonSize, buttonSizePosibilities, disabled, text, theme, iconName, isIconOnTheRight, showLoader, isInverted, isDestructive],
);
return (
@@ -55,8 +57,11 @@ const ButtonComponentLibrary = () => {
{disabledSelector}
{buttonSizeSelector}
{buttonEmphasisSelector}
- {buttonTypeSelector}
- {buttonStateSelector}
+ {iconNameSelector}
+ {isIconOnTheRightSelector}
+ {showLoaderSelector}
+ {isInvertedSelector}
+ {isDestructiveSelector}
{components}
>
);
diff --git a/app/screens/component_library/chip.cl.tsx b/app/screens/component_library/chip.cl.tsx
new file mode 100644
index 000000000..75b6c7b90
--- /dev/null
+++ b/app/screens/component_library/chip.cl.tsx
@@ -0,0 +1,55 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {useMemo} from 'react';
+import {Alert, View} from 'react-native';
+
+import BaseChip from '@components/chips/base_chip';
+import CompassIcon from '@components/compass_icon';
+import {useTheme} from '@context/theme';
+
+import {useBooleanProp, useStringProp} from './hooks';
+import {buildComponent} from './utils';
+
+const propPossibilities = {};
+
+const onPress = () => Alert.alert('Button pressed!');
+
+const ChipComponentLibrary = () => {
+ const theme = useTheme();
+ const [label, labelSelector] = useStringProp('label', 'Chip text', false);
+ const [showRemoveOption, showRemoveOptionSelector] = useBooleanProp('showRemoveOption', false);
+ const [hasPrefix, hasPrefixSelector] = useBooleanProp('hasPrefix', false);
+
+ const components = useMemo(
+ () => buildComponent(BaseChip, propPossibilities, [], [
+ label,
+ showRemoveOption,
+ {
+ onPress,
+ prefix: hasPrefix.hasPrefix ? (
+
+ ) : undefined,
+ testID: 'chip-example',
+ theme,
+ },
+ ]),
+ [label, showRemoveOption, hasPrefix.hasPrefix, theme],
+ );
+
+ return (
+ <>
+ {labelSelector}
+ {showRemoveOptionSelector}
+ {hasPrefixSelector}
+ {components}
+ >
+ );
+};
+
+export default ChipComponentLibrary;
diff --git a/app/screens/component_library/index.tsx b/app/screens/component_library/index.tsx
index e4b992dba..e3c373327 100644
--- a/app/screens/component_library/index.tsx
+++ b/app/screens/component_library/index.tsx
@@ -12,11 +12,17 @@ import SecurityManager from '@managers/security_manager';
import {popTopScreen} from '@screens/navigation';
import ButtonComponentLibrary from './button.cl';
+import ChipComponentLibrary from './chip.cl';
+import SectionNoticeComponentLibrary from './section_notice.cl';
+import TagComponentLibrary from './tag.cl';
import type {AvailableScreens} from '@typings/screens/navigation';
const componentMap = {
Button: ButtonComponentLibrary,
+ Chip: ChipComponentLibrary,
+ Tag: TagComponentLibrary,
+ SectionNotice: SectionNoticeComponentLibrary,
};
type ComponentName = keyof typeof componentMap
diff --git a/app/screens/component_library/section_notice.cl.tsx b/app/screens/component_library/section_notice.cl.tsx
new file mode 100644
index 000000000..7dff03c5c
--- /dev/null
+++ b/app/screens/component_library/section_notice.cl.tsx
@@ -0,0 +1,102 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {useMemo} from 'react';
+import {Alert, View} from 'react-native';
+
+import SectionNotice from '@components/section_notice';
+
+import {useBooleanProp, useDropdownProp, useStringProp} from './hooks';
+import {buildComponent} from './utils';
+
+const propPossibilities = {};
+
+const sectionNoticeTypeValues = ['info', 'success', 'danger', 'welcome', 'warning', 'hint'];
+
+const onButtonPress = (name: string) => Alert.alert(`Button pressed: ${name}`);
+const onDismissPress = () => Alert.alert('Notice dismissed!');
+
+const PRIMARY_BUTTON = {
+ onClick: () => onButtonPress('primary'),
+ text: 'Primary Action',
+};
+
+const SECONDARY_BUTTON = {
+ onClick: () => onButtonPress('secondary'),
+ text: 'Secondary Action',
+};
+
+const LINK_BUTTON = {
+ onClick: () => onButtonPress('link'),
+ text: 'Link Action',
+};
+
+const TAGS = ['tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6', 'tag7', 'tag8', 'tag9', 'tag10'];
+
+const SectionNoticeComponentLibrary = () => {
+ const [title, titleSelector] = useStringProp('title', 'Notice Title', false);
+ const [text, textSelector] = useStringProp('text', 'This is the notice text content with **markdown**.', false);
+ const [isDismissable, isDismissableSelector] = useBooleanProp('isDismissable', false);
+ const [squareCorners, squareCornersSelector] = useBooleanProp('squareCorners', false);
+ const [hasPrimaryButton, hasPrimaryButtonSelector] = useBooleanProp('hasPrimaryButton', false);
+ const [hasSecondaryButton, hasSecondaryButtonSelector] = useBooleanProp('hasSecondaryButton', false);
+ const [hasLinkButton, hasLinkButtonSelector] = useBooleanProp('hasLinkButton', false);
+ const [hasTags, hasTagsSelector] = useBooleanProp('hasTags', false);
+ const [sectionNoticeType, sectionNoticeTypePossibilities, sectionNoticeTypeSelector] = useDropdownProp('type', 'info', sectionNoticeTypeValues, true);
+
+ const primaryButton = hasPrimaryButton.hasPrimaryButton ? PRIMARY_BUTTON : undefined;
+ const secondaryButton = hasSecondaryButton.hasSecondaryButton ? SECONDARY_BUTTON : undefined;
+ const linkButton = hasLinkButton.hasLinkButton ? LINK_BUTTON : undefined;
+
+ const tags = hasTags.hasTags ? TAGS : undefined;
+
+ const components = useMemo(
+ () => buildComponent(SectionNotice, propPossibilities, [
+ sectionNoticeTypePossibilities,
+ ], [
+ title,
+ text,
+ isDismissable,
+ squareCorners,
+ {
+ primaryButton,
+ secondaryButton,
+ linkButton,
+ tags,
+ onDismissClick: isDismissable.isDismissable ? onDismissPress : undefined,
+ location: 'ComponentLibrary',
+ testID: 'section-notice-example',
+ },
+ sectionNoticeType,
+ ]),
+ [
+ sectionNoticeTypePossibilities,
+ title,
+ text,
+ isDismissable,
+ squareCorners,
+ primaryButton,
+ secondaryButton,
+ linkButton,
+ tags,
+ sectionNoticeType,
+ ],
+ );
+
+ return (
+ <>
+ {titleSelector}
+ {textSelector}
+ {isDismissableSelector}
+ {squareCornersSelector}
+ {hasPrimaryButtonSelector}
+ {hasSecondaryButtonSelector}
+ {hasLinkButtonSelector}
+ {hasTagsSelector}
+ {sectionNoticeTypeSelector}
+ {components}
+ >
+ );
+};
+
+export default SectionNoticeComponentLibrary;
diff --git a/app/screens/component_library/tag.cl.tsx b/app/screens/component_library/tag.cl.tsx
new file mode 100644
index 000000000..46bdb1b68
--- /dev/null
+++ b/app/screens/component_library/tag.cl.tsx
@@ -0,0 +1,58 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {useMemo} from 'react';
+import {View} from 'react-native';
+
+import Tag from '@components/tag/base_tag';
+import {useTheme} from '@context/theme';
+
+import {useBooleanProp, useDropdownProp, useStringProp} from './hooks';
+import {buildComponent} from './utils';
+
+const propPossibilities = {};
+
+const tagTypeValues = ['general', 'info', 'danger', 'success', 'warning', 'infoDim'];
+const tagSizeValues = ['xs', 's', 'm'];
+
+const TagComponentLibrary = () => {
+ const theme = useTheme();
+ const [message, messageSelector] = useStringProp('message', 'Tag text', false);
+ const [icon, iconSelector] = useStringProp('icon', 'check', false);
+ const [useIcon, useIconSelector] = useBooleanProp('useIcon', true);
+ const [uppercase, uppercaseSelector] = useBooleanProp('uppercase', true);
+ const [tagType, tagTypePossibilities, tagTypeSelector] = useDropdownProp('type', 'general', tagTypeValues, true);
+ const [tagSize, tagSizePossibilities, tagSizeSelector] = useDropdownProp('size', 's', tagSizeValues, true);
+
+ const components = useMemo(
+ () => buildComponent(Tag, propPossibilities, [
+ tagTypePossibilities,
+ tagSizePossibilities,
+ ], [
+ message,
+ uppercase,
+ {
+ icon: useIcon.useIcon ? icon.icon : undefined,
+ theme,
+ testID: 'tag-example',
+ },
+ tagType,
+ tagSize,
+ ]),
+ [tagTypePossibilities, tagSizePossibilities, message, uppercase, useIcon.useIcon, icon.icon, theme, tagType, tagSize],
+ );
+
+ return (
+ <>
+ {messageSelector}
+ {iconSelector}
+ {useIconSelector}
+ {uppercaseSelector}
+ {tagTypeSelector}
+ {tagSizeSelector}
+ {components}
+ >
+ );
+};
+
+export default TagComponentLibrary;
diff --git a/app/screens/invite/selection.tsx b/app/screens/invite/selection.tsx
index 600c1b061..0c4b02a7f 100644
--- a/app/screens/invite/selection.tsx
+++ b/app/screens/invite/selection.tsx
@@ -96,6 +96,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
flexWrap: 'wrap',
marginHorizontal: 20,
marginVertical: 16,
+ gap: 8,
},
};
});
diff --git a/app/screens/manage_channel_members/manage_channel_members.tsx b/app/screens/manage_channel_members/manage_channel_members.tsx
index c34eabb98..901704d70 100644
--- a/app/screens/manage_channel_members/manage_channel_members.tsx
+++ b/app/screens/manage_channel_members/manage_channel_members.tsx
@@ -316,9 +316,7 @@ export default function ManageChannelMembers({
tags={attributeTags.length > 0 ? attributeTags : undefined}
location={Screens.MANAGE_CHANNEL_MEMBERS}
testID={`${TEST_ID}.notice`}
- containerStyle={styles.flatBottomBanner}
- iconSize={24}
- tagsVariant='subtle'
+ squareCorners={true}
/>
)}
diff --git a/app/screens/settings/notifications/send_test_notification_notice/__snapshots__/send_test_notification_notice.test.tsx.snap b/app/screens/settings/notifications/send_test_notification_notice/__snapshots__/send_test_notification_notice.test.tsx.snap
index d225fb772..daaab225f 100644
--- a/app/screens/settings/notifications/send_test_notification_notice/__snapshots__/send_test_notification_notice.test.tsx.snap
+++ b/app/screens/settings/notifications/send_test_notification_notice/__snapshots__/send_test_notification_notice.test.tsx.snap
@@ -14,7 +14,6 @@ exports[`SendTestNotificationNotice should match snapshot 1`] = `
style={
[
{
- "borderRadius": 4,
"borderStyle": "solid",
"borderWidth": 1,
},
@@ -22,7 +21,9 @@ exports[`SendTestNotificationNotice should match snapshot 1`] = `
"backgroundColor": "rgba(93,137,234,0.08)",
"borderColor": "rgba(93,137,234,0.16)",
},
- undefined,
+ {
+ "borderRadius": 4,
+ },
]
}
testID="sectionNoticeContainer"
diff --git a/app/screens/user_profile/title/index.tsx b/app/screens/user_profile/title/index.tsx
index fd78b72b9..25c6e048f 100644
--- a/app/screens/user_profile/title/index.tsx
+++ b/app/screens/user_profile/title/index.tsx
@@ -48,6 +48,8 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
details: {
marginLeft: 24,
justifyContent: 'center',
+ alignItems: 'flex-start',
+ gap: 4,
flex: 1,
},
displayName: {
diff --git a/app/screens/user_profile/title/tag.tsx b/app/screens/user_profile/title/tag.tsx
index 6cdc2a4c4..12a5ff7d8 100644
--- a/app/screens/user_profile/title/tag.tsx
+++ b/app/screens/user_profile/title/tag.tsx
@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import React from 'react';
-import {StyleSheet} from 'react-native';
+import {defineMessages} from 'react-intl';
import Tag, {BotTag, GuestTag} from '@components/tag';
@@ -14,37 +14,37 @@ type Props = {
isTeamAdmin: boolean;
}
-const styles = StyleSheet.create({
- tag: {
- alignSelf: 'flex-start',
- paddingHorizontal: 6,
- marginBottom: 4,
+const messages = defineMessages({
+ sysAdmin: {
+ id: 'user_profile.system_admin',
+ defaultMessage: 'System Admin',
+ },
+ teamAdmin: {
+ id: 'user_profile.team_admin',
+ defaultMessage: 'Team Admin',
+ },
+ channelAdmin: {
+ id: 'user_profile.channel_admin',
+ defaultMessage: 'Channel Admin',
},
});
const UserProfileTag = ({isBot, isChannelAdmin, showGuestTag, isSystemAdmin, isTeamAdmin}: Props) => {
if (isBot) {
return (
- );
+ );
}
if (showGuestTag) {
return (
- );
+ );
}
if (isSystemAdmin) {
return (
);
@@ -53,10 +53,9 @@ const UserProfileTag = ({isBot, isChannelAdmin, showGuestTag, isSystemAdmin, isT
if (isTeamAdmin) {
return (
);
}
@@ -64,10 +63,9 @@ const UserProfileTag = ({isBot, isChannelAdmin, showGuestTag, isSystemAdmin, isT
if (isChannelAdmin) {
return (
);
}
diff --git a/app/utils/buttonStyles.ts b/app/utils/buttonStyles.ts
index 5f52dcd6a..069271cf6 100644
--- a/app/utils/buttonStyles.ts
+++ b/app/utils/buttonStyles.ts
@@ -198,7 +198,7 @@ export const getBackgroundStyles = (theme: Theme): BackgroundStyles => {
},
inverted: {
default: {
- backgroundColor: changeOpacity(theme.sidebarText, 0.12),
+ backgroundColor: changeOpacity(theme.buttonColor, 0.12),
},
hover: {
backgroundColor: changeOpacity(theme.sidebarText, 0.16),
diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json
index f7c837cd3..fdcb909dd 100644
--- a/assets/base/i18n/en.json
+++ b/assets/base/i18n/en.json
@@ -1272,7 +1272,10 @@
"unreads.empty.paragraph": "Turn off the unread filter to show all your channels.",
"unreads.empty.show_all": "Show all",
"unreads.empty.title": "No more unreads",
+ "user_profile.channel_admin": "Channel Admin",
"user_profile.custom_status": "Custom Status",
+ "user_profile.system_admin": "System Admin",
+ "user_profile.team_admin": "Team Admin",
"user_settings.notifications.test_notification.body": "Not receiving notifications? Start by sending a test notification to all your devices to check if they’re working as expected. If issues persist, explore ways to solve them with troubleshooting steps.",
"user_settings.notifications.test_notification.go_to_docs": "Troubleshooting docs",
"user_settings.notifications.test_notification.send_button.error": "Error sending test notification",