MM-42835_Invite People - add email+user invites

This commit is contained in:
Julian Mondragon
2023-01-19 17:18:51 -05:00
parent a8da27d5e9
commit 29a66bbc19
7 changed files with 57 additions and 96 deletions

View File

@@ -508,19 +508,13 @@ export async function getTeamMembersByIds(serverUrl: string, teamId: string, use
if (!fetchOnly) {
setTeamLoading(serverUrl, true);
const teamMemberships: TeamMembership[] = [];
const roles: Record<string, boolean> = {};
const roles = [];
for (const member of members) {
teamMemberships.push(member);
member.roles.split(' ').forEach((role) => {
if (!roles[role]) {
roles[role] = true;
}
});
for (const {roles: memberRoles} of members) {
roles.push(...memberRoles.split(' '));
}
fetchRolesIfNeeded(serverUrl, Object.getOwnPropertyNames(roles));
fetchRolesIfNeeded(serverUrl, Array.from(new Set(roles)));
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([
operator.handleTeam({teams: [team], prepareRecordsOnly: true}),
operator.handleTeamMemberships({teamMemberships, prepareRecordsOnly: true}),
operator.handleTeamMemberships({teamMemberships: members, prepareRecordsOnly: true}),
])).flat();
await operator.batchRecords(models);

View File

@@ -115,6 +115,7 @@ export default function Invite({
const modalPosition = useModalPosition(mainView);
const searchTimeoutId = useRef<NodeJS.Timeout | null>(null);
const retryTimeoutId = useRef<NodeJS.Timeout | null>(null);
const [term, setTerm] = useState('');
const [searchResults, setSearchResults] = useState<SearchResult[]>([]);
@@ -137,7 +138,7 @@ export default function Invite({
return;
}
const {data} = await searchProfiles(serverUrl, searchTerm.toLowerCase(), {allow_inactive: true});
const {data} = await searchProfiles(serverUrl, searchTerm.toLowerCase());
const results: SearchResult[] = data ?? [];
if (!results.length && isEmail(searchTerm.trim())) {
@@ -193,7 +194,7 @@ export default function Invite({
setSendError('');
setStage(Stage.LOADING);
setTimeout(() => {
retryTimeoutId.current = setTimeout(() => {
handleSend();
}, TIMEOUT_MILLISECONDS);
};
@@ -243,9 +244,9 @@ export default function Invite({
for (const userId of userIds) {
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]) {
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 {
usersToAdd.push(userId);
}
@@ -338,6 +339,18 @@ export default function Invite({
});
}, [componentId, locale, theme, stage]);
useEffect(() => {
return () => {
if (searchTimeoutId.current) {
clearTimeout(searchTimeoutId.current);
}
if (retryTimeoutId.current) {
clearTimeout(retryTimeoutId.current);
}
};
}, []);
const handleRemoveItem = useCallback((id: string) => {
const newSelectedIds = Object.assign({}, selectedIds);

View File

@@ -23,7 +23,6 @@ import {useTheme} from '@context/theme';
import {useAutocompleteDefaultAnimatedValues} from '@hooks/autocomplete';
import {useIsTablet, useKeyboardHeight} from '@hooks/device';
import {makeStyleSheetFromTheme, changeOpacity} from '@utils/theme';
import {typography} from '@utils/typography';
import {SearchResult} from './invite';
import SelectedEmail from './selected_email';
@@ -56,54 +55,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
display: 'flex',
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: {
left: 20,
right: 20,

View File

@@ -262,18 +262,22 @@ export default function Summary({
</Text>
) : (
<>
<SummaryReport
type={SummaryReportType.NOT_SENT}
invites={notSent}
selectedIds={selectedIds}
testID='invite.summary_report'
/>
<SummaryReport
type={SummaryReportType.SENT}
invites={sent}
selectedIds={selectedIds}
testID='invite.summary_report'
/>
{notSent.length > 0 && (
<SummaryReport
type={SummaryReportType.NOT_SENT}
invites={notSent}
selectedIds={selectedIds}
testID='invite.summary_report'
/>
)}
{sent.length > 0 && (
<SummaryReport
type={SummaryReportType.SENT}
invites={sent}
selectedIds={selectedIds}
testID='invite.summary_report'
/>
)}
</>
)}
</View>

View File

@@ -14,9 +14,12 @@ import {typography} from '@utils/typography';
import {SearchResult, InviteResult} from './invite';
import TextItem, {TextItemType} from './text_item';
const COLOR_SUCCESS = '#3db887';
const COLOR_ERROR = '#d24b4e';
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
return {
summaryInvitationsContainer: {
container: {
display: 'flex',
flexDirection: 'column',
borderWidth: 1,
@@ -25,29 +28,29 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
marginBottom: 16,
paddingVertical: 8,
},
summaryInvitationsTitle: {
title: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 20,
paddingVertical: 12,
},
summaryInvitationsTitleText: {
titleText: {
marginLeft: 12,
...typography('Heading', 300, 'SemiBold'),
color: theme.centerChannelColor,
},
summaryInvitationsItem: {
item: {
display: 'flex',
flexDirection: 'column',
paddingVertical: 12,
},
summaryInvitationsUser: {
user: {
paddingTop: 0,
paddingBottom: 0,
height: 'auto',
},
summaryInvitationsReason: {
reason: {
paddingLeft: 56,
paddingRight: 20,
...typography('Body', 75, 'Regular'),
@@ -80,10 +83,6 @@ export default function SummaryReport({
const count = invites.length;
if (!count) {
return null;
}
const sent = type === SummaryReportType.SENT;
const message = sent ? (
formatMessage(
@@ -105,16 +104,16 @@ export default function SummaryReport({
return (
<View
style={styles.summaryInvitationsContainer}
style={styles.container}
testID={`${testID}.${type}`}
>
<View style={styles.summaryInvitationsTitle}>
<View style={styles.title}>
<CompassIcon
name={sent ? 'check-circle' : 'close-circle'}
size={24}
style={{color: sent ? '#3db887' : '#d24b4e'}}
style={{color: sent ? COLOR_SUCCESS : COLOR_ERROR}}
/>
<Text style={styles.summaryInvitationsTitleText}>
<Text style={styles.titleText}>
{message}
</Text>
</View>
@@ -124,7 +123,7 @@ export default function SummaryReport({
return (
<View
key={userId}
style={styles.summaryInvitationsItem}
style={styles.item}
>
{typeof item === 'string' ? (
<TextItem
@@ -135,11 +134,11 @@ export default function SummaryReport({
) : (
<UserItem
user={item}
containerStyle={styles.summaryInvitationsUser}
containerStyle={styles.user}
testID={`${testID}.user_item`}
/>
)}
<Text style={styles.summaryInvitationsReason}>
<Text style={styles.reason}>
{reason}
</Text>
</View>

View File

@@ -703,7 +703,7 @@ export function setButtons(componentId: string, buttons: NavButtons = {leftButto
mergeNavigationOptions(componentId, options);
}
export function showOverlay(name: string, passProps = {}, options = {}) {
export function showOverlay(name: string, passProps = {}, options: Options = {}) {
if (!isScreenRegistered(name)) {
return;
}

View File

@@ -327,8 +327,8 @@
"intro.welcome.public": "Add some more team members to the channel or start a conversation below.",
"invite_people_to_team.message": "Heres a link to collaborate and communicate with us on Mattermost.",
"invite_people_to_team.title": "Join the {team} team",
"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.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.search.email_invite": "invite",
"invite.search.no_results": "No one found matching",
"invite.searchPlaceholder": "Type a name or email address…",