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