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

* Chore: Migrate methods/actions to Typescript

* tweak in actions

* Co-authored-by: Gleidson 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: Gleidson Daniel Silva <gleidson10daniel@hotmail.com>
This commit is contained in:
Reinaldo Neto 2022-03-16 16:07:49 -03:00 committed by GitHub
parent f98f2a46d9
commit 823d4c658c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 406 additions and 193 deletions

View File

@ -3,25 +3,18 @@ import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
import Button from '../Button'; import Button from '../Button';
import I18n from '../../i18n'; import I18n from '../../i18n';
import { IActions } from './interfaces';
interface IActions {
blockId: string;
appId: string;
elements: any[];
parser: any;
theme: string;
}
export const Actions = ({ blockId, appId, elements, parser, theme }: IActions) => { export const Actions = ({ blockId, appId, elements, parser, theme }: IActions) => {
const [showMoreVisible, setShowMoreVisible] = useState(() => elements.length > 5); const [showMoreVisible, setShowMoreVisible] = useState(() => elements && elements.length > 5);
const renderedElements = showMoreVisible ? elements.slice(0, 5) : elements; const renderedElements = showMoreVisible ? elements?.slice(0, 5) : elements;
const Elements = () => const Elements = () => (
renderedElements.map((element: any) => parser.renderActions({ blockId, appId, ...element }, BLOCK_CONTEXT.ACTION, parser)); <>{renderedElements?.map(element => parser?.renderActions({ blockId, appId, ...element }, BLOCK_CONTEXT.ACTION, parser))}</>
);
return ( return (
<> <>
{/* @ts-ignore*/}
<Elements /> <Elements />
{showMoreVisible && <Button theme={theme} title={I18n.t('Show_more')} onPress={() => setShowMoreVisible(false)} />} {showMoreVisible && <Button theme={theme} title={I18n.t('Show_more')} onPress={() => setShowMoreVisible(false)} />}
</> </>

View File

@ -1,8 +1,9 @@
import React from 'react'; import React from 'react';
import { StyleSheet, View } from 'react-native'; import { StyleSheet, View } from 'react-native';
import PropTypes from 'prop-types';
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit'; import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
import { IContext } from './interfaces';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
minHeight: 36, minHeight: 36,
@ -11,13 +12,6 @@ const styles = StyleSheet.create({
} }
}); });
export const Context = ({ elements, parser }: any) => ( export const Context = ({ elements, parser }: IContext) => (
<View style={styles.container}> <View style={styles.container}>{elements?.map(element => parser?.renderContext(element, BLOCK_CONTEXT.CONTEXT, parser))}</View>
{elements.map((element: any) => parser.renderContext(element, BLOCK_CONTEXT.CONTEXT, parser))}
</View>
); );
Context.propTypes = {
elements: PropTypes.array,
parser: PropTypes.object
};

View File

@ -12,6 +12,7 @@ import sharedStyles from '../../views/Styles';
import { CustomIcon } from '../../lib/Icons'; import { CustomIcon } from '../../lib/Icons';
import { isAndroid } from '../../utils/deviceInfo'; import { isAndroid } from '../../utils/deviceInfo';
import ActivityIndicator from '../ActivityIndicator'; import ActivityIndicator from '../ActivityIndicator';
import { IDatePicker } from './interfaces';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
input: { 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) => { export const DatePicker = ({ element, language, action, context, theme, loading, value, error }: IDatePicker) => {
const [show, onShow] = useState(false); 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 [currentDate, onChangeDate] = useState(new Date(initial_date || value));
const onChange = ({ nativeEvent: { timestamp } }: any, date: any) => { const onChange = ({ nativeEvent: { timestamp } }: any, date: any) => {

View File

@ -5,6 +5,8 @@ import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
import ImageContainer from '../message/Image'; import ImageContainer from '../message/Image';
import Navigation from '../../lib/Navigation'; import Navigation from '../../lib/Navigation';
import { IThumb, IImage, IElement } from './interfaces';
import { TThemeMode } from '../../definitions/ITheme';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
image: { image: {
@ -15,44 +17,25 @@ const styles = StyleSheet.create({
} }
}); });
interface IThumb { const ThumbContext = (args: 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) => (
<View style={styles.mediaContext}> <View style={styles.mediaContext}>
<Thumb size={20} {...args} /> <Thumb size={20} {...args} />
</View> </View>
); );
export const Thumb = ({ element, size = 88 }: IThumb) => ( 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 showAttachment = (attachment: any) => Navigation.navigate('AttachmentView', { attachment });
const { imageUrl } = element; const imageUrl = element?.imageUrl ?? '';
// @ts-ignore // @ts-ignore
// TODO: delete ts-ignore after refactor Markdown and ImageContainer
return <ImageContainer file={{ image_url: imageUrl }} imageUrl={imageUrl} showAttachment={showAttachment} theme={theme} />; 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) { switch (context) {
case BLOCK_CONTEXT.SECTION: case BLOCK_CONTEXT.SECTION:
return <Thumb element={element} />; 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);

View File

@ -4,6 +4,7 @@ import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
import sharedStyles from '../../views/Styles'; import sharedStyles from '../../views/Styles';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { IInput } from './interfaces';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { 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) => ( export const Input = ({ element, parser, label, description, error, hint, theme }: IInput) => (
<View style={styles.container}> <View style={styles.container}>
{label ? ( {label ? (

View File

@ -8,32 +8,7 @@ import ActivityIndicator from '../ActivityIndicator';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { BUTTON_HIT_SLOP } from '../message/utils'; import { BUTTON_HIT_SLOP } from '../message/utils';
import * as List from '../List'; import * as List from '../List';
import { IOption, IOptions, IOverflow } from './interfaces';
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;
}
const keyExtractor = (item: any) => item.value; 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) => { 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 [show, onShow] = useState(false);
const onOptionPress = ({ value }: any) => { const onOptionPress = ({ value }: any) => {
@ -82,8 +58,7 @@ export const Overflow = ({ element, loading, action, parser, theme }: IOverflow)
return ( return (
<> <>
<Touchable <Touchable
/* @ts-ignore*/ ref={(ref: any) => (touchable[blockId] = ref)}
ref={ref => (touchable[blockId] = ref)}
background={Touchable.Ripple(themes[theme].bannerBackground)} background={Touchable.Ripple(themes[theme].bannerBackground)}
onPress={() => onShow(!show)} onPress={() => onShow(!show)}
hitSlop={BUTTON_HIT_SLOP} hitSlop={BUTTON_HIT_SLOP}

View File

@ -3,6 +3,7 @@ import { StyleSheet, Text, View } from 'react-native';
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit'; import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { IAccessoryComponent, IFields, ISection } from './interfaces';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
content: { content: {
@ -23,36 +24,16 @@ const styles = StyleSheet.create({
} }
}); });
interface IAccessory { const Accessory = ({ element, parser }: IAccessoryComponent) =>
blockId?: string; parser.renderAccessories({ ...element }, BLOCK_CONTEXT.SECTION, parser);
appId?: string;
element: any;
parser: any;
}
interface IFields { const Fields = ({ fields, parser, theme }: IFields) => (
fields: any; <>
parser: any; {fields.map(field => (
theme: string; <Text style={[styles.text, styles.field, { color: themes[theme].bodyText }]}>{parser.text(field)}</Text>
} ))}
</>
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 accessoriesRight = ['image', 'overflow']; const accessoriesRight = ['image', 'overflow'];

View File

@ -20,6 +20,7 @@ import { Input } from './Input';
import { DatePicker } from './DatePicker'; import { DatePicker } from './DatePicker';
import { Overflow } from './Overflow'; import { Overflow } from './Overflow';
import { ThemeContext } from '../../theme'; import { ThemeContext } from '../../theme';
import { BlockContext, IButton, IInputIndex, IParser, IText } from './interfaces';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
input: { input: {
@ -42,8 +43,12 @@ const styles = StyleSheet.create({
const plainText = ({ text } = { text: '' }) => text; const plainText = ({ text } = { text: '' }) => text;
class MessageParser extends UiKitParserMessage { class MessageParser extends UiKitParserMessage {
text({ text, type }: any = { text: '' }, context: any) { get current() {
const { theme }: any = useContext(ThemeContext); return this as unknown as IParser;
}
text({ text, type }: Partial<IText> = { text: '' }, context: BlockContext) {
const { theme } = useContext(ThemeContext);
if (type !== 'mrkdwn') { if (type !== 'mrkdwn') {
return <Text style={[styles.text, { color: themes[theme].bodyText }]}>{text}</Text>; 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 }]} />; 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 { text, value, actionId, style } = element;
const [{ loading }, action]: any = useBlockContext(element, context); const [{ loading }, action] = useBlockContext(element, context);
const { theme } = useContext(ThemeContext); const { theme } = useContext(ThemeContext);
return ( return (
<Button <Button
@ -73,7 +78,7 @@ class MessageParser extends UiKitParserMessage {
} }
divider() { divider() {
const { theme }: any = useContext(ThemeContext); const { theme } = useContext(ThemeContext);
// @ts-ignore // @ts-ignore
return <Divider theme={theme} />; return <Divider theme={theme} />;
} }
@ -91,7 +96,7 @@ class MessageParser extends UiKitParserMessage {
overflow(element: any, context: any) { overflow(element: any, context: any) {
const [{ loading }, action]: any = useBlockContext(element, context); const [{ loading }, action]: any = useBlockContext(element, context);
const { theme }: any = useContext(ThemeContext); 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) { 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 [{ error }]: any = useBlockContext({ ...element, appId, blockId }, context);
const { theme }: any = useContext(ThemeContext); const { theme }: any = useContext(ThemeContext);
return ( return (
<Input <Input
parser={this} parser={this.current}
element={{ ...element, appId, blockId }} element={{ ...element, appId, blockId }}
label={plainText(label)} label={plainText(label)}
description={plainText(description)} description={plainText(description)}

View File

@ -0,0 +1,273 @@
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;
theme: TThemeMode;
}
export interface IOptions extends PropsOption {
options: Option[];
}
export interface IOption extends PropsOption {
option: Option;
}
// UiKit/Section
interface IAccessory {
type: ElementTypes;
actionId: string;
value: number;
text: IText;
}
type TElementAccessory = IAccessory & { blockId: string; appId: string };
export interface IAccessoryComponent {
element: TElementAccessory;
parser: IParser;
}
export interface ISection {
blockId: string;
appId: string;
text?: IText;
accessory?: IAccessory;
parser: IParser;
theme: TThemeMode;
fields?: any[];
}
export interface IFields {
parser: IParser;
theme: TThemeMode;
fields: any[];
}

View File

@ -2,6 +2,8 @@
import React, { useContext, useState } from 'react'; import React, { useContext, useState } from 'react';
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit'; import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
import { BlockContext } from './interfaces';
export const textParser = ([{ text }]: any) => text; export const textParser = ([{ text }]: any) => text;
export const defaultContext: any = { export const defaultContext: any = {
@ -13,7 +15,19 @@ export const defaultContext: any = {
export const KitContext = React.createContext(defaultContext); export const KitContext = React.createContext(defaultContext);
export const useBlockContext = ({ blockId, actionId, appId, initialValue }: any, context: any) => { type TObjectReturn = {
loading: boolean;
setLoading: React.Dispatch<React.SetStateAction<boolean>>;
error: any;
value: any;
language: any;
};
type TFunctionReturn = (value: any) => Promise<void>;
type TReturn = [TObjectReturn, TFunctionReturn];
export const useBlockContext = ({ blockId, actionId, appId, initialValue }: any, context: BlockContext): TReturn => {
const { action, appId: appIdFromContext, viewId, state, language, errors, values = {} } = useContext(KitContext); const { action, appId: appIdFromContext, viewId, state, language, errors, values = {} } = useContext(KitContext);
const { value = initialValue } = values[actionId] || {}; const { value = initialValue } = values[actionId] || {};
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);

View File

@ -2,42 +2,37 @@ import random from '../../utils/random';
import EventEmitter from '../../utils/events'; import EventEmitter from '../../utils/events';
import fetch from '../../utils/fetch'; import fetch from '../../utils/fetch';
import Navigation from '../Navigation'; import Navigation from '../Navigation';
import sdk from '../rocketchat/services/sdk';
const ACTION_TYPES = { import {
ACTION: 'blockAction', ActionTypes,
SUBMIT: 'viewSubmit', ITriggerAction,
CLOSED: 'viewClosed' ITriggerBlockAction,
}; ITriggerCancel,
ITriggerSubmitView,
export const MODAL_ACTIONS = { IUserInteraction,
MODAL: 'modal', ModalActions
OPEN: 'modal.open', } from '../../containers/UIKit/interfaces';
CLOSE: 'modal.close', import { TRocketChat } from '../../definitions/IRocketChat';
UPDATE: 'modal.update',
ERRORS: 'errors'
};
export const CONTAINER_TYPES = {
VIEW: 'view',
MESSAGE: 'message'
};
const triggersId = new Map(); const triggersId = new Map();
const invalidateTriggerId = id => { const invalidateTriggerId = (id: string) => {
const appId = triggersId.get(id); const appId = triggersId.get(id);
triggersId.delete(id); triggersId.delete(id);
return appId; return appId;
}; };
export const generateTriggerId = appId => { export const generateTriggerId = (appId?: string): string => {
const triggerId = random(17); const triggerId = random(17);
triggersId.set(triggerId, appId); triggersId.set(triggerId, appId);
return triggerId; return triggerId;
}; };
export const handlePayloadUserInteraction = (type, { triggerId, ...data }) => { export const handlePayloadUserInteraction = (
type: ModalActions,
{ triggerId, ...data }: IUserInteraction
): ModalActions | undefined => {
if (!triggersId.has(triggerId)) { if (!triggersId.has(triggerId)) {
return; return;
} }
@ -58,7 +53,7 @@ export const handlePayloadUserInteraction = (type, { triggerId, ...data }) => {
return; return;
} }
if ([MODAL_ACTIONS.ERRORS].includes(type)) { if ([ModalActions.ERRORS].includes(type)) {
EventEmitter.emit(viewId, { EventEmitter.emit(viewId, {
type, type,
triggerId, triggerId,
@ -66,10 +61,10 @@ export const handlePayloadUserInteraction = (type, { triggerId, ...data }) => {
appId, appId,
...data ...data
}); });
return MODAL_ACTIONS.ERRORS; return ModalActions.ERRORS;
} }
if ([MODAL_ACTIONS.UPDATE].includes(type)) { if ([ModalActions.UPDATE].includes(type)) {
EventEmitter.emit(viewId, { EventEmitter.emit(viewId, {
type, type,
triggerId, triggerId,
@ -77,10 +72,10 @@ export const handlePayloadUserInteraction = (type, { triggerId, ...data }) => {
appId, appId,
...data ...data
}); });
return MODAL_ACTIONS.UPDATE; return ModalActions.UPDATE;
} }
if ([MODAL_ACTIONS.OPEN].includes(type) || [MODAL_ACTIONS.MODAL].includes(type)) { if ([ModalActions.OPEN].includes(type) || [ModalActions.MODAL].includes(type)) {
Navigation.navigate('ModalBlockView', { Navigation.navigate('ModalBlockView', {
data: { data: {
triggerId, triggerId,
@ -89,21 +84,24 @@ export const handlePayloadUserInteraction = (type, { triggerId, ...data }) => {
...data ...data
} }
}); });
return MODAL_ACTIONS.OPEN; return ModalActions.OPEN;
} }
return MODAL_ACTIONS.CLOSE; return ModalActions.CLOSE;
}; };
export function triggerAction({ type, actionId, appId, rid, mid, viewId, container, ...rest }) { export function triggerAction(
return new Promise(async (resolve, reject) => { this: TRocketChat,
{ type, actionId, appId, rid, mid, viewId, container, ...rest }: ITriggerAction
) {
return new Promise<ModalActions | undefined | void>(async (resolve, reject) => {
const triggerId = generateTriggerId(appId); const triggerId = generateTriggerId(appId);
const payload = rest.payload || rest; const payload = rest.payload || rest;
try { try {
const { userId, authToken } = this.sdk.currentLogin; const { userId, authToken } = sdk.current.currentLogin;
const { host } = this.sdk.client; const { host } = sdk.current.client;
// we need to use fetch because this.sdk.post add /v1 to url // we need to use fetch because this.sdk.post add /v1 to url
const result = await fetch(`${host}/api/apps/ui.interaction/${appId}/`, { const result = await fetch(`${host}/api/apps/ui.interaction/${appId}/`, {
@ -142,17 +140,17 @@ export function triggerAction({ type, actionId, appId, rid, mid, viewId, contain
}); });
} }
export default function triggerBlockAction(options) { export default function triggerBlockAction(this: TRocketChat, options: ITriggerBlockAction) {
return triggerAction.call(this, { type: ACTION_TYPES.ACTION, ...options }); return triggerAction.call(this, { type: ActionTypes.ACTION, ...options });
} }
export async function triggerSubmitView({ viewId, ...options }) { export async function triggerSubmitView(this: TRocketChat, { viewId, ...options }: ITriggerSubmitView) {
const result = await triggerAction.call(this, { type: ACTION_TYPES.SUBMIT, viewId, ...options }); const result = await triggerAction.call(this, { type: ActionTypes.SUBMIT, viewId, ...options });
if (!result || MODAL_ACTIONS.CLOSE === result) { if (!result || ModalActions.CLOSE === result) {
Navigation.back(); Navigation.back();
} }
} }
export function triggerCancel({ view, ...options }) { export function triggerCancel(this: TRocketChat, { view, ...options }: ITriggerCancel) {
return triggerAction.call(this, { type: ACTION_TYPES.CLOSED, view, ...options }); return triggerAction.call(this, { type: ActionTypes.CLOSED, view, ...options });
} }

View File

@ -1,3 +1,4 @@
import { IEmitUserInteraction } from '../containers/UIKit/interfaces';
import { ICommand } from '../definitions/ICommand'; import { ICommand } from '../definitions/ICommand';
import log from './log'; import log from './log';
@ -11,7 +12,8 @@ type TEventEmitterEmmitArgs =
| { hasBiometry: boolean } | { hasBiometry: boolean }
| { event: string | ICommand } | { event: string | ICommand }
| { cancel: () => void } | { cancel: () => void }
| { submit: (param: string) => void }; | { submit: (param: string) => void }
| IEmitUserInteraction;
class EventEmitter { class EventEmitter {
private events: { [key: string]: any }; private events: { [key: string]: any };

View File

@ -4,9 +4,21 @@ import { settings as RocketChatSettings } from '@rocket.chat/sdk';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
export type TMethods = 'POST' | 'GET' | 'DELETE' | 'PUT' | 'post' | 'get' | 'delete' | 'put';
interface CustomHeaders { interface CustomHeaders {
'User-Agent': string; 'User-Agent'?: string;
Authorization?: string; Authorization?: string;
'Content-Type'?: string;
'X-Auth-Token'?: string;
'X-User-Id'?: string;
}
interface IOptions {
headers?: CustomHeaders;
signal?: AbortSignal;
method?: TMethods;
body?: any;
} }
// this form is required by Rocket.Chat's parser in "app/statistics/server/lib/UAParserCustom.js" // this form is required by Rocket.Chat's parser in "app/statistics/server/lib/UAParserCustom.js"
@ -29,7 +41,7 @@ export const BASIC_AUTH_KEY = 'BASIC_AUTH_KEY';
RocketChatSettings.customHeaders = headers; RocketChatSettings.customHeaders = headers;
export default (url: string, options: { headers?: Headers; signal?: AbortSignal } = {}): Promise<Response> => { export default (url: string, options: IOptions = {}): Promise<Response> => {
let customOptions = { ...options, headers: RocketChatSettings.customHeaders }; let customOptions = { ...options, headers: RocketChatSettings.customHeaders };
if (options && options.headers) { if (options && options.headers) {
customOptions = { ...customOptions, headers: { ...options.headers, ...customOptions.headers } }; customOptions = { ...customOptions, headers: { ...options.headers, ...customOptions.headers } };

View File

@ -1,9 +1,8 @@
import RNFetchBlob from 'rn-fetch-blob'; import RNFetchBlob from 'rn-fetch-blob';
import { TMethods } from '../fetch';
import { IFileUpload } from './interfaces'; import { IFileUpload } from './interfaces';
type TMethods = 'POST' | 'GET' | 'DELETE' | 'PUT' | 'post' | 'get' | 'delete' | 'put';
class FileUpload { class FileUpload {
fetch = (method: TMethods, url: string, headers: { [key: string]: string }, data: IFileUpload[]) => { fetch = (method: TMethods, url: string, headers: { [key: string]: string }, data: IFileUpload[]) => {
const formData = data.map(item => { const formData = data.map(item => {

View File

@ -12,10 +12,10 @@ import * as HeaderButton from '../containers/HeaderButton';
import { modalBlockWithContext } from '../containers/UIKit/MessageBlock'; import { modalBlockWithContext } from '../containers/UIKit/MessageBlock';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import ActivityIndicator from '../containers/ActivityIndicator'; import ActivityIndicator from '../containers/ActivityIndicator';
import { CONTAINER_TYPES, MODAL_ACTIONS } from '../lib/methods/actions';
import { textParser } from '../containers/UIKit/utils'; import { textParser } from '../containers/UIKit/utils';
import Navigation from '../lib/Navigation'; import Navigation from '../lib/Navigation';
import { MasterDetailInsideStackParamList } from '../stacks/MasterDetailStack/types'; import { MasterDetailInsideStackParamList } from '../stacks/MasterDetailStack/types';
import { ContainerTypes, ModalActions } from '../containers/UIKit/interfaces';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -161,8 +161,8 @@ class ModalBlockView extends React.Component<IModalBlockViewProps, IModalBlockVi
}); });
}; };
handleUpdate = ({ type, ...data }: { type: string }) => { handleUpdate = ({ type, ...data }: { type: ModalActions }) => {
if ([MODAL_ACTIONS.ERRORS].includes(type)) { if ([ModalActions.ERRORS].includes(type)) {
const { errors }: any = data; const { errors }: any = data;
this.setState({ errors }); this.setState({ errors });
} else { } else {
@ -232,7 +232,7 @@ class ModalBlockView extends React.Component<IModalBlockViewProps, IModalBlockVi
const { mid, appId, viewId } = data; const { mid, appId, viewId } = data;
await RocketChat.triggerBlockAction({ await RocketChat.triggerBlockAction({
container: { container: {
type: CONTAINER_TYPES.VIEW, type: ContainerTypes.VIEW,
id: viewId id: viewId
}, },
actionId, actionId,

View File

@ -44,7 +44,6 @@ import {
import { Review } from '../../utils/review'; import { Review } from '../../utils/review';
import RoomClass from '../../lib/methods/subscriptions/room'; import RoomClass from '../../lib/methods/subscriptions/room';
import { getUserSelector } from '../../selectors/login'; import { getUserSelector } from '../../selectors/login';
import { CONTAINER_TYPES } from '../../lib/methods/actions';
import Navigation from '../../lib/Navigation'; import Navigation from '../../lib/Navigation';
import SafeAreaView from '../../containers/SafeAreaView'; import SafeAreaView from '../../containers/SafeAreaView';
import { withDimensions } from '../../dimensions'; import { withDimensions } from '../../dimensions';
@ -55,6 +54,7 @@ import Loading from '../../containers/Loading';
import { goRoom, TGoRoomItem } from '../../utils/goRoom'; import { goRoom, TGoRoomItem } from '../../utils/goRoom';
import getThreadName from '../../lib/methods/getThreadName'; import getThreadName from '../../lib/methods/getThreadName';
import getRoomInfo from '../../lib/methods/getRoomInfo'; import getRoomInfo from '../../lib/methods/getRoomInfo';
import { ContainerTypes } from '../../containers/UIKit/interfaces';
import RoomServices from './services'; import RoomServices from './services';
import LoadMore from './LoadMore'; import LoadMore from './LoadMore';
import Banner from './Banner'; import Banner from './Banner';
@ -1104,7 +1104,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
rid, rid,
appId, appId,
container: { container: {
type: CONTAINER_TYPES.MESSAGE, type: ContainerTypes.MESSAGE,
id: mid id: mid
} }
}); });