Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions apps/meteor/app/api/server/v1/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type {
TwitterOAuthConfiguration,
OAuthConfiguration,
} from '@rocket.chat/core-typings';
import { isSettingAction, isSettingColor } from '@rocket.chat/core-typings';
import { isActionSettingWithEndpoint, isSettingAction, isSettingColor } from '@rocket.chat/core-typings';
import { LoginServiceConfiguration as LoginServiceConfigurationModel, Settings } from '@rocket.chat/models';
import {
ajv,
Expand Down Expand Up @@ -348,7 +348,12 @@ API.v1.post(

const { bodyParams } = this;

if (isSettingAction(setting) && isSettingsUpdatePropsActions(bodyParams) && bodyParams.execute) {
if (
isSettingAction(setting) &&
isSettingsUpdatePropsActions(bodyParams) &&
bodyParams.execute &&
!isActionSettingWithEndpoint(setting.value)
) {
await Meteor.callAsync(setting.value);
return API.v1.success();
}
Comment thread
KevLehman marked this conversation as resolved.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Button, FieldRow, FieldHint } from '@rocket.chat/fuselage';
import type { TranslationKey } from '@rocket.chat/ui-contexts';
import { useToastMessageDispatch } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import { useTranslation } from 'react-i18next';

import type { SettingInputProps } from './types';

export type ActionInputBaseProps = SettingInputProps & {
actionText: TranslationKey;
sectionChanged: boolean;
onAction: () => Promise<{ message: TranslationKey; params?: string[] }>;
};

function ActionInputBase({ actionText, hint, disabled, sectionChanged, onAction }: ActionInputBaseProps): ReactElement {
const { t } = useTranslation();
const dispatchToastMessage = useToastMessageDispatch();

const handleClick = async (): Promise<void> => {
try {
const data = await onAction();
const params = data.params || [];
dispatchToastMessage({ type: 'success', message: t(data.message, { postProcess: 'sprintf', sprintf: params }) });
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
}
};

return (
<>
<FieldRow>
<Button disabled={disabled || sectionChanged} primary onClick={handleClick}>
{t(actionText)}
</Button>
</FieldRow>
{sectionChanged && <FieldHint>{t('Save_to_enable_this_action')}</FieldHint>}
{hint && <FieldHint>{hint}</FieldHint>}
</>
);
}

export default ActionInputBase;
Original file line number Diff line number Diff line change
@@ -1,46 +1,25 @@
import { isActionSettingWithEndpoint } from '@rocket.chat/core-typings';
import type { ServerMethods } from '@rocket.chat/ddp-client';
import { Button, FieldRow, FieldHint } from '@rocket.chat/fuselage';
import type { PathPattern, Method } from '@rocket.chat/rest-typings';
import type { TranslationKey } from '@rocket.chat/ui-contexts';
import { useMethod, useToastMessageDispatch } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import { useTranslation } from 'react-i18next';

import EndpointActionInput from './EndpointActionInput';
import MethodActionInput from './MethodActionInput';
import type { SettingInputProps } from './types';

type ActionSettingInputProps = SettingInputProps & {
value: keyof ServerMethods;
value: keyof ServerMethods | { method: Method; path: PathPattern };
actionText: TranslationKey;
sectionChanged: boolean;
};

function ActionSettingInput({ actionText, value, hint, disabled, sectionChanged }: ActionSettingInputProps): ReactElement {
const { t } = useTranslation();
function ActionSettingInput({ value, ...rest }: ActionSettingInputProps): ReactElement {
if (isActionSettingWithEndpoint(value)) {
return <EndpointActionInput endpoint={{ method: value.method, path: value.path }} {...rest} />;
}

const dispatchToastMessage = useToastMessageDispatch();
const actionMethod = useMethod(value);

const handleClick = async (): Promise<void> => {
try {
const data: { message: TranslationKey; params?: string[] } = await actionMethod();

const params = data.params || [];
dispatchToastMessage({ type: 'success', message: t(data.message, { postProcess: 'sprintf', sprintf: params }) });
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
}
};

return (
<>
<FieldRow>
<Button disabled={disabled || sectionChanged} primary onClick={handleClick}>
{t(actionText)}
</Button>
</FieldRow>
{sectionChanged && <FieldHint>{t('Save_to_enable_this_action')}</FieldHint>}
{hint && <FieldHint>{hint}</FieldHint>}
</>
);
return <MethodActionInput value={value} {...rest} />;
}

export default ActionSettingInput;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Method, PathPattern } from '@rocket.chat/rest-typings';
import { useEndpoint } from '@rocket.chat/ui-contexts';

import type { ActionInputBaseProps } from './ActionInputBase';
import ActionInputBase from './ActionInputBase';

type EndpointActionInputProps = Omit<ActionInputBaseProps, 'onAction'> & {
endpoint: {
method: Method;
path: PathPattern;
};
};

function EndpointActionInput({ endpoint, ...rest }: EndpointActionInputProps) {
const callEndpoint = useEndpoint(endpoint.method, endpoint.path);
return <ActionInputBase onAction={() => callEndpoint({} as never)} {...rest} />;
}
Comment thread
KevLehman marked this conversation as resolved.

export default EndpointActionInput;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { ServerMethods } from '@rocket.chat/ddp-client';
import { useMethod } from '@rocket.chat/ui-contexts';

import type { ActionInputBaseProps } from './ActionInputBase';
import ActionInputBase from './ActionInputBase';

type MethodActionInputProps = Omit<ActionInputBaseProps, 'onAction'> & {
value: keyof ServerMethods;
};

function MethodActionInput({ value, ...rest }: MethodActionInputProps) {
const actionMethod = useMethod(value);
return <ActionInputBase onAction={actionMethod} {...rest} />;
}

export default MethodActionInput;
9 changes: 8 additions & 1 deletion packages/core-typings/src/ISetting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ export enum SettingEditor {

export type SettingValueMultiSelect = (string | number)[];
export type SettingValueRoomPick = { _id: string; name?: string }[];
export type SettingValueAction = { method: string; path: string };
export type SettingValue =
| string
| boolean
| number
| SettingValueMultiSelect
| SettingValueRoomPick
| SettingValueAction
| Date
| { url?: string; defaultUrl?: string }
| undefined
Expand Down Expand Up @@ -122,9 +124,11 @@ interface ISettingCode extends ISettingBase {
code?: string;
}

type SettingActionEndpoint = { method: 'GET' | 'POST' | 'PUT' | 'DELETE'; path: string };

interface ISettingAction extends ISettingBase {
type: 'action';
value: string;
value: string | SettingActionEndpoint;
actionText?: string;
}

Expand Down Expand Up @@ -160,6 +164,9 @@ export const isSettingCode = (setting: ISettingBase): setting is ISettingCode =>

export const isSettingAction = (setting: ISettingBase): setting is ISettingAction => setting.type === 'action';

export const isActionSettingWithEndpoint = (value: ISettingAction['value']): value is SettingActionEndpoint =>
typeof value === 'object' && value !== null && 'method' in value && 'path' in value;

export const isSettingRange = (setting: ISettingBase): setting is ISettingRange => setting.type === 'range';

export interface ISettingStatistics {
Expand Down
Loading