From 0f409898c23cc9e901d16e89af829b5e3dc73d51 Mon Sep 17 00:00:00 2001 From: Hugo Costa Date: Wed, 29 Jun 2022 09:29:00 -0300 Subject: [PATCH] [NEW] Community Edition Watermark (#25844) ## Proposed changes (including videos or screenshots) ## Issue(s) ## Steps to test or reproduce ## Further comments --- apps/meteor/client/contexts/CallContext.ts | 12 +++- .../client/sidebar/footer/SidebarFooter.tsx | 31 +++++++++- apps/meteor/ee/server/api/licenses.ts | 13 ++++- .../rocketchat-i18n/i18n/en.i18n.json | 2 + .../tests/end-to-end/api/20-licenses.js | 56 +++++++++++++++---- packages/rest-typings/src/v1/licenses.ts | 3 + 6 files changed, 100 insertions(+), 17 deletions(-) diff --git a/apps/meteor/client/contexts/CallContext.ts b/apps/meteor/client/contexts/CallContext.ts index c6ab16deffa3..1a25cf040d8d 100644 --- a/apps/meteor/client/contexts/CallContext.ts +++ b/apps/meteor/client/contexts/CallContext.ts @@ -1,4 +1,4 @@ -import type { IVoipRoom } from '@rocket.chat/core-typings'; +import type { CallStates, IVoipRoom } from '@rocket.chat/core-typings'; import { ICallerInfo, VoIpCallerInfo } from '@rocket.chat/core-typings'; import { Device } from '@rocket.chat/ui-contexts'; import { createContext, useContext, useMemo } from 'react'; @@ -87,6 +87,16 @@ export const useCallActions = (): CallActionsType => { return context.actions; }; +export const useCallerStatus = (): CallStates => { + const context = useContext(CallContext); + + if (isCallContextReady(context)) { + return context.voipClient.callState; + } + + return 'INITIAL'; +}; + export const useCallerInfo = (): VoIpCallerInfo => { const context = useContext(CallContext); diff --git a/apps/meteor/client/sidebar/footer/SidebarFooter.tsx b/apps/meteor/client/sidebar/footer/SidebarFooter.tsx index 6682dabd4142..301e535589fd 100644 --- a/apps/meteor/client/sidebar/footer/SidebarFooter.tsx +++ b/apps/meteor/client/sidebar/footer/SidebarFooter.tsx @@ -1,10 +1,13 @@ +import { CallStates } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; -import { Box, SidebarFooter as Footer } from '@rocket.chat/fuselage'; +import { Box, Divider, SidebarFooter as Footer } from '@rocket.chat/fuselage'; import colors from '@rocket.chat/fuselage-tokens/colors.json'; +import { useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; import React, { ReactElement } from 'react'; +import { useQuery } from 'react-query'; import { settings } from '../../../app/settings/client'; -import { useIsCallEnabled, useIsCallReady } from '../../contexts/CallContext'; +import { useCallerStatus, useIsCallEnabled, useIsCallReady } from '../../contexts/CallContext'; import { VoipFooter } from './voip'; const SidebarFooter = (): ReactElement => { @@ -20,15 +23,25 @@ const SidebarFooter = (): ReactElement => { } `; + const t = useTranslation(); + + const isEnterpriseEdition = useEndpoint('GET', '/v1/licenses.isEnterprise'); + const { data, isSuccess } = useQuery(['licences'], () => isEnterpriseEdition()); + const isCommunityEdition = isSuccess && !data?.isEnterprise; + const isCallEnabled = useIsCallEnabled(); const ready = useIsCallReady(); + const callerStatus = useCallerStatus(); + + const activeCallStatus: CallStates[] = ['OFFER_RECEIVED', 'IN_CALL', 'ON_HOLD', 'ANSWER_SENT']; - if (isCallEnabled && ready) { + if (activeCallStatus.includes(callerStatus) && isCallEnabled && ready) { return ; } return ( ); }; diff --git a/apps/meteor/ee/server/api/licenses.ts b/apps/meteor/ee/server/api/licenses.ts index 009dffc15493..91ba8cc4cca0 100644 --- a/apps/meteor/ee/server/api/licenses.ts +++ b/apps/meteor/ee/server/api/licenses.ts @@ -1,6 +1,6 @@ import { check } from 'meteor/check'; -import { getLicenses, validateFormat, flatModules, getMaxActiveUsers } from '../../app/license/server/license'; +import { getLicenses, validateFormat, flatModules, getMaxActiveUsers, isEnterprise } from '../../app/license/server/license'; import { Settings, Users } from '../../../app/models/server'; import { API } from '../../../app/api/server/api'; import { hasPermission } from '../../../app/authorization/server'; @@ -68,3 +68,14 @@ API.v1.addRoute( }, }, ); + +API.v1.addRoute( + 'licenses.isEnterprise', + { authRequired: true }, + { + get() { + const isEnterpriseEdtion = isEnterprise(); + return API.v1.success({ isEnterprise: isEnterpriseEdtion }); + }, + }, +); diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 25c6b402dbae..fc73c37baa95 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -988,6 +988,7 @@ "Comment": "Comment", "Common_Access": "Common Access", "Community": "Community", + "Free_Edition": "Free edition", "Compact": "Compact", "Composer_not_available_phone_calls": "Messages are not available on phone calls", "Condensed": "Condensed", @@ -3961,6 +3962,7 @@ "seconds": "seconds", "Secret_token": "Secret Token", "Security": "Security", + "Powered_by_RocketChat": "Powered by Rocket.Chat", "See_full_profile": "See full profile", "Select_a_department": "Select a department", "Select_a_room": "Select a room", diff --git a/apps/meteor/tests/end-to-end/api/20-licenses.js b/apps/meteor/tests/end-to-end/api/20-licenses.js index 20d2c347d8cb..307ff1810dfe 100644 --- a/apps/meteor/tests/end-to-end/api/20-licenses.js +++ b/apps/meteor/tests/end-to-end/api/20-licenses.js @@ -8,14 +8,14 @@ describe('licenses', function () { this.retries(0); before((done) => getCredentials(done)); + let unauthorizedUserCredentials; - describe('[/licenses.add]', () => { - let unauthorizedUserCredentials; - before(async () => { - const createdUser = await createUser(); - unauthorizedUserCredentials = await doLogin(createdUser.username, password); - }); + before(async () => { + const createdUser = await createUser(); + unauthorizedUserCredentials = await doLogin(createdUser.username, password); + }); + describe('[/licenses.add]', () => { it('should fail if not logged in', (done) => { request .post(api('licenses.add')) @@ -65,12 +65,6 @@ describe('licenses', function () { }); describe('[/licenses.get]', () => { - let unauthorizedUserCredentials; - before(async () => { - const createdUser = await createUser(); - unauthorizedUserCredentials = await doLogin(createdUser.username, password); - }); - it('should fail if not logged in', (done) => { request .get(api('licenses.get')) @@ -109,4 +103,42 @@ describe('licenses', function () { .end(done); }); }); + + describe('[/licenses.isEnterprise]', () => { + it('should fail if not logged in', (done) => { + request + .get(api('licenses.isEnterprise')) + .expect('Content-Type', 'application/json') + .expect(401) + .expect((res) => { + expect(res.body).to.have.property('status', 'error'); + expect(res.body).to.have.property('message'); + }) + .end(done); + }); + + it('should pass if user has user role', (done) => { + request + .get(api('licenses.isEnterprise')) + .set(unauthorizedUserCredentials) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('isEnterprise', false); + }) + .end(done); + }); + + it('should pass if user has admin role', (done) => { + request + .get(api('licenses.isEnterprise')) + .set(credentials) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('isEnterprise', false); + }) + .end(done); + }); + }); }); diff --git a/packages/rest-typings/src/v1/licenses.ts b/packages/rest-typings/src/v1/licenses.ts index 88b1d34dc23f..c6d102a967e4 100644 --- a/packages/rest-typings/src/v1/licenses.ts +++ b/packages/rest-typings/src/v1/licenses.ts @@ -35,4 +35,7 @@ export type LicensesEndpoints = { '/v1/licenses.requestSeatsLink': { GET: () => { url: string }; }; + '/v1/licenses.isEnterprise': { + GET: () => { isEnterprise: boolean }; + }; };