forked from Ivasoft/mattermost-mobile
MM-42835_Invite People - add email+user invites
This commit is contained in:
@@ -508,19 +508,13 @@ export async function getTeamMembersByIds(serverUrl: string, teamId: string, use
|
|||||||
if (!fetchOnly) {
|
if (!fetchOnly) {
|
||||||
setTeamLoading(serverUrl, true);
|
setTeamLoading(serverUrl, true);
|
||||||
|
|
||||||
const teamMemberships: TeamMembership[] = [];
|
const roles = [];
|
||||||
const roles: Record<string, boolean> = {};
|
|
||||||
|
|
||||||
for (const member of members) {
|
for (const {roles: memberRoles} of members) {
|
||||||
teamMemberships.push(member);
|
roles.push(...memberRoles.split(' '));
|
||||||
member.roles.split(' ').forEach((role) => {
|
|
||||||
if (!roles[role]) {
|
|
||||||
roles[role] = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchRolesIfNeeded(serverUrl, Object.getOwnPropertyNames(roles));
|
fetchRolesIfNeeded(serverUrl, Array.from(new Set(roles)));
|
||||||
|
|
||||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||||
|
|
||||||
@@ -529,7 +523,7 @@ export async function getTeamMembersByIds(serverUrl: string, teamId: string, use
|
|||||||
|
|
||||||
const models: Model[] = (await Promise.all([
|
const models: Model[] = (await Promise.all([
|
||||||
operator.handleTeam({teams: [team], prepareRecordsOnly: true}),
|
operator.handleTeam({teams: [team], prepareRecordsOnly: true}),
|
||||||
operator.handleTeamMemberships({teamMemberships, prepareRecordsOnly: true}),
|
operator.handleTeamMemberships({teamMemberships: members, prepareRecordsOnly: true}),
|
||||||
])).flat();
|
])).flat();
|
||||||
|
|
||||||
await operator.batchRecords(models);
|
await operator.batchRecords(models);
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ export default function Invite({
|
|||||||
const modalPosition = useModalPosition(mainView);
|
const modalPosition = useModalPosition(mainView);
|
||||||
|
|
||||||
const searchTimeoutId = useRef<NodeJS.Timeout | null>(null);
|
const searchTimeoutId = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
const retryTimeoutId = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
const [term, setTerm] = useState('');
|
const [term, setTerm] = useState('');
|
||||||
const [searchResults, setSearchResults] = useState<SearchResult[]>([]);
|
const [searchResults, setSearchResults] = useState<SearchResult[]>([]);
|
||||||
@@ -137,7 +138,7 @@ export default function Invite({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {data} = await searchProfiles(serverUrl, searchTerm.toLowerCase(), {allow_inactive: true});
|
const {data} = await searchProfiles(serverUrl, searchTerm.toLowerCase());
|
||||||
const results: SearchResult[] = data ?? [];
|
const results: SearchResult[] = data ?? [];
|
||||||
|
|
||||||
if (!results.length && isEmail(searchTerm.trim())) {
|
if (!results.length && isEmail(searchTerm.trim())) {
|
||||||
@@ -193,7 +194,7 @@ export default function Invite({
|
|||||||
setSendError('');
|
setSendError('');
|
||||||
setStage(Stage.LOADING);
|
setStage(Stage.LOADING);
|
||||||
|
|
||||||
setTimeout(() => {
|
retryTimeoutId.current = setTimeout(() => {
|
||||||
handleSend();
|
handleSend();
|
||||||
}, TIMEOUT_MILLISECONDS);
|
}, TIMEOUT_MILLISECONDS);
|
||||||
};
|
};
|
||||||
@@ -243,9 +244,9 @@ export default function Invite({
|
|||||||
|
|
||||||
for (const userId of userIds) {
|
for (const userId of userIds) {
|
||||||
if (isGuest((selectedIds[userId] as UserProfile).roles)) {
|
if (isGuest((selectedIds[userId] as UserProfile).roles)) {
|
||||||
notSent.push({userId, reason: formatMessage({id: 'invite.members.user-is-guest', defaultMessage: 'Contact your admin to make this guest a full member'})});
|
notSent.push({userId, reason: formatMessage({id: 'invite.members.user_is_guest', defaultMessage: 'Contact your admin to make this guest a full member'})});
|
||||||
} else if (currentMemberIds[userId]) {
|
} else if (currentMemberIds[userId]) {
|
||||||
notSent.push({userId, reason: formatMessage({id: 'invite.members.already-member', defaultMessage: 'This person is already a team member'})});
|
notSent.push({userId, reason: formatMessage({id: 'invite.members.already_member', defaultMessage: 'This person is already a team member'})});
|
||||||
} else {
|
} else {
|
||||||
usersToAdd.push(userId);
|
usersToAdd.push(userId);
|
||||||
}
|
}
|
||||||
@@ -338,6 +339,18 @@ export default function Invite({
|
|||||||
});
|
});
|
||||||
}, [componentId, locale, theme, stage]);
|
}, [componentId, locale, theme, stage]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (searchTimeoutId.current) {
|
||||||
|
clearTimeout(searchTimeoutId.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retryTimeoutId.current) {
|
||||||
|
clearTimeout(retryTimeoutId.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleRemoveItem = useCallback((id: string) => {
|
const handleRemoveItem = useCallback((id: string) => {
|
||||||
const newSelectedIds = Object.assign({}, selectedIds);
|
const newSelectedIds = Object.assign({}, selectedIds);
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import {useTheme} from '@context/theme';
|
|||||||
import {useAutocompleteDefaultAnimatedValues} from '@hooks/autocomplete';
|
import {useAutocompleteDefaultAnimatedValues} from '@hooks/autocomplete';
|
||||||
import {useIsTablet, useKeyboardHeight} from '@hooks/device';
|
import {useIsTablet, useKeyboardHeight} from '@hooks/device';
|
||||||
import {makeStyleSheetFromTheme, changeOpacity} from '@utils/theme';
|
import {makeStyleSheetFromTheme, changeOpacity} from '@utils/theme';
|
||||||
import {typography} from '@utils/typography';
|
|
||||||
|
|
||||||
import {SearchResult} from './invite';
|
import {SearchResult} from './invite';
|
||||||
import SelectedEmail from './selected_email';
|
import SelectedEmail from './selected_email';
|
||||||
@@ -56,54 +55,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
teamContainer: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
width: '100%',
|
|
||||||
paddingVertical: 16,
|
|
||||||
paddingHorizontal: 20,
|
|
||||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.04),
|
|
||||||
},
|
|
||||||
iconContainer: {
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
},
|
|
||||||
textContainer: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
},
|
|
||||||
teamText: {
|
|
||||||
color: theme.centerChannelColor,
|
|
||||||
marginLeft: 12,
|
|
||||||
...typography('Body', 200, 'SemiBold'),
|
|
||||||
},
|
|
||||||
serverText: {
|
|
||||||
color: changeOpacity(theme.centerChannelColor, 0.72),
|
|
||||||
marginLeft: 12,
|
|
||||||
...typography('Body', 75, 'Regular'),
|
|
||||||
},
|
|
||||||
shareLink: {
|
|
||||||
display: 'flex',
|
|
||||||
marginLeft: 'auto',
|
|
||||||
},
|
|
||||||
shareLinkButton: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
height: 40,
|
|
||||||
paddingHorizontal: 20,
|
|
||||||
backgroundColor: changeOpacity(theme.buttonBg, 0.08),
|
|
||||||
borderRadius: 4,
|
|
||||||
},
|
|
||||||
shareLinkText: {
|
|
||||||
color: theme.buttonBg,
|
|
||||||
...typography('Body', 100, 'SemiBold'),
|
|
||||||
paddingLeft: 7,
|
|
||||||
},
|
|
||||||
shareLinkIcon: {
|
|
||||||
color: theme.buttonBg,
|
|
||||||
},
|
|
||||||
searchList: {
|
searchList: {
|
||||||
left: 20,
|
left: 20,
|
||||||
right: 20,
|
right: 20,
|
||||||
|
|||||||
@@ -262,18 +262,22 @@ export default function Summary({
|
|||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<SummaryReport
|
{notSent.length > 0 && (
|
||||||
type={SummaryReportType.NOT_SENT}
|
<SummaryReport
|
||||||
invites={notSent}
|
type={SummaryReportType.NOT_SENT}
|
||||||
selectedIds={selectedIds}
|
invites={notSent}
|
||||||
testID='invite.summary_report'
|
selectedIds={selectedIds}
|
||||||
/>
|
testID='invite.summary_report'
|
||||||
<SummaryReport
|
/>
|
||||||
type={SummaryReportType.SENT}
|
)}
|
||||||
invites={sent}
|
{sent.length > 0 && (
|
||||||
selectedIds={selectedIds}
|
<SummaryReport
|
||||||
testID='invite.summary_report'
|
type={SummaryReportType.SENT}
|
||||||
/>
|
invites={sent}
|
||||||
|
selectedIds={selectedIds}
|
||||||
|
testID='invite.summary_report'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -14,9 +14,12 @@ import {typography} from '@utils/typography';
|
|||||||
import {SearchResult, InviteResult} from './invite';
|
import {SearchResult, InviteResult} from './invite';
|
||||||
import TextItem, {TextItemType} from './text_item';
|
import TextItem, {TextItemType} from './text_item';
|
||||||
|
|
||||||
|
const COLOR_SUCCESS = '#3db887';
|
||||||
|
const COLOR_ERROR = '#d24b4e';
|
||||||
|
|
||||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
||||||
return {
|
return {
|
||||||
summaryInvitationsContainer: {
|
container: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
@@ -25,29 +28,29 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
|||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
paddingVertical: 8,
|
paddingVertical: 8,
|
||||||
},
|
},
|
||||||
summaryInvitationsTitle: {
|
title: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
paddingHorizontal: 20,
|
paddingHorizontal: 20,
|
||||||
paddingVertical: 12,
|
paddingVertical: 12,
|
||||||
},
|
},
|
||||||
summaryInvitationsTitleText: {
|
titleText: {
|
||||||
marginLeft: 12,
|
marginLeft: 12,
|
||||||
...typography('Heading', 300, 'SemiBold'),
|
...typography('Heading', 300, 'SemiBold'),
|
||||||
color: theme.centerChannelColor,
|
color: theme.centerChannelColor,
|
||||||
},
|
},
|
||||||
summaryInvitationsItem: {
|
item: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
paddingVertical: 12,
|
paddingVertical: 12,
|
||||||
},
|
},
|
||||||
summaryInvitationsUser: {
|
user: {
|
||||||
paddingTop: 0,
|
paddingTop: 0,
|
||||||
paddingBottom: 0,
|
paddingBottom: 0,
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
},
|
},
|
||||||
summaryInvitationsReason: {
|
reason: {
|
||||||
paddingLeft: 56,
|
paddingLeft: 56,
|
||||||
paddingRight: 20,
|
paddingRight: 20,
|
||||||
...typography('Body', 75, 'Regular'),
|
...typography('Body', 75, 'Regular'),
|
||||||
@@ -80,10 +83,6 @@ export default function SummaryReport({
|
|||||||
|
|
||||||
const count = invites.length;
|
const count = invites.length;
|
||||||
|
|
||||||
if (!count) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sent = type === SummaryReportType.SENT;
|
const sent = type === SummaryReportType.SENT;
|
||||||
const message = sent ? (
|
const message = sent ? (
|
||||||
formatMessage(
|
formatMessage(
|
||||||
@@ -105,16 +104,16 @@ export default function SummaryReport({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={styles.summaryInvitationsContainer}
|
style={styles.container}
|
||||||
testID={`${testID}.${type}`}
|
testID={`${testID}.${type}`}
|
||||||
>
|
>
|
||||||
<View style={styles.summaryInvitationsTitle}>
|
<View style={styles.title}>
|
||||||
<CompassIcon
|
<CompassIcon
|
||||||
name={sent ? 'check-circle' : 'close-circle'}
|
name={sent ? 'check-circle' : 'close-circle'}
|
||||||
size={24}
|
size={24}
|
||||||
style={{color: sent ? '#3db887' : '#d24b4e'}}
|
style={{color: sent ? COLOR_SUCCESS : COLOR_ERROR}}
|
||||||
/>
|
/>
|
||||||
<Text style={styles.summaryInvitationsTitleText}>
|
<Text style={styles.titleText}>
|
||||||
{message}
|
{message}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -124,7 +123,7 @@ export default function SummaryReport({
|
|||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
key={userId}
|
key={userId}
|
||||||
style={styles.summaryInvitationsItem}
|
style={styles.item}
|
||||||
>
|
>
|
||||||
{typeof item === 'string' ? (
|
{typeof item === 'string' ? (
|
||||||
<TextItem
|
<TextItem
|
||||||
@@ -135,11 +134,11 @@ export default function SummaryReport({
|
|||||||
) : (
|
) : (
|
||||||
<UserItem
|
<UserItem
|
||||||
user={item}
|
user={item}
|
||||||
containerStyle={styles.summaryInvitationsUser}
|
containerStyle={styles.user}
|
||||||
testID={`${testID}.user_item`}
|
testID={`${testID}.user_item`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Text style={styles.summaryInvitationsReason}>
|
<Text style={styles.reason}>
|
||||||
{reason}
|
{reason}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -703,7 +703,7 @@ export function setButtons(componentId: string, buttons: NavButtons = {leftButto
|
|||||||
mergeNavigationOptions(componentId, options);
|
mergeNavigationOptions(componentId, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showOverlay(name: string, passProps = {}, options = {}) {
|
export function showOverlay(name: string, passProps = {}, options: Options = {}) {
|
||||||
if (!isScreenRegistered(name)) {
|
if (!isScreenRegistered(name)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -327,8 +327,8 @@
|
|||||||
"intro.welcome.public": "Add some more team members to the channel or start a conversation below.",
|
"intro.welcome.public": "Add some more team members to the channel or start a conversation below.",
|
||||||
"invite_people_to_team.message": "Here’s a link to collaborate and communicate with us on Mattermost.",
|
"invite_people_to_team.message": "Here’s a link to collaborate and communicate with us on Mattermost.",
|
||||||
"invite_people_to_team.title": "Join the {team} team",
|
"invite_people_to_team.title": "Join the {team} team",
|
||||||
"invite.members.already-member": "This person is already a team member",
|
"invite.members.already_member": "This person is already a team member",
|
||||||
"invite.members.user-is-guest": "Contact your admin to make this guest a full member",
|
"invite.members.user_is_guest": "Contact your admin to make this guest a full member",
|
||||||
"invite.search.email_invite": "invite",
|
"invite.search.email_invite": "invite",
|
||||||
"invite.search.no_results": "No one found matching",
|
"invite.search.no_results": "No one found matching",
|
||||||
"invite.searchPlaceholder": "Type a name or email address…",
|
"invite.searchPlaceholder": "Type a name or email address…",
|
||||||
|
|||||||
Reference in New Issue
Block a user