Unverified Commit 823d4c65 authored by Reinaldo Neto's avatar Reinaldo Neto Committed by GitHub
Browse files

Chore: Migrate methods/actions to Typescript and refactor UiKit folder (#3716)



* Chore: Migrate methods/actions to Typescript

* tweak in actions

* Co-authored-by: default avatarGleidson Daniel Silva <gleidson10daniel@hotmail.com>

* refactor sdk.current

* interface and uikit done

* refactor interface, index and utils from UiKit

* minor tweak

* minor tweak

Co-authored-by: default avatarGleidson Daniel Silva <gleidson10daniel@hotmail.com>
parent f98f2a46
......@@ -3,25 +3,18 @@ import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
import Button from '../Button';
import I18n from '../../i18n';
interface IActions {
blockId: string;
appId: string;
elements: any[];
parser: any;
theme: string;
}
import { IActions } from './interfaces';
export const Actions = ({ blockId, appId, elements, parser, theme }: IActions) => {
const [showMoreVisible, setShowMoreVisible] = useState(() => elements.length > 5);
const renderedElements = showMoreVisible ? elements.slice(0, 5) : elements;
const [showMoreVisible, setShowMoreVisible] = useState(() => elements && elements.length > 5);
const renderedElements = showMoreVisible ? elements?.slice(0, 5) : elements;
const Elements = () =>
renderedElements.map((element: any) => parser.renderActions({ blockId, appId, ...element }, BLOCK_CONTEXT.ACTION, parser));
const Elements = () => (
<>{renderedElements?.map(element => parser?.renderActions({ blockId, appId, ...element }, BLOCK_CONTEXT.ACTION, parser))}</>
);
return (
<>
{/* @ts-ignore*/}
<Elements />
{showMoreVisible && <Button theme={theme} title={I18n.t('Show_more')} onPress={() => setShowMoreVisible(false)} />}
</>
......
import React from 'react';
import { StyleSheet, View } from 'react-native';
import PropTypes from 'prop-types';
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
import { IContext } from './interfaces';
const styles = StyleSheet.create({
container: {
minHeight: 36,
......@@ -11,13 +12,6 @@ const styles = StyleSheet.create({
}
});
export const Context = ({ elements, parser }: any) => (
<View style={styles.container}>
{elements.map((element: any) => parser.renderContext(element, BLOCK_CONTEXT.CONTEXT, parser))}
</View>
export const Context = ({ elements, parser }: IContext) => (
<View style={styles.container}>{elements?.map(element => parser?.renderContext(element, BLOCK_CONTEXT.CONTEXT, parser))}</View>
);
Context.propTypes = {
elements: PropTypes.array,
parser: PropTypes.object
};
......@@ -12,6 +12,7 @@ import sharedStyles from '../../views/Styles';
import { CustomIcon } from '../../lib/Icons';
import { isAndroid } from '../../utils/deviceInfo';
import ActivityIndicator from '../ActivityIndicator';
import { IDatePicker } from './interfaces';
const styles = StyleSheet.create({
input: {
......@@ -35,23 +36,11 @@ const styles = StyleSheet.create({
}
});
interface IDatePicker {
element: {
initial_date: any;
placeholder: string;
};
language: string;
action: Function;
context: number;
loading: boolean;
theme: string;
value: string;
error: string;
}
export const DatePicker = ({ element, language, action, context, theme, loading, value, error }: IDatePicker) => {
const [show, onShow] = useState(false);
const { initial_date, placeholder } = element;
const initial_date = element?.initial_date;
const placeholder = element?.placeholder;
const [currentDate, onChangeDate] = useState(new Date(initial_date || value));
const onChange = ({ nativeEvent: { timestamp } }: any, date: any) => {
......
......@@ -5,6 +5,8 @@ import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
import ImageContainer from '../message/Image';
import Navigation from '../../lib/Navigation';
import { IThumb, IImage, IElement } from './interfaces';
import { TThemeMode } from '../../definitions/ITheme';
const styles = StyleSheet.create({
image: {
......@@ -15,44 +17,25 @@ const styles = StyleSheet.create({
}
});
interface IThumb {
element: {
imageUrl: string;
};
size?: number;
}
interface IMedia {
element: {
imageUrl: string;
};
theme: string;
}
interface IImage {
element: any;
context: any;
theme: string;
}
const ThumbContext = (args: any) => (
const ThumbContext = (args: IThumb) => (
<View style={styles.mediaContext}>
<Thumb size={20} {...args} />
</View>
);
export const Thumb = ({ element, size = 88 }: IThumb) => (
<FastImage style={[{ width: size, height: size }, styles.image]} source={{ uri: element.imageUrl }} />
<FastImage style={[{ width: size, height: size }, styles.image]} source={{ uri: element?.imageUrl }} />
);
export const Media = ({ element, theme }: IMedia) => {
export const Media = ({ element, theme }: IImage) => {
const showAttachment = (attachment: any) => Navigation.navigate('AttachmentView', { attachment });
const { imageUrl } = element;
const imageUrl = element?.imageUrl ?? '';
// @ts-ignore
// TODO: delete ts-ignore after refactor Markdown and ImageContainer
return <ImageContainer file={{ image_url: imageUrl }} imageUrl={imageUrl} showAttachment={showAttachment} theme={theme} />;
};
const genericImage = (element: any, context: any, theme: string) => {
const genericImage = (theme: TThemeMode, element: IElement, context?: number) => {
switch (context) {
case BLOCK_CONTEXT.SECTION:
return <Thumb element={element} />;
......@@ -63,4 +46,4 @@ const genericImage = (element: any, context: any, theme: string) => {
}
};
export const Image = ({ element, context, theme }: IImage) => genericImage(element, context, theme);
export const Image = ({ element, context, theme }: IImage) => genericImage(theme, element, context);
......@@ -4,6 +4,7 @@ import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
import sharedStyles from '../../views/Styles';
import { themes } from '../../constants/colors';
import { IInput } from './interfaces';
const styles = StyleSheet.create({
container: {
......@@ -31,16 +32,6 @@ const styles = StyleSheet.create({
}
});
interface IInput {
element: object;
parser: any;
label: string;
description: string;
error: string;
hint: string;
theme: string;
}
export const Input = ({ element, parser, label, description, error, hint, theme }: IInput) => (
<View style={styles.container}>
{label ? (
......
......@@ -8,32 +8,7 @@ import ActivityIndicator from '../ActivityIndicator';
import { themes } from '../../constants/colors';
import { BUTTON_HIT_SLOP } from '../message/utils';
import * as List from '../List';
interface IOption {
option: {
text: string;
value: string;
};
onOptionPress: Function;
parser: any;
theme: string;
}
interface IOptions {
options: [];
onOptionPress: Function;
parser: object;
theme: string;
}
interface IOverflow {
element: any;
action: Function;
loading: boolean;
parser: object;
theme: string;
context: any;
}
import { IOption, IOptions, IOverflow } from './interfaces';
const keyExtractor = (item: any) => item.value;
......@@ -68,10 +43,11 @@ const Options = ({ options, onOptionPress, parser, theme }: IOptions) => (
/>
);
const touchable = {};
const touchable: { [key: string]: any } = {};
export const Overflow = ({ element, loading, action, parser, theme }: IOverflow) => {
const { options, blockId } = element;
const options = element?.options || [];
const blockId = element?.blockId || '';
const [show, onShow] = useState(false);
const onOptionPress = ({ value }: any) => {
......@@ -82,8 +58,7 @@ export const Overflow = ({ element, loading, action, parser, theme }: IOverflow)
return (
<>
<Touchable
/* @ts-ignore*/
ref={ref => (touchable[blockId] = ref)}
ref={(ref: any) => (touchable[blockId] = ref)}
background={Touchable.Ripple(themes[theme].bannerBackground)}
onPress={() => onShow(!show)}
hitSlop={BUTTON_HIT_SLOP}
......
......@@ -3,6 +3,7 @@ import { StyleSheet, Text, View } from 'react-native';
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
import { themes } from '../../constants/colors';
import { IAccessoryComponent, IFields, ISection } from './interfaces';
const styles = StyleSheet.create({
content: {
......@@ -23,36 +24,16 @@ const styles = StyleSheet.create({
}
});
interface IAccessory {
blockId?: string;
appId?: string;
element: any;
parser: any;
}
const Accessory = ({ element, parser }: IAccessoryComponent) =>
parser.renderAccessories({ ...element }, BLOCK_CONTEXT.SECTION, parser);
interface IFields {
fields: any;
parser: any;
theme: string;
}
interface ISection {
blockId: string;
appId: string;
text: object;
fields: [];
accessory: any;
theme: string;
parser: any;
}
const Accessory = ({ blockId, appId, element, parser }: IAccessory) =>
parser.renderAccessories({ blockId, appId, ...element }, BLOCK_CONTEXT.SECTION, parser);
const Fields = ({ fields, parser, theme }: IFields) =>
fields.map((field: any) => (
<Text style={[styles.text, styles.field, { color: themes[theme].bodyText }]}>{parser.text(field)}</Text>
));
const Fields = ({ fields, parser, theme }: IFields) => (
<>
{fields.map(field => (
<Text style={[styles.text, styles.field, { color: themes[theme].bodyText }]}>{parser.text(field)}</Text>
))}
</>
);
const accessoriesRight = ['image', 'overflow'];
......
......@@ -20,6 +20,7 @@ import { Input } from './Input';
import { DatePicker } from './DatePicker';
import { Overflow } from './Overflow';
import { ThemeContext } from '../../theme';
import { BlockContext, IButton, IInputIndex, IParser, IText } from './interfaces';
const styles = StyleSheet.create({
input: {
......@@ -42,8 +43,12 @@ const styles = StyleSheet.create({
const plainText = ({ text } = { text: '' }) => text;
class MessageParser extends UiKitParserMessage {
text({ text, type }: any = { text: '' }, context: any) {
const { theme }: any = useContext(ThemeContext);
get current() {
return this as unknown as IParser;
}
text({ text, type }: Partial<IText> = { text: '' }, context: BlockContext) {
const { theme } = useContext(ThemeContext);
if (type !== 'mrkdwn') {
return <Text style={[styles.text, { color: themes[theme].bodyText }]}>{text}</Text>;
}
......@@ -55,9 +60,9 @@ class MessageParser extends UiKitParserMessage {
return <Markdown msg={text} theme={theme} style={[isContext && { color: themes[theme].auxiliaryText }]} />;
}
button(element: any, context: any) {
button(element: IButton, context: BlockContext) {
const { text, value, actionId, style } = element;
const [{ loading }, action]: any = useBlockContext(element, context);
const [{ loading }, action] = useBlockContext(element, context);
const { theme } = useContext(ThemeContext);
return (
<Button
......@@ -73,7 +78,7 @@ class MessageParser extends UiKitParserMessage {
}
divider() {
const { theme }: any = useContext(ThemeContext);
const { theme } = useContext(ThemeContext);
// @ts-ignore
return <Divider theme={theme} />;
}
......@@ -91,7 +96,7 @@ class MessageParser extends UiKitParserMessage {
overflow(element: any, context: any) {
const [{ loading }, action]: any = useBlockContext(element, context);
const { theme }: any = useContext(ThemeContext);
return <Overflow element={element} context={context} loading={loading} action={action} theme={theme} parser={this} />;
return <Overflow element={element} context={context} loading={loading} action={action} theme={theme} parser={this.current} />;
}
datePicker(element: any, context: any) {
......@@ -150,12 +155,16 @@ class ModalParser extends UiKitParserModal {
});
}
input({ element, blockId, appId, label, description, hint }: any, context: any) {
get current() {
return this as unknown as IParser;
}
input({ element, blockId, appId, label, description, hint }: IInputIndex, context: number) {
const [{ error }]: any = useBlockContext({ ...element, appId, blockId }, context);
const { theme }: any = useContext(ThemeContext);
return (
<Input
parser={this}
parser={this.current}
element={{ ...element, appId, blockId }}
label={plainText(label)}
description={plainText(description)}
......
import { TThemeMode } from '../../definitions/ITheme';
export enum ElementTypes {
IMAGE = 'image',
BUTTON = 'button',
STATIC_SELECT = 'static_select',
MULTI_STATIC_SELECT = 'multi_static_select',
CONVERSATION_SELECT = 'conversations_select',
CHANNEL_SELECT = 'channels_select',
USER_SELECT = 'users_select',
OVERFLOW = 'overflow',
DATEPICKER = 'datepicker',
PLAIN_TEXT_INPUT = 'plain_text_input',
SECTION = 'section',
DIVIDER = 'divider',
ACTIONS = 'actions',
CONTEXT = 'context',
FIELDS = 'fields',
INPUT = 'input',
PLAIN_TEXT = 'plain_text',
TEXT = 'text',
MARKDOWN = 'mrkdwn'
}
export enum BlockContext {
BLOCK,
SECTION,
ACTION,
FORM,
CONTEXT
}
export enum ActionTypes {
ACTION = 'blockAction',
SUBMIT = 'viewSubmit',
CLOSED = 'viewClosed'
}
export enum ContainerTypes {
VIEW = 'view',
MESSAGE = 'message'
}
export enum ModalActions {
MODAL = 'modal',
OPEN = 'modal.open',
CLOSE = 'modal.close',
UPDATE = 'modal.update',
ERRORS = 'errors'
}
export interface IStateView {
[key: string]: { [settings: string]: string | number };
}
export interface IView {
appId: string;
type: ModalActions;
id: string;
title: IText;
submit: IButton;
close: IButton;
blocks: Block[];
showIcon: boolean;
state?: IStateView;
}
export interface Block {
type: ElementTypes;
blockId: string;
element?: IElement;
label?: string;
appId: string;
optional?: boolean;
elements?: IElement[];
}
export interface IElement {
type: ElementTypes;
placeholder?: IText;
actionId: string;
initialValue?: string;
options?: Option[];
text?: IText;
value?: string;
initial_date?: any;
imageUrl?: string;
appId?: string;
blockId?: string;
}
export interface IText {
type: ElementTypes;
text: string;
emoji?: boolean;
}
export interface Option {
text: IText;
value: string;
}
export interface IButton {
type: ElementTypes;
text: IText;
actionId: string;
value?: any;
style?: any;
}
export interface IContainer {
type: ContainerTypes;
id: string;
}
// methods/actions
export interface IUserInteraction {
triggerId: string;
appId?: string;
viewId?: string;
view: IView;
}
export interface IEmitUserInteraction extends IUserInteraction {
type: ModalActions;
}
export interface ITriggerAction {
type: ActionTypes;
actionId?: string;
appId?: string;
container?: IContainer;
value?: number;
blockId?: string;
rid?: string;
mid?: string;
viewId?: string;
payload?: any;
view?: IView;
}
export interface ITriggerBlockAction {
container: IContainer;
actionId: string;
appId: string;
value: number;
blockId?: string;
mid?: string;
rid?: string;
}
export interface ITriggerSubmitView {
viewId: string;
appId: string;
payload: {
view: {
id: string;
state: IStateView;
};
};
}
export interface ITriggerCancel {
view: IView;
appId: string;
viewId: string;
isCleared: boolean;
}
// UiKit components
export interface IParser {
renderAccessories: (data: TElementAccessory, context: BlockContext, parser: IParser) => JSX.Element;
renderActions: (data: Block, context: BlockContext, parser: IParser) => JSX.Element;
renderContext: (data: IElement, context: BlockContext, parser: IParser) => JSX.Element;
renderInputs: (data: Partial<IElement>, context: BlockContext, parser: IParser) => JSX.Element;
text: (data: IText) => JSX.Element;
}
export interface IActions extends Block {
parser?: IParser;
theme: TThemeMode;
}
export interface IContext extends Block {
parser: IParser;
}
export interface IDatePicker extends Partial<Block> {
language: string;
action: Function;
context: number;
loading: boolean;
value: string;
error: string;
theme: TThemeMode;
}
export interface IInput extends Partial<Block> {
parser: IParser;
description: string;
error: string;
hint: string;
theme: TThemeMode;
}
export interface IInputIndex {
element: IElement;
blockId: string;
appId: string;
label: IText;
description: IText;
hint: IText;
}
export interface IThumb {
element: IElement;
size?: number;
}
export interface IImage {
element: IElement;
theme: TThemeMode;
context?: number;
}
// UiKit/Overflow
export interface IOverflow extends Partial<Block> {
action: Function;
loading: boolean;
parser: IParser;
theme: TThemeMode;
context: number;
}
interface PropsOption {
onOptionPress: Function;
parser: IParser