snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import type { IApiEndpointMetadata } from '@rocket.chat/apps-engine/definition/api';
|
||||
|
||||
import { Utilities } from '../../../../ee/lib/misc/Utilities';
|
||||
|
||||
export const apiCurlGetter =
|
||||
(absoluteUrl: (path: string) => string) =>
|
||||
(method: string, api: IApiEndpointMetadata): string[] => {
|
||||
const example = api.examples?.[method];
|
||||
return Utilities.curl({
|
||||
url: absoluteUrl(api.computedPath),
|
||||
method,
|
||||
params: example?.params,
|
||||
query: example?.query,
|
||||
content: example?.content,
|
||||
headers: example?.headers,
|
||||
auth: '',
|
||||
}).split('\n');
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
import { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus';
|
||||
|
||||
export const appErroredStatuses = [
|
||||
AppStatus.COMPILER_ERROR_DISABLED,
|
||||
AppStatus.ERROR_DISABLED,
|
||||
AppStatus.INVALID_SETTINGS_DISABLED,
|
||||
AppStatus.INVALID_LICENSE_DISABLED,
|
||||
];
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { App } from '../types';
|
||||
import { filterAppsByCategories } from './filterAppsByCategories';
|
||||
|
||||
describe('filterAppsByCategories', () => {
|
||||
it('should return true if the app is in the categories', () => {
|
||||
const app = {
|
||||
categories: ['category1', 'category2'],
|
||||
};
|
||||
const categories = ['category1'];
|
||||
const result = filterAppsByCategories(app as App, categories);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the app is not in the categories', () => {
|
||||
const app = {
|
||||
categories: ['category1', 'category2'],
|
||||
};
|
||||
const categories = ['category3'];
|
||||
const result = filterAppsByCategories(app as App, categories);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
import type { App } from '../types';
|
||||
|
||||
export const filterAppsByCategories = (app: App, categories: string[]): boolean =>
|
||||
!app.categories || categories.length === 0 || app.categories.some((c) => categories.includes(c));
|
||||
@@ -0,0 +1,10 @@
|
||||
import { appStatusSpanProps } from '../helpers';
|
||||
import type { App } from '../types';
|
||||
|
||||
export const filterAppsByDisabled = (app: App): boolean => {
|
||||
const appStatus = appStatusSpanProps(app)?.label;
|
||||
|
||||
const uiDisabledStatus = ['Disabled', 'Disabled*', 'Config Needed', 'Failed'];
|
||||
|
||||
return uiDisabledStatus.includes(appStatus || '');
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
import { appStatusSpanProps } from '../helpers';
|
||||
import type { App } from '../types';
|
||||
|
||||
export const filterAppsByEnabled = (app: App): boolean => {
|
||||
const appStatus = appStatusSpanProps(app)?.label;
|
||||
|
||||
const uiEnabledStatus = ['Enabled', 'Enabled*', 'Trial period'];
|
||||
|
||||
return uiEnabledStatus.includes(appStatus || '');
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
import type { App } from '../types';
|
||||
|
||||
export const filterAppsByEnterprise = ({ isEnterpriseOnly }: Partial<App>): boolean => Boolean(isEnterpriseOnly);
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { PurchaseType } from '@rocket.chat/core-typings';
|
||||
|
||||
import { filterAppsByFree } from './filterAppsByFree';
|
||||
|
||||
describe('filterAppsByFree', () => {
|
||||
it('should return true if app purchase type is buy and price does not exist or is 0', () => {
|
||||
const purchaseType: PurchaseType = 'buy';
|
||||
const app = {
|
||||
purchaseType,
|
||||
price: 0,
|
||||
};
|
||||
const result = filterAppsByFree(app);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if app purchase type is not buy', () => {
|
||||
const purchaseType: PurchaseType = 'subscription';
|
||||
const app = {
|
||||
purchaseType,
|
||||
price: 0,
|
||||
};
|
||||
const result = filterAppsByFree(app);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false if app price exists and is different than 0', () => {
|
||||
const purchaseType: PurchaseType = 'buy';
|
||||
const app = {
|
||||
purchaseType,
|
||||
price: 5,
|
||||
};
|
||||
const result = filterAppsByFree(app);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false if both app purchase type is different than buy and price exists and is different than 0', () => {
|
||||
const purchaseType: PurchaseType = 'subscription';
|
||||
const app = {
|
||||
purchaseType,
|
||||
price: 5,
|
||||
};
|
||||
const result = filterAppsByFree(app);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
import type { App } from '../types';
|
||||
|
||||
export const filterAppsByFree = ({ purchaseType, price, isEnterpriseOnly }: Partial<App>): boolean =>
|
||||
purchaseType === 'buy' && !price && !isEnterpriseOnly;
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { PurchaseType } from '@rocket.chat/core-typings';
|
||||
|
||||
import { filterAppsByPaid } from './filterAppsByPaid';
|
||||
|
||||
describe('filterAppsByPaid', () => {
|
||||
it('should return true if both app purchase type is subscription and app price exists and is not 0', () => {
|
||||
const purchaseType: PurchaseType = 'subscription';
|
||||
const app = {
|
||||
purchaseType,
|
||||
price: 5,
|
||||
};
|
||||
const result = filterAppsByPaid(app);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if app purchase type is subscription', () => {
|
||||
const purchaseType: PurchaseType = 'subscription';
|
||||
const app = {
|
||||
purchaseType,
|
||||
price: 0,
|
||||
};
|
||||
const result = filterAppsByPaid(app);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if app price exists and is not 0', () => {
|
||||
const purchaseType: PurchaseType = 'buy';
|
||||
const app = {
|
||||
purchaseType,
|
||||
price: 5,
|
||||
};
|
||||
const result = filterAppsByPaid(app);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if both app price does not exist or is 0 and app purchase type is not subscription', () => {
|
||||
const purchaseType: PurchaseType = 'buy';
|
||||
const app = {
|
||||
purchaseType,
|
||||
price: 0,
|
||||
};
|
||||
const result = filterAppsByPaid(app);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
import type { App } from '../types';
|
||||
|
||||
export const filterAppsByPaid = ({ purchaseType, price }: Partial<App>): boolean => purchaseType === 'subscription' || Boolean(price);
|
||||
@@ -0,0 +1,21 @@
|
||||
import { filterAppsByText } from './filterAppsByText';
|
||||
|
||||
describe('filterAppsByText', () => {
|
||||
it('should return true if the text is the name of an app', () => {
|
||||
const app = {
|
||||
name: 'name1',
|
||||
};
|
||||
const text = 'name1';
|
||||
const result = filterAppsByText(app.name, text);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the text is not the name of an app', () => {
|
||||
const app = {
|
||||
name: 'name1',
|
||||
};
|
||||
const text = 'name2';
|
||||
const result = filterAppsByText(app.name, text);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
export const filterAppsByText = (name: string, text: string): boolean => name.toLowerCase().indexOf(text.toLowerCase()) > -1;
|
||||
@@ -0,0 +1 @@
|
||||
export const formatPrice = (price: number): string => `\$${price.toFixed(2)}`;
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { PurchaseType, AppPricingPlan } from '@rocket.chat/core-typings';
|
||||
|
||||
import { formatPrice } from './formatPrice';
|
||||
import { formatPricingPlan } from './formatPricingPlan';
|
||||
|
||||
type PlanType = 'Subscription' | 'Paid' | 'Free';
|
||||
|
||||
type FormattedPriceAndPlan = {
|
||||
type: PlanType;
|
||||
price: string;
|
||||
};
|
||||
|
||||
export const formatPriceAndPurchaseType = (
|
||||
purchaseType: PurchaseType,
|
||||
pricingPlans: AppPricingPlan[],
|
||||
price: number,
|
||||
): FormattedPriceAndPlan => {
|
||||
if (purchaseType === 'subscription') {
|
||||
const type = 'Subscription';
|
||||
if (!pricingPlans || !Array.isArray(pricingPlans) || pricingPlans.length === 0) {
|
||||
return { type, price: '-' };
|
||||
}
|
||||
|
||||
return { type, price: formatPricingPlan(pricingPlans[0]) };
|
||||
}
|
||||
|
||||
if (price > 0) {
|
||||
return { type: 'Paid', price: formatPrice(price) };
|
||||
}
|
||||
|
||||
return { type: 'Free', price: '-' };
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { AppPricingPlan } from '@rocket.chat/core-typings';
|
||||
|
||||
import { formatPrice } from './formatPrice';
|
||||
import { t } from '../../../../app/utils/lib/i18n';
|
||||
|
||||
export const formatPricingPlan = ({ strategy, price, tiers = [], trialDays }: AppPricingPlan): string => {
|
||||
const { perUnit = false } = (Array.isArray(tiers) && tiers.find((tier) => tier.price === price)) || {};
|
||||
|
||||
const pricingPlanTranslationString = [
|
||||
'Apps_Marketplace_pricingPlan',
|
||||
Array.isArray(tiers) && tiers.length > 0 && '+*',
|
||||
strategy,
|
||||
trialDays && 'trialDays',
|
||||
perUnit && 'perUser',
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join('_');
|
||||
|
||||
return t(pricingPlanTranslationString, {
|
||||
price: formatPrice(price),
|
||||
trialDays,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import { t } from '../../../../app/utils/lib/i18n';
|
||||
import { dispatchToastMessage } from '../../../lib/toast';
|
||||
|
||||
const shouldHandleErrorAsWarning = (message: string): boolean => {
|
||||
const warnings = ['Could not reach the Marketplace'];
|
||||
|
||||
return warnings.includes(message);
|
||||
};
|
||||
|
||||
export const handleAPIError = (errorObject: unknown): void => {
|
||||
const { error = '', message = error } = errorObject as { message?: string; error?: string };
|
||||
|
||||
if (shouldHandleErrorAsWarning(message)) {
|
||||
return dispatchToastMessage({ type: 'error', message: t(message) });
|
||||
}
|
||||
|
||||
dispatchToastMessage({ type: 'error', message: t(`Apps_Error_${error}`) });
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
import { t } from '../../../../app/utils/lib/i18n';
|
||||
import { dispatchToastMessage } from '../../../lib/toast';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
interface ApiError {
|
||||
xhr: {
|
||||
responseJSON: {
|
||||
error: string;
|
||||
status: string;
|
||||
messages: string[];
|
||||
payload?: any;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function handleInstallError(apiError: ApiError | Error): void {
|
||||
if (apiError instanceof Error) {
|
||||
dispatchToastMessage({ type: 'error', message: apiError.message });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!apiError.xhr?.responseJSON) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { status, messages, error, payload = null } = apiError.xhr.responseJSON;
|
||||
|
||||
let message: string;
|
||||
|
||||
switch (status) {
|
||||
case 'storage_error':
|
||||
message = messages.join('');
|
||||
break;
|
||||
case 'app_user_error':
|
||||
message = messages.join('');
|
||||
if (payload?.username) {
|
||||
message = t('Apps_User_Already_Exists', { username: payload.username });
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (error) {
|
||||
message = error;
|
||||
} else {
|
||||
message = t('There_has_been_an_error_installing_the_app');
|
||||
}
|
||||
}
|
||||
|
||||
dispatchToastMessage({ type: 'error', message });
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { RefObject } from 'react';
|
||||
|
||||
export const isValidReference = (reference: RefObject<HTMLElement | null>, e: { target: Node | null }): boolean => {
|
||||
const isValidTarget = Boolean(e.target);
|
||||
const isValidReference = e.target !== reference.current && !reference.current?.contains(e.target);
|
||||
|
||||
return isValidTarget && isValidReference;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { App, AppOverview } from '@rocket.chat/core-typings';
|
||||
|
||||
const normalizeFeaturedApps = (appOverviewList: AppOverview[], appsResultItems: App[]): App[] => {
|
||||
const featuredAppsIdList = appOverviewList.map<string>((featuredApp) => featuredApp.latest.id);
|
||||
|
||||
return appsResultItems.filter((app) => featuredAppsIdList.includes(app.id));
|
||||
};
|
||||
|
||||
export default normalizeFeaturedApps;
|
||||
@@ -0,0 +1,5 @@
|
||||
export const onMouseEventPreventSideEffects = (e: MouseEvent): void => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
import { sortAppsByAlphabeticalOrInverseOrder } from './sortAppsByAlphabeticalOrInverseOrder';
|
||||
|
||||
describe('sortAppsByAlphabeticalOrder', () => {
|
||||
it.skip('should return a positive number if first word is, alphabetically, after second word', () => {
|
||||
const firstWord = 'Alfa';
|
||||
const secondWord = 'Bravo';
|
||||
|
||||
const result = sortAppsByAlphabeticalOrInverseOrder(firstWord, secondWord);
|
||||
|
||||
expect(result).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it.skip('should return a negative number if first word is, alphabetically, before second word', () => {
|
||||
const firstWord = 'Bravo';
|
||||
const secondWord = 'Alfa';
|
||||
|
||||
const result = sortAppsByAlphabeticalOrInverseOrder(firstWord, secondWord);
|
||||
|
||||
expect(result).toBeLessThan(0);
|
||||
});
|
||||
|
||||
it('should return 0 if the words are the equivalent', () => {
|
||||
const firstWord = 'Alfa';
|
||||
const secondWord = 'Alfa';
|
||||
|
||||
const result = sortAppsByAlphabeticalOrInverseOrder(firstWord, secondWord);
|
||||
|
||||
expect(result).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,2 @@
|
||||
export const sortAppsByAlphabeticalOrInverseOrder = (firstWord: string, secondWord: string): number =>
|
||||
firstWord.toLowerCase().localeCompare(secondWord.toLowerCase());
|
||||
@@ -0,0 +1,30 @@
|
||||
import { sortAppsByClosestOrFarthestModificationDate } from './sortAppsByClosestOrFarthestModificationDate';
|
||||
|
||||
describe('sortAppsByClosestOrFarthestModificationDate', () => {
|
||||
it('should return a positive number if firstDate is before secondDate', () => {
|
||||
const firstDate = '2000-04-01T07:00:00';
|
||||
const secondDate = '2022-02-21T13:00:00';
|
||||
|
||||
const result = sortAppsByClosestOrFarthestModificationDate(firstDate, secondDate);
|
||||
|
||||
expect(result).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should return a negative number if firstDate is after secondDate', () => {
|
||||
const firstDate = '2022-02-21T13:00:00';
|
||||
const secondDate = '2000-04-01T07:00:00';
|
||||
|
||||
const result = sortAppsByClosestOrFarthestModificationDate(firstDate, secondDate);
|
||||
|
||||
expect(result).toBeLessThan(0);
|
||||
});
|
||||
|
||||
it.skip('should return zero if firstDate and secondDate are equivalent', () => {
|
||||
const firstDate = '2000-04-01T07:00:00';
|
||||
const secondDate = '2000-04-01T07:00:00';
|
||||
|
||||
const result = sortAppsByClosestOrFarthestModificationDate(firstDate, secondDate);
|
||||
|
||||
expect(result).toBeLessThan(0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
export const sortAppsByClosestOrFarthestModificationDate = (firstDateString: string, secondDateString: string): number => {
|
||||
const firstDate = new Date(firstDateString);
|
||||
const secondDate = new Date(secondDateString);
|
||||
|
||||
return +secondDate - +firstDate;
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus';
|
||||
|
||||
import { appErroredStatuses } from './appErroredStatuses';
|
||||
import { t } from '../../../../app/utils/lib/i18n';
|
||||
import { dispatchToastMessage } from '../../../lib/toast';
|
||||
|
||||
export const warnAppInstall = (appName: string, status: AppStatus): void => {
|
||||
if (appErroredStatuses.includes(status)) {
|
||||
dispatchToastMessage({ type: 'error', message: (t(`App_status_${status}`), appName) });
|
||||
return;
|
||||
}
|
||||
|
||||
dispatchToastMessage({ type: 'success', message: `${appName} installed` });
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus';
|
||||
|
||||
import { appErroredStatuses } from './appErroredStatuses';
|
||||
import { t } from '../../../../app/utils/lib/i18n';
|
||||
import { dispatchToastMessage } from '../../../lib/toast';
|
||||
|
||||
export const warnEnableDisableApp = (appName: string, status: AppStatus, type: string): void => {
|
||||
if (appErroredStatuses.includes(status)) {
|
||||
dispatchToastMessage({ type: 'error', message: (t(`App_status_${status}`), appName) });
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === 'enable') {
|
||||
dispatchToastMessage({ type: 'success', message: `${appName} enabled` });
|
||||
return;
|
||||
}
|
||||
|
||||
dispatchToastMessage({ type: 'success', message: `${appName} disabled` });
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus';
|
||||
|
||||
import { appErroredStatuses } from './appErroredStatuses';
|
||||
import { t } from '../../../../app/utils/lib/i18n';
|
||||
import { dispatchToastMessage } from '../../../lib/toast';
|
||||
|
||||
export const warnStatusChange = (appName: string, status: AppStatus): void => {
|
||||
if (appErroredStatuses.includes(status)) {
|
||||
dispatchToastMessage({ type: 'error', message: (t(`App_status_${status}`), appName) });
|
||||
return;
|
||||
}
|
||||
|
||||
dispatchToastMessage({ type: 'info', message: (t(`App_status_${status}`), appName) });
|
||||
};
|
||||
Reference in New Issue
Block a user