Remove t from invite-related code (#27053)
* Remove t from actions/invite_actions * Remove t from components/invitation_modal and related code * Remove t from components/channel_invite_modal * Re-add the ability for reason to be a string * Address feedback
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
497870a565
Коммит
38b5974410
@@ -37,11 +37,11 @@ jest.mock('mattermost-redux/actions/teams', () => ({
|
||||
getTeamMembersByIds: () => ({type: 'MOCK_RECEIVED_ME'}),
|
||||
sendEmailInvitesToTeamGracefully: (team: string, emails: string[]) => {
|
||||
if (team === 'incorrect-default-smtp') {
|
||||
return ({type: 'MOCK_RECEIVED_ME', data: emails.map((email) => ({email, error: {message: 'SMTP is not configured in System Console.', id: 'api.team.invite_members.unable_to_send_email_with_defaults.app_error'}}))});
|
||||
return ({type: 'MOCK_RECEIVED_ME', data: emails.map((email) => ({email, error: {message: '(From server) SMTP is not configured in System Console.', id: 'api.team.invite_members.unable_to_send_email_with_defaults.app_error'}}))});
|
||||
} else if (emails.length > 21) { // Poor attempt to mock rate limiting.
|
||||
return ({type: 'MOCK_RECEIVED_ME', data: emails.map((email) => ({email, error: {message: 'Invite emails rate limit exceeded.'}}))});
|
||||
return ({type: 'MOCK_RECEIVED_ME', data: emails.map((email) => ({email, error: {message: '(From server) Invite emails rate limit exceeded.'}}))});
|
||||
} else if (team === 'error') {
|
||||
return ({type: 'MOCK_RECEIVED_ME', data: emails.map((email) => ({email, error: {message: 'Unable to add the user to the team.'}}))});
|
||||
return ({type: 'MOCK_RECEIVED_ME', data: emails.map((email) => ({email, error: {message: '(From server) Unable to add the user to the team.'}}))});
|
||||
}
|
||||
|
||||
// team === 'correct' i.e no error
|
||||
@@ -50,11 +50,11 @@ jest.mock('mattermost-redux/actions/teams', () => ({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
sendEmailGuestInvitesToChannelsGracefully: (teamId: string, _channelIds: string[], emails: string[], _message: string) => {
|
||||
if (teamId === 'incorrect-default-smtp') {
|
||||
return ({type: 'MOCK_RECEIVED_ME', data: emails.map((email) => ({email, error: {message: 'SMTP is not configured in System Console.', id: 'api.team.invite_members.unable_to_send_email_with_defaults.app_error'}}))});
|
||||
return ({type: 'MOCK_RECEIVED_ME', data: emails.map((email) => ({email, error: {message: '(From server) SMTP is not configured in System Console.', id: 'api.team.invite_members.unable_to_send_email_with_defaults.app_error'}}))});
|
||||
} else if (emails.length > 21) { // Poor attempt to mock rate limiting.
|
||||
return ({type: 'MOCK_RECEIVED_ME', data: emails.map((email) => ({email, error: {message: 'Invite emails rate limit exceeded.'}}))});
|
||||
return ({type: 'MOCK_RECEIVED_ME', data: emails.map((email) => ({email, error: {message: '(From server) Invite emails rate limit exceeded.'}}))});
|
||||
} else if (teamId === 'error') {
|
||||
return ({type: 'MOCK_RECEIVED_ME', data: emails.map((email) => ({email, error: {message: 'Unable to add the guest to the channels.'}}))});
|
||||
return ({type: 'MOCK_RECEIVED_ME', data: emails.map((email) => ({email, error: {message: '(From server) Unable to add the guest to the channels.'}}))});
|
||||
}
|
||||
|
||||
// teamId === 'correct' i.e no error
|
||||
@@ -141,15 +141,24 @@ describe('actions/invite_actions', () => {
|
||||
sent: [
|
||||
{
|
||||
email: 'email-one@email-one.com',
|
||||
reason: 'An invitation email has been sent.',
|
||||
reason: {
|
||||
id: 'invite.members.invite-sent',
|
||||
defaultMessage: 'An invitation email has been sent.',
|
||||
},
|
||||
},
|
||||
{
|
||||
email: 'email-two@email-two.com',
|
||||
reason: 'An invitation email has been sent.',
|
||||
reason: {
|
||||
id: 'invite.members.invite-sent',
|
||||
defaultMessage: 'An invitation email has been sent.',
|
||||
},
|
||||
},
|
||||
{
|
||||
email: 'email-three@email-three.com',
|
||||
reason: 'An invitation email has been sent.',
|
||||
reason: {
|
||||
id: 'invite.members.invite-sent',
|
||||
defaultMessage: 'An invitation email has been sent.',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -165,15 +174,15 @@ describe('actions/invite_actions', () => {
|
||||
notSent: [
|
||||
{
|
||||
email: 'email-one@email-one.com',
|
||||
reason: 'Unable to add the user to the team.',
|
||||
reason: '(From server) Unable to add the user to the team.',
|
||||
},
|
||||
{
|
||||
email: 'email-two@email-two.com',
|
||||
reason: 'Unable to add the user to the team.',
|
||||
reason: '(From server) Unable to add the user to the team.',
|
||||
},
|
||||
{
|
||||
email: 'email-three@email-three.com',
|
||||
reason: 'Unable to add the user to the team.',
|
||||
reason: '(From server) Unable to add the user to the team.',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -192,7 +201,10 @@ describe('actions/invite_actions', () => {
|
||||
data: {
|
||||
sent: [
|
||||
{
|
||||
reason: 'This member has been added to the team.',
|
||||
reason: {
|
||||
id: 'invite.members.added-to-team',
|
||||
defaultMessage: 'This member has been added to the team.',
|
||||
},
|
||||
user: {
|
||||
id: 'other-user',
|
||||
roles: 'system_user',
|
||||
@@ -201,21 +213,30 @@ describe('actions/invite_actions', () => {
|
||||
],
|
||||
notSent: [
|
||||
{
|
||||
reason: 'This person is already a team member.',
|
||||
reason: {
|
||||
id: 'invite.members.already-member',
|
||||
defaultMessage: 'This person is already a team member.',
|
||||
},
|
||||
user: {
|
||||
id: 'user1',
|
||||
roles: 'system_user',
|
||||
},
|
||||
},
|
||||
{
|
||||
reason: 'Contact your admin to make this guest a full member.',
|
||||
reason: {
|
||||
id: 'invite.members.user-is-guest',
|
||||
defaultMessage: 'Contact your admin to make this guest a full member.',
|
||||
},
|
||||
user: {
|
||||
id: 'guest1',
|
||||
roles: 'system_guest',
|
||||
},
|
||||
},
|
||||
{
|
||||
reason: 'Contact your admin to make this guest a full member.',
|
||||
reason: {
|
||||
id: 'invite.members.user-is-guest',
|
||||
defaultMessage: 'Contact your admin to make this guest a full member.',
|
||||
},
|
||||
user: {
|
||||
id: 'other-guest',
|
||||
roles: 'system_guest',
|
||||
@@ -236,24 +257,44 @@ describe('actions/invite_actions', () => {
|
||||
const response = await store.dispatch(sendMembersInvites('error', users, []));
|
||||
expect(response).toEqual({
|
||||
data: {
|
||||
sent: [{user: {id: 'other-user', roles: 'system_user'}, reason: 'This member has been added to the team.'}],
|
||||
sent: [
|
||||
{
|
||||
reason: {
|
||||
id: 'invite.members.added-to-team',
|
||||
defaultMessage: 'This member has been added to the team.',
|
||||
},
|
||||
user: {
|
||||
id: 'other-user',
|
||||
roles: 'system_user',
|
||||
},
|
||||
},
|
||||
],
|
||||
notSent: [
|
||||
{
|
||||
reason: 'This person is already a team member.',
|
||||
reason: {
|
||||
id: 'invite.members.already-member',
|
||||
defaultMessage: 'This person is already a team member.',
|
||||
},
|
||||
user: {
|
||||
id: 'user1',
|
||||
roles: 'system_user',
|
||||
},
|
||||
},
|
||||
{
|
||||
reason: 'Contact your admin to make this guest a full member.',
|
||||
reason: {
|
||||
id: 'invite.members.user-is-guest',
|
||||
defaultMessage: 'Contact your admin to make this guest a full member.',
|
||||
},
|
||||
user: {
|
||||
id: 'guest1',
|
||||
roles: 'system_guest',
|
||||
},
|
||||
},
|
||||
{
|
||||
reason: 'Contact your admin to make this guest a full member.',
|
||||
reason: {
|
||||
id: 'invite.members.user-is-guest',
|
||||
defaultMessage: 'Contact your admin to make this guest a full member.',
|
||||
},
|
||||
user: {
|
||||
id: 'other-guest',
|
||||
roles: 'system_guest',
|
||||
@@ -271,7 +312,7 @@ describe('actions/invite_actions', () => {
|
||||
emails.push('email-' + i + '@example.com');
|
||||
expectedNotSent.push({
|
||||
email: 'email-' + i + '@example.com',
|
||||
reason: 'Invite emails rate limit exceeded.',
|
||||
reason: '(From server) Invite emails rate limit exceeded.',
|
||||
});
|
||||
}
|
||||
const response = await store.dispatch(sendMembersInvites('correct', [], emails));
|
||||
@@ -293,7 +334,7 @@ describe('actions/invite_actions', () => {
|
||||
email: 'email-one@email-one.com',
|
||||
reason: {
|
||||
id: 'admin.environment.smtp.smtpFailure',
|
||||
message: 'SMTP is not configured in System Console. Can be configured <a>here</a>.',
|
||||
defaultMessage: 'SMTP is not configured in System Console. Can be configured <a>here</a>.',
|
||||
},
|
||||
path: ConsolePages.SMTP,
|
||||
}],
|
||||
@@ -324,15 +365,24 @@ describe('actions/invite_actions', () => {
|
||||
sent: [
|
||||
{
|
||||
email: 'email-one@email-one.com',
|
||||
reason: 'An invitation email has been sent.',
|
||||
reason: {
|
||||
id: 'invite.guests.added-to-channel',
|
||||
defaultMessage: 'An invitation email has been sent.',
|
||||
},
|
||||
},
|
||||
{
|
||||
email: 'email-two@email-two.com',
|
||||
reason: 'An invitation email has been sent.',
|
||||
reason: {
|
||||
id: 'invite.guests.added-to-channel',
|
||||
defaultMessage: 'An invitation email has been sent.',
|
||||
},
|
||||
},
|
||||
{
|
||||
email: 'email-three@email-three.com',
|
||||
reason: 'An invitation email has been sent.',
|
||||
reason: {
|
||||
id: 'invite.guests.added-to-channel',
|
||||
defaultMessage: 'An invitation email has been sent.',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -349,15 +399,15 @@ describe('actions/invite_actions', () => {
|
||||
notSent: [
|
||||
{
|
||||
email: 'email-one@email-one.com',
|
||||
reason: 'Unable to add the guest to the channels.',
|
||||
reason: '(From server) Unable to add the guest to the channels.',
|
||||
},
|
||||
{
|
||||
email: 'email-two@email-two.com',
|
||||
reason: 'Unable to add the guest to the channels.',
|
||||
reason: '(From server) Unable to add the guest to the channels.',
|
||||
},
|
||||
{
|
||||
email: 'email-three@email-three.com',
|
||||
reason: 'Unable to add the guest to the channels.',
|
||||
reason: '(From server) Unable to add the guest to the channels.',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -379,7 +429,7 @@ describe('actions/invite_actions', () => {
|
||||
{
|
||||
reason: {
|
||||
id: 'invite.guests.new-member',
|
||||
message: 'This guest has been added to the team and {count, plural, one {channel} other {channels}}.',
|
||||
defaultMessage: 'This guest has been added to the team and {count, plural, one {channel} other {channels}}.',
|
||||
values: {count: channels.length},
|
||||
},
|
||||
user: {
|
||||
@@ -390,7 +440,7 @@ describe('actions/invite_actions', () => {
|
||||
{
|
||||
reason: {
|
||||
id: 'invite.guests.new-member',
|
||||
message: 'This guest has been added to the team and {count, plural, one {channel} other {channels}}.',
|
||||
defaultMessage: 'This guest has been added to the team and {count, plural, one {channel} other {channels}}.',
|
||||
values: {count: channels.length},
|
||||
},
|
||||
user: {
|
||||
@@ -401,14 +451,20 @@ describe('actions/invite_actions', () => {
|
||||
],
|
||||
notSent: [
|
||||
{
|
||||
reason: 'This person is already a member of the workspace. Invite them as a member instead of a guest.',
|
||||
reason: {
|
||||
id: 'invite.members.user-is-not-guest',
|
||||
defaultMessage: 'This person is already a member of the workspace. Invite them as a member instead of a guest.',
|
||||
},
|
||||
user: {
|
||||
id: 'user1',
|
||||
roles: 'system_user',
|
||||
},
|
||||
},
|
||||
{
|
||||
reason: 'This person is already a member of the workspace. Invite them as a member instead of a guest.',
|
||||
reason: {
|
||||
id: 'invite.members.user-is-not-guest',
|
||||
defaultMessage: 'This person is already a member of the workspace. Invite them as a member instead of a guest.',
|
||||
},
|
||||
user: {
|
||||
id: 'other-user',
|
||||
roles: 'system_user',
|
||||
@@ -430,14 +486,20 @@ describe('actions/invite_actions', () => {
|
||||
sent: [],
|
||||
notSent: [
|
||||
{
|
||||
reason: 'This person is already a member of all the channels.',
|
||||
reason: {
|
||||
id: 'invite.guests.already-all-channels-member',
|
||||
defaultMessage: 'This person is already a member of all the channels.',
|
||||
},
|
||||
user: {
|
||||
id: 'guest2',
|
||||
roles: 'system_guest',
|
||||
},
|
||||
},
|
||||
{
|
||||
reason: 'This person is already a member of some of the channels.',
|
||||
reason: {
|
||||
id: 'invite.guests.already-some-channels-member',
|
||||
defaultMessage: 'This person is already a member of some of the channels.',
|
||||
},
|
||||
user: {
|
||||
id: 'guest3',
|
||||
roles: 'system_guest',
|
||||
@@ -467,7 +529,7 @@ describe('actions/invite_actions', () => {
|
||||
},
|
||||
reason: {
|
||||
id: 'invite.guests.new-member',
|
||||
message: 'This guest has been added to the team and {count, plural, one {channel} other {channels}}.',
|
||||
defaultMessage: 'This guest has been added to the team and {count, plural, one {channel} other {channels}}.',
|
||||
values: {
|
||||
count: 1,
|
||||
},
|
||||
@@ -480,7 +542,7 @@ describe('actions/invite_actions', () => {
|
||||
},
|
||||
reason: {
|
||||
id: 'invite.guests.new-member',
|
||||
message: 'This guest has been added to the team and {count, plural, one {channel} other {channels}}.',
|
||||
defaultMessage: 'This guest has been added to the team and {count, plural, one {channel} other {channels}}.',
|
||||
values: {
|
||||
count: 1,
|
||||
},
|
||||
@@ -489,14 +551,20 @@ describe('actions/invite_actions', () => {
|
||||
],
|
||||
notSent: [
|
||||
{
|
||||
reason: 'This person is already a member of the workspace. Invite them as a member instead of a guest.',
|
||||
reason: {
|
||||
id: 'invite.members.user-is-not-guest',
|
||||
defaultMessage: 'This person is already a member of the workspace. Invite them as a member instead of a guest.',
|
||||
},
|
||||
user: {
|
||||
id: 'user1',
|
||||
roles: 'system_user',
|
||||
},
|
||||
},
|
||||
{
|
||||
reason: 'This person is already a member of the workspace. Invite them as a member instead of a guest.',
|
||||
reason: {
|
||||
id: 'invite.members.user-is-not-guest',
|
||||
defaultMessage: 'This person is already a member of the workspace. Invite them as a member instead of a guest.',
|
||||
},
|
||||
user: {
|
||||
id: 'other-user',
|
||||
roles: 'system_user',
|
||||
@@ -520,28 +588,40 @@ describe('actions/invite_actions', () => {
|
||||
sent: [],
|
||||
notSent: [
|
||||
{
|
||||
reason: 'This person is already a member of the workspace. Invite them as a member instead of a guest.',
|
||||
reason: {
|
||||
id: 'invite.members.user-is-not-guest',
|
||||
defaultMessage: 'This person is already a member of the workspace. Invite them as a member instead of a guest.',
|
||||
},
|
||||
user: {
|
||||
id: 'user1',
|
||||
roles: 'system_user',
|
||||
},
|
||||
},
|
||||
{
|
||||
reason: 'Unable to add the guest to the channels.',
|
||||
reason: {
|
||||
id: 'invite.guests.unable-to-add-the-user-to-the-channels',
|
||||
defaultMessage: 'Unable to add the guest to the channels.',
|
||||
},
|
||||
user: {
|
||||
id: 'guest1',
|
||||
roles: 'system_guest',
|
||||
},
|
||||
},
|
||||
{
|
||||
reason: 'This person is already a member of the workspace. Invite them as a member instead of a guest.',
|
||||
reason: {
|
||||
id: 'invite.members.user-is-not-guest',
|
||||
defaultMessage: 'This person is already a member of the workspace. Invite them as a member instead of a guest.',
|
||||
},
|
||||
user: {
|
||||
id: 'other-user',
|
||||
roles: 'system_user',
|
||||
},
|
||||
},
|
||||
{
|
||||
reason: 'Unable to add the guest to the channels.',
|
||||
reason: {
|
||||
id: 'invite.guests.unable-to-add-the-user-to-the-channels',
|
||||
defaultMessage: 'Unable to add the guest to the channels.',
|
||||
},
|
||||
user: {
|
||||
id: 'other-guest',
|
||||
roles: 'system_guest',
|
||||
@@ -559,7 +639,7 @@ describe('actions/invite_actions', () => {
|
||||
emails.push('email-' + i + '@example.com');
|
||||
expectedNotSent.push({
|
||||
email: 'email-' + i + '@example.com',
|
||||
reason: 'Invite emails rate limit exceeded.',
|
||||
reason: '(From server) Invite emails rate limit exceeded.',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -582,7 +662,7 @@ describe('actions/invite_actions', () => {
|
||||
email: 'email-one@email-one.com',
|
||||
reason: {
|
||||
id: 'admin.environment.smtp.smtpFailure',
|
||||
message: 'SMTP is not configured in System Console. Can be configured <a>here</a>.',
|
||||
defaultMessage: 'SMTP is not configured in System Console. Can be configured <a>here</a>.',
|
||||
},
|
||||
path: ConsolePages.SMTP,
|
||||
}],
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {defineMessage} from 'react-intl';
|
||||
|
||||
import type {Channel, ChannelMembership} from '@mattermost/types/channels';
|
||||
import type {TeamMemberWithError, TeamInviteWithError} from '@mattermost/types/teams';
|
||||
import type {UserProfile} from '@mattermost/types/users';
|
||||
@@ -16,11 +18,10 @@ import {isGuest} from 'mattermost-redux/utils/user_utils';
|
||||
|
||||
import {addUsersToTeam} from 'actions/team_actions';
|
||||
|
||||
import type {InviteResult} from 'components/invitation_modal/result_table';
|
||||
import type {InviteResults} from 'components/invitation_modal/result_view';
|
||||
|
||||
import {ConsolePages} from 'utils/constants';
|
||||
import {t} from 'utils/i18n';
|
||||
import {localizeMessage} from 'utils/utils';
|
||||
|
||||
export function sendMembersInvites(teamId: string, users: UserProfile[], emails: string[]): ActionFuncAsync<InviteResults> {
|
||||
return async (dispatch, getState) => {
|
||||
@@ -34,9 +35,21 @@ export function sendMembersInvites(teamId: string, users: UserProfile[], emails:
|
||||
for (const user of users) {
|
||||
const member = getTeamMember(state, teamId, user.id);
|
||||
if (isGuest(user.roles)) {
|
||||
notSent.push({user, reason: localizeMessage('invite.members.user-is-guest', 'Contact your admin to make this guest a full member.')});
|
||||
notSent.push({
|
||||
user,
|
||||
reason: defineMessage({
|
||||
id: 'invite.members.user-is-guest',
|
||||
defaultMessage: 'Contact your admin to make this guest a full member.',
|
||||
}),
|
||||
});
|
||||
} else if (member) {
|
||||
notSent.push({user, reason: localizeMessage('invite.members.already-member', 'This person is already a team member.')});
|
||||
notSent.push({
|
||||
user,
|
||||
reason: defineMessage({
|
||||
id: 'invite.members.already-member',
|
||||
defaultMessage: 'This person is already a team member.',
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
usersToAdd.push(user);
|
||||
}
|
||||
@@ -52,9 +65,18 @@ export function sendMembersInvites(teamId: string, users: UserProfile[], emails:
|
||||
for (const userToAdd of usersToAdd) {
|
||||
const memberWithError = members.find((m: TeamMemberWithError) => m.user_id === userToAdd.id && m.error);
|
||||
if (memberWithError) {
|
||||
notSent.push({user: userToAdd, reason: memberWithError.error.message});
|
||||
notSent.push({
|
||||
user: userToAdd,
|
||||
reason: memberWithError.error.message,
|
||||
});
|
||||
} else {
|
||||
sent.push({user: userToAdd, reason: localizeMessage('invite.members.added-to-team', 'This member has been added to the team.')});
|
||||
sent.push({
|
||||
user: userToAdd,
|
||||
reason: defineMessage({
|
||||
id: 'invite.members.added-to-team',
|
||||
defaultMessage: 'This member has been added to the team.',
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,17 +89,28 @@ export function sendMembersInvites(teamId: string, users: UserProfile[], emails:
|
||||
response = {
|
||||
data: emails.map((email) => ({
|
||||
email,
|
||||
error: {error: localizeMessage('invite.members.unable-to-add-the-user-to-the-team', 'Unable to add the user to the team.')},
|
||||
error: {
|
||||
error: defineMessage({
|
||||
id: 'invite.members.unable-to-add-the-user-to-the-team',
|
||||
defaultMessage: 'Unable to add the user to the team.',
|
||||
}),
|
||||
},
|
||||
})) as unknown as TeamInviteWithError[],
|
||||
};
|
||||
}
|
||||
const invitesWithErrors = response.data || [];
|
||||
if (response.error) {
|
||||
if (response.error.server_error_id === 'app.email.rate_limit_exceeded.app_error') {
|
||||
response.error.message = localizeMessage('invite.rate-limit-exceeded', 'Invite emails rate limit exceeded.');
|
||||
response.error.message = defineMessage({
|
||||
id: 'invite.rate-limit-exceeded',
|
||||
defaultMessage: 'Invite emails rate limit exceeded.',
|
||||
});
|
||||
}
|
||||
for (const email of emails) {
|
||||
notSent.push({email, reason: response.error.message});
|
||||
notSent.push({
|
||||
email,
|
||||
reason: response.error.message,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (const email of emails) {
|
||||
@@ -85,16 +118,25 @@ export function sendMembersInvites(teamId: string, users: UserProfile[], emails:
|
||||
if (inviteWithError && inviteWithError.error.id === 'api.team.invite_members.unable_to_send_email_with_defaults.app_error' && isCurrentUserSystemAdmin(state)) {
|
||||
notSent.push({
|
||||
email,
|
||||
reason: {
|
||||
id: t('admin.environment.smtp.smtpFailure'),
|
||||
message: 'SMTP is not configured in System Console. Can be configured <a>here</a>.',
|
||||
},
|
||||
reason: defineMessage({
|
||||
id: 'admin.environment.smtp.smtpFailure',
|
||||
defaultMessage: 'SMTP is not configured in System Console. Can be configured <a>here</a>.',
|
||||
}),
|
||||
path: ConsolePages.SMTP,
|
||||
});
|
||||
} else if (inviteWithError) {
|
||||
notSent.push({email, reason: inviteWithError.error.message});
|
||||
notSent.push({
|
||||
email,
|
||||
reason: inviteWithError.error.message,
|
||||
});
|
||||
} else {
|
||||
sent.push({email, reason: localizeMessage('invite.members.invite-sent', 'An invitation email has been sent.')});
|
||||
sent.push({
|
||||
email,
|
||||
reason: defineMessage({
|
||||
id: 'invite.members.invite-sent',
|
||||
defaultMessage: 'An invitation email has been sent.',
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,9 +156,17 @@ export async function sendGuestInviteForUser(
|
||||
teamId: string,
|
||||
channels: Channel[],
|
||||
members: RelationOneToOne<Channel, Record<string, ChannelMembership>>,
|
||||
) {
|
||||
): Promise<({sent: InviteResult} | {notSent: InviteResult})> {
|
||||
if (!isGuest(user.roles)) {
|
||||
return {notSent: {user, reason: localizeMessage('invite.members.user-is-not-guest', 'This person is already a member of the workspace. Invite them as a member instead of a guest.')}};
|
||||
return {
|
||||
notSent: {
|
||||
user,
|
||||
reason: defineMessage({
|
||||
id: 'invite.members.user-is-not-guest',
|
||||
defaultMessage: 'This person is already a member of the workspace. Invite them as a member instead of a guest.',
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
let memberOfAll = true;
|
||||
let memberOfAny = false;
|
||||
@@ -131,7 +181,15 @@ export async function sendGuestInviteForUser(
|
||||
}
|
||||
|
||||
if (memberOfAll) {
|
||||
return {notSent: {user, reason: localizeMessage('invite.guests.already-all-channels-member', 'This person is already a member of all the channels.')}};
|
||||
return {
|
||||
notSent: {
|
||||
user,
|
||||
reason: defineMessage({
|
||||
id: 'invite.guests.already-all-channels-member',
|
||||
defaultMessage: 'This person is already a member of all the channels.',
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -143,13 +201,40 @@ export async function sendGuestInviteForUser(
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
return {notSent: {user, reason: localizeMessage('invite.guests.unable-to-add-the-user-to-the-channels', 'Unable to add the guest to the channels.')}};
|
||||
return {
|
||||
notSent: {
|
||||
user,
|
||||
reason: defineMessage({
|
||||
id: 'invite.guests.unable-to-add-the-user-to-the-channels',
|
||||
defaultMessage: 'Unable to add the guest to the channels.',
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (memberOfAny) {
|
||||
return {notSent: {user, reason: localizeMessage('invite.guests.already-some-channels-member', 'This person is already a member of some of the channels.')}};
|
||||
return {
|
||||
notSent: {
|
||||
user,
|
||||
reason: defineMessage({
|
||||
id: 'invite.guests.already-some-channels-member',
|
||||
defaultMessage: 'This person is already a member of some of the channels.',
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
return {sent: {user, reason: {id: t('invite.guests.new-member'), message: 'This guest has been added to the team and {count, plural, one {channel} other {channels}}.', values: {count: channels.length}}}};
|
||||
return {
|
||||
sent: {
|
||||
user,
|
||||
reason: defineMessage({
|
||||
id: 'invite.guests.new-member',
|
||||
defaultMessage: 'This guest has been added to the team and {count, plural, one {channel} other {channels}}.',
|
||||
values: {
|
||||
count: channels.length,
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function sendGuestsInvites(
|
||||
@@ -167,10 +252,10 @@ export function sendGuestsInvites(
|
||||
const results = await Promise.all(users.map((user) => sendGuestInviteForUser(dispatch, user, teamId, channels, members)));
|
||||
|
||||
for (const result of results) {
|
||||
if (result.sent) {
|
||||
if ('sent' in result && result.sent) {
|
||||
sent.push(result.sent);
|
||||
}
|
||||
if (result.notSent) {
|
||||
if ('notSent' in result && result.notSent) {
|
||||
notSent.push(result.notSent);
|
||||
}
|
||||
}
|
||||
@@ -183,17 +268,28 @@ export function sendGuestsInvites(
|
||||
response = {
|
||||
data: emails.map((email) => ({
|
||||
email,
|
||||
error: {error: localizeMessage('invite.guests.unable-to-add-the-user-to-the-channels', 'Unable to add the guest to the channels.')},
|
||||
error: {
|
||||
error: defineMessage({
|
||||
id: 'invite.guests.unable-to-add-the-user-to-the-channels',
|
||||
defaultMessage: 'Unable to add the guest to the channels.',
|
||||
}),
|
||||
},
|
||||
})) as unknown as TeamInviteWithError[],
|
||||
};
|
||||
}
|
||||
|
||||
if (response.error) {
|
||||
if (response.error.server_error_id === 'app.email.rate_limit_exceeded.app_error') {
|
||||
response.error.message = localizeMessage('invite.rate-limit-exceeded', 'Invite emails rate limit exceeded.');
|
||||
response.error.message = defineMessage({
|
||||
id: 'invite.rate-limit-exceeded',
|
||||
defaultMessage: 'Invite emails rate limit exceeded.',
|
||||
});
|
||||
}
|
||||
for (const email of emails) {
|
||||
notSent.push({email, reason: response.error.message});
|
||||
notSent.push({
|
||||
email,
|
||||
reason: response.error.message,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (const res of (response.data || [])) {
|
||||
@@ -201,22 +297,36 @@ export function sendGuestsInvites(
|
||||
if (res.error.id === 'api.team.invite_members.unable_to_send_email_with_defaults.app_error' && isCurrentUserSystemAdmin(state)) {
|
||||
notSent.push({
|
||||
email: res.email,
|
||||
reason: {
|
||||
id: t('admin.environment.smtp.smtpFailure'),
|
||||
message: 'SMTP is not configured in System Console. Can be configured <a>here</a>.',
|
||||
},
|
||||
reason: defineMessage({
|
||||
id: 'admin.environment.smtp.smtpFailure',
|
||||
defaultMessage: 'SMTP is not configured in System Console. Can be configured <a>here</a>.',
|
||||
}),
|
||||
path: ConsolePages.SMTP,
|
||||
});
|
||||
} else {
|
||||
notSent.push({email: res.email, reason: res.error.message});
|
||||
notSent.push({
|
||||
email: res.email,
|
||||
reason: res.error.message,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
sent.push({email: res.email, reason: localizeMessage('invite.guests.added-to-channel', 'An invitation email has been sent.')});
|
||||
sent.push({
|
||||
email: res.email,
|
||||
reason: defineMessage({
|
||||
id: 'invite.guests.added-to-channel',
|
||||
defaultMessage: 'An invitation email has been sent.',
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {data: {sent, notSent}};
|
||||
return {
|
||||
data: {
|
||||
sent,
|
||||
notSent,
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -240,9 +350,21 @@ export function sendMembersInvitesToChannels(
|
||||
for (const user of users) {
|
||||
const member = getTeamMember(state, teamId, user.id);
|
||||
if (isGuest(user.roles)) {
|
||||
notSent.push({user, reason: localizeMessage('invite.members.user-is-guest', 'Contact your admin to make this guest a full member.')});
|
||||
notSent.push({
|
||||
user,
|
||||
reason: defineMessage({
|
||||
id: 'invite.members.user-is-guest',
|
||||
defaultMessage: 'Contact your admin to make this guest a full member.',
|
||||
}),
|
||||
});
|
||||
} else if (member) {
|
||||
notSent.push({user, reason: localizeMessage('invite.members.already-member', 'This person is already a team member.')});
|
||||
notSent.push({
|
||||
user,
|
||||
reason: defineMessage({
|
||||
id: 'invite.members.already-member',
|
||||
defaultMessage: 'This person is already a team member.',
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
usersToAdd.push(user);
|
||||
}
|
||||
@@ -252,15 +374,27 @@ export function sendMembersInvitesToChannels(
|
||||
const members = response.data || [];
|
||||
if (response.error) {
|
||||
for (const userToAdd of usersToAdd) {
|
||||
notSent.push({user: userToAdd, reason: response.error.message});
|
||||
notSent.push({
|
||||
user: userToAdd,
|
||||
reason: response.error.message,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (const userToAdd of usersToAdd) {
|
||||
const memberWithError = members.find((m: TeamMemberWithError) => m.user_id === userToAdd.id && m.error);
|
||||
if (memberWithError) {
|
||||
notSent.push({user: userToAdd, reason: memberWithError.error.message});
|
||||
notSent.push({
|
||||
user: userToAdd,
|
||||
reason: memberWithError.error.message,
|
||||
});
|
||||
} else {
|
||||
sent.push({user: userToAdd, reason: localizeMessage('invite.members.added-to-team', 'This member has been added to the team.')});
|
||||
sent.push({
|
||||
user: userToAdd,
|
||||
reason: defineMessage({
|
||||
id: 'invite.members.added-to-team',
|
||||
defaultMessage: 'This member has been added to the team.',
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -280,17 +414,25 @@ export function sendMembersInvitesToChannels(
|
||||
response = {
|
||||
data: emails.map((email) => ({
|
||||
email,
|
||||
error: {error: localizeMessage('invite.members.unable-to-add-the-user-to-the-team', 'Unable to add the user to the team.')},
|
||||
error: {
|
||||
error: defineMessage({
|
||||
id: 'invite.members.unable-to-add-the-user-to-the-team',
|
||||
defaultMessage: 'Unable to add the user to the team.',
|
||||
}),
|
||||
},
|
||||
})) as unknown as TeamInviteWithError[],
|
||||
};
|
||||
}
|
||||
const invitesWithErrors = response.data || [];
|
||||
if (response.error) {
|
||||
if (response.error.server_error_id === 'app.email.rate_limit_exceeded.app_error') {
|
||||
response.error.message = localizeMessage('invite.rate-limit-exceeded', 'Invite emails rate limit exceeded.');
|
||||
response.error.message = defineMessage({id: 'invite.rate-limit-exceeded', defaultMessage: 'Invite emails rate limit exceeded.'});
|
||||
}
|
||||
for (const email of emails) {
|
||||
notSent.push({email, reason: response.error.message});
|
||||
notSent.push({
|
||||
email,
|
||||
reason: response.error.message,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (const email of emails) {
|
||||
@@ -299,17 +441,26 @@ export function sendMembersInvitesToChannels(
|
||||
if (inviteWithError.error.id === 'api.team.invite_members.unable_to_send_email_with_defaults.app_error' && isCurrentUserSystemAdmin(state)) {
|
||||
notSent.push({
|
||||
email,
|
||||
reason: {
|
||||
id: t('admin.environment.smtp.smtpFailure'),
|
||||
message: 'SMTP is not configured in System Console. Can be configured <a>here</a>.',
|
||||
},
|
||||
reason: defineMessage({
|
||||
id: 'admin.environment.smtp.smtpFailure',
|
||||
defaultMessage: 'SMTP is not configured in System Console. Can be configured <a>here</a>.',
|
||||
}),
|
||||
path: ConsolePages.SMTP,
|
||||
});
|
||||
} else {
|
||||
notSent.push({email, reason: inviteWithError.error.message});
|
||||
notSent.push({
|
||||
email,
|
||||
reason: inviteWithError.error.message,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
sent.push({email, reason: localizeMessage('invite.members.invite-sent', 'An invitation email has been sent.')});
|
||||
sent.push({
|
||||
email,
|
||||
reason: defineMessage({
|
||||
id: 'invite.members.invite-sent',
|
||||
defaultMessage: 'An invitation email has been sent.',
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@ import AtMention from 'components/at_mention';
|
||||
import type {Value} from 'components/multiselect/multiselect';
|
||||
import SimpleTooltip from 'components/widgets/simple_tooltip';
|
||||
|
||||
import {t} from 'utils/i18n';
|
||||
|
||||
type UserProfileValue = Value & UserProfile;
|
||||
|
||||
export type Props = {
|
||||
@@ -53,7 +51,7 @@ const TeamWarningBanner = (props: Props) => {
|
||||
return (
|
||||
formatMessage(
|
||||
{
|
||||
id: t('channel_invite.invite_team_members.guests.messageOverflow'),
|
||||
id: 'channel_invite.invite_team_members.guests.messageOverflow',
|
||||
defaultMessage: '{firstUser} and {others} are guest users and need to first be invited to the team before you can add them to the channel. Once they\'ve joined the team, you can add them to this channel.',
|
||||
},
|
||||
{
|
||||
@@ -98,7 +96,7 @@ const TeamWarningBanner = (props: Props) => {
|
||||
return (
|
||||
formatMessage(
|
||||
{
|
||||
id: t('channel_invite.invite_team_members.guests.message'),
|
||||
id: 'channel_invite.invite_team_members.guests.message',
|
||||
defaultMessage: '{count, plural, =1 {{firstUser} is a guest user and needs} other {{users} are guest users and need}} to first be invited to the team before you can add them to the channel. Once they\'ve joined the team, you can add them to this channel.',
|
||||
},
|
||||
{
|
||||
@@ -123,7 +121,7 @@ const TeamWarningBanner = (props: Props) => {
|
||||
if (users.length > 10) {
|
||||
return formatMessage(
|
||||
{
|
||||
id: t('channel_invite.invite_team_members.messageOverflow'),
|
||||
id: 'channel_invite.invite_team_members.messageOverflow',
|
||||
defaultMessage: 'You can add {firstUser} and {others} to this channel once they are members of the {team} team.',
|
||||
},
|
||||
{
|
||||
@@ -168,7 +166,7 @@ const TeamWarningBanner = (props: Props) => {
|
||||
return (
|
||||
formatMessage(
|
||||
{
|
||||
id: t('channel_invite.invite_team_members.message'),
|
||||
id: 'channel_invite.invite_team_members.message',
|
||||
defaultMessage: 'You can add {count, plural, =1 {{firstUser}} other {{users}}} to this channel once they are members of the {team} team.',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@ import {SelfHostedProducts} from 'utils/constants';
|
||||
import {TestHelper} from 'utils/test_helper';
|
||||
import {generateId} from 'utils/utils';
|
||||
|
||||
import InvitationModal, {View, InvitationModal as BaseInvitationModal} from './invitation_modal';
|
||||
import InvitationModal, {View} from './invitation_modal';
|
||||
import type {Props} from './invitation_modal';
|
||||
import InviteView from './invite_view';
|
||||
import NoPermissionsView from './no_permissions_view';
|
||||
@@ -129,7 +129,7 @@ describe('InvitationModal', () => {
|
||||
<InvitationModal {...props}/>
|
||||
</Provider>,
|
||||
);
|
||||
wrapper.find(BaseInvitationModal).at(0).setState({view: View.RESULT});
|
||||
wrapper.find(InvitationModal).at(0).setState({view: View.RESULT});
|
||||
|
||||
wrapper.update();
|
||||
expect(wrapper.find(ResultView).length).toBe(1);
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import {Modal} from 'react-bootstrap';
|
||||
import {injectIntl} from 'react-intl';
|
||||
import type {IntlShape} from 'react-intl';
|
||||
import {defineMessages} from 'react-intl';
|
||||
|
||||
import type {Channel} from '@mattermost/types/channels';
|
||||
import type {Team} from '@mattermost/types/teams';
|
||||
@@ -31,6 +30,17 @@ import './invitation_modal.scss';
|
||||
// false means no backdrop
|
||||
type Backdrop = 'static' | boolean
|
||||
|
||||
const messages = defineMessages({
|
||||
notValidChannel: {
|
||||
id: 'invitation-modal.confirm.not-valid-channel',
|
||||
defaultMessage: 'Does not match a valid channel name.',
|
||||
},
|
||||
notValidUserOrEmail: {
|
||||
id: 'invitation-modal.confirm.not-valid-user-or-email',
|
||||
defaultMessage: 'Does not match a valid user or email.',
|
||||
},
|
||||
});
|
||||
|
||||
export type Props = {
|
||||
actions: {
|
||||
searchChannels: (teamId: string, term: string) => Promise<ActionResult<Channel[]>>;
|
||||
@@ -66,7 +76,6 @@ export type Props = {
|
||||
isCloud: boolean;
|
||||
canAddUsers: boolean;
|
||||
canInviteGuests: boolean;
|
||||
intl: IntlShape;
|
||||
onExited: () => void;
|
||||
channelToInvite?: Channel;
|
||||
initialValue?: string;
|
||||
@@ -89,7 +98,7 @@ type State = {
|
||||
show: boolean;
|
||||
};
|
||||
|
||||
export class InvitationModal extends React.PureComponent<Props, State> {
|
||||
export default class InvitationModal extends React.PureComponent<Props, State> {
|
||||
defaultState: State = deepFreeze({
|
||||
view: View.INVITE,
|
||||
termWithoutResults: null,
|
||||
@@ -206,20 +215,14 @@ export class InvitationModal extends React.PureComponent<Props, State> {
|
||||
if (this.state.invite.usersEmailsSearch !== '') {
|
||||
invites.notSent.push({
|
||||
text: this.state.invite.usersEmailsSearch,
|
||||
reason: this.props.intl.formatMessage({
|
||||
id: 'invitation-modal.confirm.not-valid-user-or-email',
|
||||
defaultMessage: 'Does not match a valid user or email.',
|
||||
}),
|
||||
reason: messages.notValidUserOrEmail,
|
||||
});
|
||||
}
|
||||
|
||||
if (inviteAs === InviteType.GUEST && this.state.invite.inviteChannels.search !== '') {
|
||||
invites.notSent.push({
|
||||
text: this.state.invite.inviteChannels.search,
|
||||
reason: this.props.intl.formatMessage({
|
||||
id: 'invitation-modal.confirm.not-valid-channel',
|
||||
defaultMessage: 'Does not match a valid channel name.',
|
||||
}),
|
||||
reason: messages.notValidChannel,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -431,5 +434,3 @@ export class InvitationModal extends React.PureComponent<Props, State> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectIntl(InvitationModal);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import classNames from 'classnames';
|
||||
import React, {useEffect, useMemo} from 'react';
|
||||
import {Modal} from 'react-bootstrap';
|
||||
import {FormattedMessage, useIntl} from 'react-intl';
|
||||
import {FormattedMessage, defineMessages, useIntl} from 'react-intl';
|
||||
import {useSelector} from 'react-redux';
|
||||
|
||||
import type {Channel} from '@mattermost/types/channels';
|
||||
@@ -20,7 +20,6 @@ import {getAnalyticsCategory} from 'components/onboarding_tasks';
|
||||
import UsersEmailsInput from 'components/widgets/inputs/users_emails_input';
|
||||
|
||||
import {Constants} from 'utils/constants';
|
||||
import {t} from 'utils/i18n';
|
||||
import {getSiteURL} from 'utils/url';
|
||||
import {getTrackFlowRole, getRoleForTrackFlow, getSourceForTrackFlow} from 'utils/utils';
|
||||
|
||||
@@ -133,42 +132,37 @@ export default function InviteView(props: Props) {
|
||||
|
||||
const errorProperties = {
|
||||
showError: false,
|
||||
errorMessageId: '',
|
||||
errorMessageDefault: '',
|
||||
errorMessage: messages.exceededMaxBatch,
|
||||
errorMessageValues: {
|
||||
text: '',
|
||||
text: Constants.MAX_ADD_MEMBERS_BATCH.toString(),
|
||||
},
|
||||
extraErrorText: '',
|
||||
};
|
||||
|
||||
if (props.usersEmails.length > Constants.MAX_ADD_MEMBERS_BATCH) {
|
||||
errorProperties.showError = true;
|
||||
errorProperties.errorMessageId = t(
|
||||
'invitation_modal.invite_members.exceeded_max_add_members_batch',
|
||||
);
|
||||
errorProperties.errorMessageDefault = 'No more than **{text}** people can be invited at once';
|
||||
errorProperties.errorMessageValues.text = Constants.MAX_ADD_MEMBERS_BATCH.toString();
|
||||
}
|
||||
|
||||
let placeholder = formatMessage({
|
||||
id: 'invite_modal.add_invites',
|
||||
defaultMessage: 'Enter a name or email address',
|
||||
});
|
||||
let noMatchMessageId = t(
|
||||
'invitation_modal.members.users_emails_input.no_user_found_matching',
|
||||
);
|
||||
let noMatchMessageDefault =
|
||||
'No one found matching **{text}**. Enter their email to invite them.';
|
||||
|
||||
if (!props.emailInvitationsEnabled) {
|
||||
let placeholder;
|
||||
let noMatchMessage;
|
||||
if (props.emailInvitationsEnabled) {
|
||||
placeholder = formatMessage({
|
||||
id: 'invite_modal.add_invites',
|
||||
defaultMessage: 'Enter a name or email address',
|
||||
});
|
||||
noMatchMessage = messages.noUserFound;
|
||||
} else {
|
||||
placeholder = formatMessage({
|
||||
id: 'invitation_modal.members.search-and-add.placeholder-email-disabled',
|
||||
defaultMessage: 'Add members',
|
||||
});
|
||||
noMatchMessageId = t(
|
||||
'invitation_modal.members.users_emails_input.no_user_found_matching-email-disabled',
|
||||
);
|
||||
noMatchMessageDefault = 'No one found matching **{text}**';
|
||||
noMatchMessage = messages.noUserFoundEmailDisabled;
|
||||
}
|
||||
|
||||
let validAddressMessage;
|
||||
if (props.inviteType === InviteType.MEMBER) {
|
||||
validAddressMessage = messages.validAddressMember;
|
||||
} else {
|
||||
validAddressMessage = messages.validAddressGuest;
|
||||
}
|
||||
|
||||
const isInviteValid = useMemo(() => {
|
||||
@@ -233,12 +227,8 @@ export default function InviteView(props: Props) {
|
||||
props.onChangeUsersEmails(usersEmails);
|
||||
}}
|
||||
value={props.usersEmails}
|
||||
validAddressMessageId={props.inviteType === InviteType.MEMBER ? t(
|
||||
'invitation_modal.members.users_emails_input.valid_email',
|
||||
) : t('invitation_modal.guests.users_emails_input.valid_email')}
|
||||
validAddressMessageDefault={props.inviteType === InviteType.MEMBER ? 'Invite **{email}** as a team member' : 'Invite **{email}** as a guest'}
|
||||
noMatchMessageId={noMatchMessageId}
|
||||
noMatchMessageDefault={noMatchMessageDefault}
|
||||
validAddressMessage={validAddressMessage}
|
||||
noMatchMessage={noMatchMessage}
|
||||
onInputChange={props.onUsersInputChange}
|
||||
inputValue={props.usersEmailsSearch}
|
||||
emailInvitationsEnabled={props.emailInvitationsEnabled}
|
||||
@@ -287,3 +277,26 @@ export default function InviteView(props: Props) {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
exceededMaxBatch: {
|
||||
id: 'invitation_modal.invite_members.exceeded_max_add_members_batch',
|
||||
defaultMessage: 'No more than **{text}** people can be invited at once',
|
||||
},
|
||||
noUserFound: {
|
||||
id: 'invitation_modal.members.users_emails_input.no_user_found_matching',
|
||||
defaultMessage: 'No one found matching **{text}**. Enter their email to invite them.',
|
||||
},
|
||||
noUserFoundEmailDisabled: {
|
||||
id: 'invitation_modal.members.users_emails_input.no_user_found_matching-email-disabled',
|
||||
defaultMessage: 'No one found matching **{text}**',
|
||||
},
|
||||
validAddressGuest: {
|
||||
id: 'invitation_modal.guests.users_emails_input.valid_email',
|
||||
defaultMessage: 'Invite **{email}** as a guest',
|
||||
},
|
||||
validAddressMember: {
|
||||
id: 'invitation_modal.members.users_emails_input.valid_email',
|
||||
defaultMessage: 'Invite **{email}** as a team member',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -73,7 +73,7 @@ describe('ResultTable', () => {
|
||||
test('emails render as email', () => {
|
||||
props.rows = [{
|
||||
email: 'aa@aa.aa',
|
||||
reason: 'some reason',
|
||||
reason: {id: 'some_reason', defaultMessage: 'some reason'},
|
||||
}];
|
||||
const wrapper = shallow(<ResultTable {...props}/>);
|
||||
expect(wrapper.find(EmailIcon).length).toBe(1);
|
||||
@@ -82,7 +82,7 @@ describe('ResultTable', () => {
|
||||
test('unsent invites render as unsent invites', () => {
|
||||
props.rows = [{
|
||||
text: '@incomplete_userna',
|
||||
reason: 'This was not a complete user',
|
||||
reason: {id: 'incomplete_user', defaultMessage: 'This was not a complete user'},
|
||||
}];
|
||||
const wrapper = shallow(<ResultTable {...props}/>);
|
||||
expect(wrapper.find(AlertIcon).length).toBe(1);
|
||||
@@ -91,7 +91,7 @@ describe('ResultTable', () => {
|
||||
test('user invites render as users', () => {
|
||||
props.rows = [{
|
||||
user: defaultUser,
|
||||
reason: 'added successfuly',
|
||||
reason: {id: 'success', defaultMessage: 'added successfully'},
|
||||
}];
|
||||
const wrapper = shallow(<ResultTable {...props}/>);
|
||||
expect(wrapper.find(Avatar).length).toBe(1);
|
||||
@@ -105,7 +105,7 @@ describe('ResultTable', () => {
|
||||
...defaultUser,
|
||||
is_bot: true,
|
||||
},
|
||||
reason: 'added successfuly',
|
||||
reason: {id: 'success', defaultMessage: 'added successfully'},
|
||||
}];
|
||||
const wrapper = shallow(<ResultTable {...props}/>);
|
||||
expect(wrapper.find(Avatar).length).toBe(1);
|
||||
@@ -119,7 +119,7 @@ describe('ResultTable', () => {
|
||||
...defaultUser,
|
||||
roles: 'system_guest',
|
||||
},
|
||||
reason: 'added successfuly',
|
||||
reason: {id: 'success', defaultMessage: 'added successfully'},
|
||||
}];
|
||||
const wrapper = shallow(<ResultTable {...props}/>);
|
||||
expect(wrapper.find(Avatar).length).toBe(1);
|
||||
|
||||
@@ -32,7 +32,7 @@ type InviteUser = {
|
||||
|
||||
type I18nLike = {
|
||||
id: string;
|
||||
message: string;
|
||||
defaultMessage: string;
|
||||
values?: Record<string, React.ReactNode>;
|
||||
}
|
||||
|
||||
@@ -69,12 +69,9 @@ export default function ResultTable(props: Props) {
|
||||
);
|
||||
}
|
||||
|
||||
function messageWithLink(reason: any, link: any) {
|
||||
function messageWithLink(reason: I18nLike, link: string) {
|
||||
return intl.formatMessage(
|
||||
{
|
||||
id: reason.id,
|
||||
defaultMessage: reason.message,
|
||||
},
|
||||
reason,
|
||||
{
|
||||
a: (chunks: React.ReactNode | React.ReactNodeArray) => (
|
||||
<a
|
||||
@@ -150,21 +147,19 @@ export default function ResultTable(props: Props) {
|
||||
username = text;
|
||||
}
|
||||
|
||||
let reason: React.ReactNode = invitation.reason;
|
||||
if (typeof invitation?.reason !== 'string' &&
|
||||
invitation.reason?.id &&
|
||||
invitation.reason?.message &&
|
||||
invitation.reason?.values
|
||||
) {
|
||||
let reason;
|
||||
if (typeof invitation.reason === 'string') {
|
||||
reason = invitation.reason;
|
||||
} else if (invitation.path) {
|
||||
reason = messageWithLink(invitation.reason, invitation.path);
|
||||
} else {
|
||||
reason = (
|
||||
<FormattedMessage
|
||||
id={invitation.reason.id}
|
||||
defaultMessage={invitation.reason.message}
|
||||
defaultMessage={invitation.reason.defaultMessage}
|
||||
values={invitation.reason.values}
|
||||
/>
|
||||
);
|
||||
} else if (invitation.path && invitation.reason) {
|
||||
reason = messageWithLink(invitation.reason, invitation.path);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,12 +3,10 @@
|
||||
|
||||
import React from 'react';
|
||||
import {Modal} from 'react-bootstrap';
|
||||
import {FormattedMessage, useIntl} from 'react-intl';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import deepFreeze from 'mattermost-redux/utils/deep_freeze';
|
||||
|
||||
import {t} from 'utils/i18n';
|
||||
|
||||
import {InviteType} from './invite_as';
|
||||
import ResultTable from './result_table';
|
||||
import type {InviteResult} from './result_table';
|
||||
@@ -40,7 +38,23 @@ type Props = {
|
||||
} & ResultState;
|
||||
|
||||
export default function ResultView(props: Props) {
|
||||
const {formatMessage} = useIntl();
|
||||
let inviteType;
|
||||
if (props.inviteType === InviteType.MEMBER) {
|
||||
inviteType = (
|
||||
<FormattedMessage
|
||||
id='invite_modal.invited_members'
|
||||
defaultMessage='Members'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
inviteType = (
|
||||
<FormattedMessage
|
||||
id='invite_modal.invited_guests'
|
||||
defaultMessage='Guests'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal.Header className={props.headerClass}>
|
||||
@@ -52,10 +66,7 @@ export default function ResultView(props: Props) {
|
||||
id='invite_modal.invited'
|
||||
defaultMessage='{inviteType} invited to {team_name}'
|
||||
values={{
|
||||
inviteType: formatMessage({
|
||||
id: props.inviteType === InviteType.MEMBER ? t('invite_modal.invited_members') : t('invite_modal.invited_guests'),
|
||||
defaultMessage: props.inviteType === InviteType.MEMBER ? 'Members' : 'Guests',
|
||||
}),
|
||||
inviteType,
|
||||
team_name: props.currentTeamName,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useState, useMemo, useEffect} from 'react';
|
||||
import {FormattedMessage, useIntl} from 'react-intl';
|
||||
import {FormattedMessage, defineMessages, useIntl} from 'react-intl';
|
||||
import {CSSTransition} from 'react-transition-group';
|
||||
|
||||
import type {UserProfile} from '@mattermost/types/users';
|
||||
@@ -10,7 +10,6 @@ import type {UserProfile} from '@mattermost/types/users';
|
||||
import UsersEmailsInput from 'components/widgets/inputs/users_emails_input';
|
||||
|
||||
import {Constants} from 'utils/constants';
|
||||
import {t} from 'utils/i18n';
|
||||
|
||||
import Description from './description';
|
||||
import InviteMembersLink from './invite_members_link';
|
||||
@@ -64,10 +63,7 @@ const InviteMembers = (props: Props) => {
|
||||
});
|
||||
const errorProperties = {
|
||||
showError: false,
|
||||
errorMessageId: t(
|
||||
'invitation_modal.invite_members.exceeded_max_add_members_batch',
|
||||
),
|
||||
errorMessageDefault: 'No more than **{text}** people can be invited at once',
|
||||
errorMessage: messages.exceededMaxBatch,
|
||||
errorMessageValues: {
|
||||
text: Constants.MAX_ADD_MEMBERS_BATCH.toString(),
|
||||
},
|
||||
@@ -115,8 +111,7 @@ const InviteMembers = (props: Props) => {
|
||||
inputValue={email}
|
||||
emailInvitationsEnabled={true}
|
||||
autoFocus={true}
|
||||
validAddressMessageId={t('invitation_modal.members.users_emails_input.valid_email')}
|
||||
validAddressMessageDefault={'Invite **{email}** as a team member'}
|
||||
validAddressMessage={messages.validAddress}
|
||||
suppressNoOptionsMessage={suppressNoOptionsMessage}
|
||||
/>
|
||||
);
|
||||
@@ -242,4 +237,15 @@ const InviteMembers = (props: Props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const messages = defineMessages({
|
||||
exceededMaxBatch: {
|
||||
id: 'invitation_modal.invite_members.exceeded_max_add_members_batch',
|
||||
defaultMessage: 'No more than **{text}** people can be invited at once',
|
||||
},
|
||||
validAddress: {
|
||||
id: 'invitation_modal.members.users_emails_input.valid_email',
|
||||
defaultMessage: 'Invite **{email}** as a team member',
|
||||
},
|
||||
});
|
||||
|
||||
export default InviteMembers;
|
||||
|
||||
@@ -25,8 +25,10 @@ describe('components/widgets/inputs/UsersEmailsInput', () => {
|
||||
last_name: 'user',
|
||||
} as UserProfile,
|
||||
]}
|
||||
errorMessageId='errorMessageId'
|
||||
errorMessageDefault='errorMessageDefault'
|
||||
errorMessage={{
|
||||
id: 'errorMessageId',
|
||||
defaultMessage: 'errorMessageDefault',
|
||||
}}
|
||||
onInputChange={jest.fn()}
|
||||
inputValue=''
|
||||
emailInvitationsEnabled={false}
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import type {RefObject} from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import type {MessageDescriptor} from 'react-intl';
|
||||
import {FormattedMessage, defineMessages} from 'react-intl';
|
||||
import {components} from 'react-select';
|
||||
import type {FormatOptionLabelMeta, InputActionMeta, InputProps, OptionsType, Styles, ValueType} from 'react-select';
|
||||
import AsyncCreatable from 'react-select/async-creatable';
|
||||
@@ -24,7 +25,6 @@ import BotTag from 'components/widgets/tag/bot_tag';
|
||||
import GuestTag from 'components/widgets/tag/guest_tag';
|
||||
import Avatar from 'components/widgets/users/avatar';
|
||||
|
||||
import {t} from 'utils/i18n';
|
||||
import {getDisplayName, getLongDisplayNameParts, imageURLForUser} from 'utils/utils';
|
||||
|
||||
import './users_emails_input.scss';
|
||||
@@ -37,18 +37,14 @@ type Props = {
|
||||
onBlur?: () => void;
|
||||
onChange: (change: Array<UserProfile | string>) => void;
|
||||
showError?: boolean;
|
||||
errorMessageId: string;
|
||||
errorMessageDefault: string;
|
||||
errorMessage?: MessageDescriptor;
|
||||
errorMessageValues?: Record<string, React.ReactNode>;
|
||||
value: Array<UserProfile | string>;
|
||||
onInputChange: (change: string) => void;
|
||||
inputValue: string;
|
||||
noMatchMessageId?: string;
|
||||
noMatchMessageDefault?: string;
|
||||
validAddressMessageId?: string;
|
||||
validAddressMessageDefault?: string;
|
||||
loadingMessageId?: string;
|
||||
loadingMessageDefault?: string;
|
||||
noMatchMessage?: MessageDescriptor;
|
||||
validAddressMessage?: MessageDescriptor;
|
||||
loadingMessage?: MessageDescriptor;
|
||||
emailInvitationsEnabled: boolean;
|
||||
extraErrorText?: React.ReactNode;
|
||||
autoFocus?: boolean;
|
||||
@@ -67,14 +63,26 @@ type State = {
|
||||
|
||||
const multipleValuesDelimiter = /[\s,;]+/;
|
||||
|
||||
const messages = defineMessages({
|
||||
loadingDefault: {
|
||||
id: 'widgets.users_emails_input.loading',
|
||||
defaultMessage: 'Loading',
|
||||
},
|
||||
noMatchDefault: {
|
||||
id: 'widgets.users_emails_input.no_user_found_matching',
|
||||
defaultMessage: 'No one found matching **{text}**. Enter their email to invite them.',
|
||||
},
|
||||
validAddressDefault: {
|
||||
id: 'widgets.users_emails_input.valid_email',
|
||||
defaultMessage: 'Add **{email}**',
|
||||
},
|
||||
});
|
||||
|
||||
export default class UsersEmailsInput extends React.PureComponent<Props, State> {
|
||||
static defaultProps = {
|
||||
noMatchMessageId: t('widgets.users_emails_input.no_user_found_matching'),
|
||||
noMatchMessageDefault: 'No one found matching **{text}**. Enter their email to invite them.',
|
||||
validAddressMessageId: t('widgets.users_emails_input.valid_email'),
|
||||
validAddressMessageDefault: 'Add **{email}**',
|
||||
loadingMessageId: t('widgets.users_emails_input.loading'),
|
||||
loadingMessageDefault: 'Loading',
|
||||
noMatchMessage: messages.noMatchDefault,
|
||||
validAddress: messages.validAddressDefault,
|
||||
loadingMessage: messages.loadingDefault,
|
||||
showError: false,
|
||||
};
|
||||
private selectRef: RefObject<AsyncCreatable<UserProfile | EmailInvite> & { handleInputChange: (newValue: string, actionMeta: InputActionMeta | { action: 'custom' }) => string }>;
|
||||
@@ -110,8 +118,7 @@ export default class UsersEmailsInput extends React.PureComponent<Props, State>
|
||||
loadingMessage = (): string => {
|
||||
const text = (
|
||||
<FormattedMessage
|
||||
id={this.props.loadingMessageId}
|
||||
defaultMessage={this.props.loadingMessageDefault}
|
||||
{...this.props.loadingMessage}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -200,8 +207,7 @@ export default class UsersEmailsInput extends React.PureComponent<Props, State>
|
||||
<MailPlusIcon className='mail-plus-icon'/>
|
||||
<FormattedMarkdownMessage
|
||||
key='widgets.users_emails_input.valid_email'
|
||||
id={this.props.validAddressMessageId}
|
||||
defaultMessage={this.props.validAddressMessageDefault}
|
||||
{...this.props.validAddressMessage}
|
||||
values={{email: value}}
|
||||
disableLinks={true}
|
||||
/>
|
||||
@@ -242,8 +248,7 @@ export default class UsersEmailsInput extends React.PureComponent<Props, State>
|
||||
<div className='users-emails-input__option users-emails-input__option--no-matches'>
|
||||
<Msg {...props}>
|
||||
<FormattedMarkdownMessage
|
||||
id={this.props.noMatchMessageId}
|
||||
defaultMessage={this.props.noMatchMessageDefault}
|
||||
{...this.props.noMatchMessage}
|
||||
values={{text: inputValue}}
|
||||
disableLinks={true}
|
||||
/>
|
||||
@@ -518,8 +523,7 @@ export default class UsersEmailsInput extends React.PureComponent<Props, State>
|
||||
<div className='InputErrorBox'>
|
||||
<Msg>
|
||||
<FormattedMarkdownMessage
|
||||
id={this.props.errorMessageId}
|
||||
defaultMessage={this.props.errorMessageDefault}
|
||||
{...this.props.errorMessage}
|
||||
values={this.props.errorMessageValues}
|
||||
disableLinks={true}
|
||||
/>
|
||||
|
||||
Ссылка в новой задаче
Block a user