fix: bunch of changes to match acceptance criteria

pull/3052/head
Audric Ackermann 6 months ago
parent 038e08ed3d
commit c30fcfd013
No known key found for this signature in database

@ -8,11 +8,14 @@ import { UserUtils } from '../session/utils';
import { GroupInvite } from '../session/utils/job_runners/jobs/GroupInviteJob'; import { GroupInvite } from '../session/utils/job_runners/jobs/GroupInviteJob';
import { hasClosedGroupV2QAButtons } from '../shared/env_vars'; import { hasClosedGroupV2QAButtons } from '../shared/env_vars';
import { import {
useMemberHasAcceptedInvite,
useMemberInviteFailed, useMemberInviteFailed,
useMemberInviteSending, useMemberInviteSending,
useMemberInviteSent, useMemberInviteSent,
useMemberIsPromoted,
useMemberPromoteSending, useMemberPromoteSending,
useMemberPromotionFailed, useMemberPromotionFailed,
useMemberPromotionNotSent,
useMemberPromotionSent, useMemberPromotionSent,
} from '../state/selectors/groups'; } from '../state/selectors/groups';
import { Avatar, AvatarSize, CrownIcon } from './avatar/Avatar'; import { Avatar, AvatarSize, CrownIcon } from './avatar/Avatar';
@ -118,6 +121,7 @@ type MemberListItemProps = {
displayGroupStatus?: boolean; displayGroupStatus?: boolean;
groupPk?: string; groupPk?: string;
disabled?: boolean; disabled?: boolean;
hideRadioButton?: boolean;
}; };
const ResendContainer = ({ const ResendContainer = ({
@ -139,8 +143,8 @@ const ResendContainer = ({
padding="0 var(--margins-lg)" padding="0 var(--margins-lg)"
gap="var(--margins-sm)" gap="var(--margins-sm)"
> >
<ResendInviteButton groupPk={groupPk} pubkey={pubkey} /> <ResendButton groupPk={groupPk} pubkey={pubkey} />
<ResendPromoteButton groupPk={groupPk} pubkey={pubkey} /> <PromoteButton groupPk={groupPk} pubkey={pubkey} />
</Flex> </Flex>
); );
} }
@ -215,15 +219,24 @@ const GroupStatusContainer = ({
return null; return null;
}; };
const ResendInviteButton = ({ const ResendButton = ({ groupPk, pubkey }: { pubkey: PubkeyType; groupPk: GroupPubkeyType }) => {
groupPk, const acceptedInvite = useMemberHasAcceptedInvite(pubkey, groupPk);
pubkey, const promotionFailed = useMemberPromotionFailed(pubkey, groupPk);
}: { const promotionSent = useMemberPromotionSent(pubkey, groupPk);
pubkey: PubkeyType; const promotionNotSent = useMemberPromotionNotSent(pubkey, groupPk);
groupPk: GroupPubkeyType; const promoted = useMemberIsPromoted(pubkey, groupPk);
}) => {
const inviteFailed = useMemberInviteFailed(pubkey, groupPk); // as soon as the `admin` flag is set in the group for that member, we should be able to resend a promote as we cannot remove an admin.
if (!inviteFailed) { const canResendPromotion =
hasClosedGroupV2QAButtons() &&
(promotionFailed || promotionSent || promotionNotSent || promoted);
// we can always remove/and readd a non-admin member. So we consider that a member who accepted the invite cannot be resent an invite.
const canResendInvite = !acceptedInvite;
const shouldShowResendButton = canResendInvite || canResendPromotion;
if (!shouldShowResendButton) {
return null; return null;
} }
return ( return (
@ -239,9 +252,10 @@ const ResendInviteButton = ({
window.log.warn('tried to resend invite but we do not have correct details'); window.log.warn('tried to resend invite but we do not have correct details');
return; return;
} }
const token = await MetaGroupWrapperActions.swarmSubAccountToken(groupPk, pubkey);
const unrevokeSubRequest = new SubaccountUnrevokeSubRequest({ const unrevokeSubRequest = new SubaccountUnrevokeSubRequest({
groupPk, groupPk,
revokeTokenHex: [pubkey], revokeTokenHex: [token],
timestamp: NetworkTime.now(), timestamp: NetworkTime.now(),
secretKey: group.secretKey, secretKey: group.secretKey,
}); });
@ -256,7 +270,10 @@ const ResendInviteButton = ({
// if we tried to invite that member as admin right away, let's retry it as such. // if we tried to invite that member as admin right away, let's retry it as such.
const inviteAsAdmin = const inviteAsAdmin =
member.promotionNotSent || member.promotionFailed || member.promotionPending; member.promotionNotSent ||
member.promotionFailed ||
member.promotionPending ||
member.promoted;
await GroupInvite.addJob({ await GroupInvite.addJob({
groupPk, groupPk,
member: pubkey, member: pubkey,
@ -267,14 +284,13 @@ const ResendInviteButton = ({
); );
}; };
const ResendPromoteButton = ({ const PromoteButton = ({ groupPk, pubkey }: { pubkey: PubkeyType; groupPk: GroupPubkeyType }) => {
groupPk, const memberAcceptedInvite = useMemberHasAcceptedInvite(pubkey, groupPk);
pubkey, const memberIsPromoted = useMemberIsPromoted(pubkey, groupPk);
}: { // When invite-as-admin was used to invite that member, the resend button is available to resend the promote message.
pubkey: PubkeyType; // We want to show that button only to promote a normal member who accepted a normal invite but wasn't promoted yet.
groupPk: GroupPubkeyType; // ^ this is only the case for testing. The UI will be different once we release the promotion process
}) => { if (!hasClosedGroupV2QAButtons() || !memberAcceptedInvite || memberIsPromoted) {
if (!hasClosedGroupV2QAButtons()) {
return null; return null;
} }
return ( return (
@ -309,8 +325,11 @@ export const MemberListItem = ({
disabled, disabled,
withBorder, withBorder,
maxNameWidth, maxNameWidth,
hideRadioButton,
}: MemberListItemProps) => { }: MemberListItemProps) => {
const memberName = useNicknameOrProfileNameOrShortenedPubkey(pubkey); const memberName = useNicknameOrProfileNameOrShortenedPubkey(pubkey);
const isUs = UserUtils.isUsFromCache(pubkey);
const ourName = isUs ? window.i18n('you') : null;
return ( return (
<StyledSessionMemberItem <StyledSessionMemberItem
@ -335,7 +354,7 @@ export const MemberListItem = ({
alignItems="flex-start" alignItems="flex-start"
> >
<StyledName data-testid={'group-member-name'} maxName={maxNameWidth}> <StyledName data-testid={'group-member-name'} maxName={maxNameWidth}>
{memberName} {ourName || memberName}
</StyledName> </StyledName>
<GroupStatusContainer <GroupStatusContainer
pubkey={pubkey} pubkey={pubkey}
@ -347,7 +366,7 @@ export const MemberListItem = ({
<ResendContainer pubkey={pubkey} displayGroupStatus={displayGroupStatus} groupPk={groupPk} /> <ResendContainer pubkey={pubkey} displayGroupStatus={displayGroupStatus} groupPk={groupPk} />
{!inMentions && ( {!inMentions && !hideRadioButton && (
<StyledCheckContainer> <StyledCheckContainer>
<SessionRadio active={isSelected} value={pubkey} inputName={pubkey} label="" /> <SessionRadio active={isSelected} value={pubkey} inputName={pubkey} label="" />
</StyledCheckContainer> </StyledCheckContainer>

@ -76,6 +76,7 @@ const ClassicMemberList = (props: {
onSelect={onSelect} onSelect={onSelect}
onUnselect={onUnselect} onUnselect={onUnselect}
isAdmin={isAdmin} isAdmin={isAdmin}
hideRadioButton={isAdmin} // we want to hide the toggle for admins are they are not selectable
disableBg={true} disableBg={true}
displayGroupStatus={isV2Group && weAreAdmin} displayGroupStatus={isV2Group && weAreAdmin}
groupPk={convoId} groupPk={convoId}
@ -196,14 +197,9 @@ export const UpdateGroupMembersDialog = (props: Props) => {
} }
if (groupAdmins?.includes(member)) { if (groupAdmins?.includes(member)) {
if (PubKey.is03Pubkey(conversationId)) { ToastUtils.pushCannotRemoveGroupAdmin();
window?.log?.warn(`User ${member} cannot be selected as they are an admin.`); window?.log?.warn(`User ${member} cannot be selected as they are an admin.`);
return;
}
ToastUtils.pushCannotRemoveCreatorFromGroup();
window?.log?.warn(
`User ${member} cannot be selected as they are the creator of the closed group.`
);
return; return;
} }

@ -547,8 +547,7 @@ export async function deleteMessagesById(messageIds: Array<string>, conversation
window.inboxStore?.dispatch( window.inboxStore?.dispatch(
updateConfirmModal({ updateConfirmModal({
title: window.i18n('clearMessagesForMe'), title: window.i18n('deleteMessage', { count: selectedMessages.length }),
i18nMessage: { token: 'deleteMessage', args: { count: selectedMessages.length } },
radioOptions: !isMe radioOptions: !isMe
? [ ? [
{ {

@ -60,7 +60,6 @@ export function pushLoadAttachmentFailure(message?: string) {
// TODOLATER pushToast functions should take I18nArgs and then run strip in the function itself. // TODOLATER pushToast functions should take I18nArgs and then run strip in the function itself.
export function pushFileSizeErrorAsByte() { export function pushFileSizeErrorAsByte() {
pushToastError('fileSizeWarning', window.i18n.stripped('attachmentsErrorSize')); pushToastError('fileSizeWarning', window.i18n.stripped('attachmentsErrorSize'));
} }
@ -114,7 +113,6 @@ export function pushMessageDeleteForbidden() {
export function pushUnableToCall() { export function pushUnableToCall() {
pushToastError('unableToCall', window.i18n.stripped('callsCannotStart')); pushToastError('unableToCall', window.i18n.stripped('callsCannotStart'));
} }
export function pushedMissedCall(userName: string) { export function pushedMissedCall(userName: string) {
@ -184,7 +182,7 @@ export function pushDeleted(count: number) {
pushToastSuccess('deleted', window.i18n.stripped('deleteMessageDeleted', { count })); pushToastSuccess('deleted', window.i18n.stripped('deleteMessageDeleted', { count }));
} }
export function pushCannotRemoveCreatorFromGroup() { export function pushCannotRemoveGroupAdmin() {
pushToastWarning('adminCannotBeRemoved', window.i18n.stripped('adminCannotBeRemoved')); pushToastWarning('adminCannotBeRemoved', window.i18n.stripped('adminCannotBeRemoved'));
} }

@ -64,11 +64,17 @@ async function addJob({ groupPk, member, inviteAsAdmin }: JobExtraArgs) {
await runners.groupInviteJobRunner.addJob(groupInviteJob); await runners.groupInviteJobRunner.addJob(groupInviteJob);
if (inviteAsAdmin) {
window?.inboxStore?.dispatch(
groupInfoActions.setPromotionPending({ groupPk, pubkey: member, sending: true })
);
} else {
window?.inboxStore?.dispatch( window?.inboxStore?.dispatch(
groupInfoActions.setInvitePending({ groupPk, pubkey: member, sending: true }) groupInfoActions.setInvitePending({ groupPk, pubkey: member, sending: true })
); );
} }
} }
}
function displayFailedInvitesForGroup(groupPk: GroupPubkeyType) { function displayFailedInvitesForGroup(groupPk: GroupPubkeyType) {
const thisGroupFailures = invitesFailed.get(groupPk); const thisGroupFailures = invitesFailed.get(groupPk);
@ -219,9 +225,16 @@ class GroupInviteJob extends PersistedJob<GroupInvitePersistedData> {
} }
updateFailedStateForMember(groupPk, member, failed); updateFailedStateForMember(groupPk, member, failed);
if (inviteAsAdmin) {
window?.inboxStore?.dispatch(
groupInfoActions.setPromotionPending({ groupPk, pubkey: member, sending: false })
);
} else {
window?.inboxStore?.dispatch( window?.inboxStore?.dispatch(
groupInfoActions.setInvitePending({ groupPk, pubkey: member, sending: false }) groupInfoActions.setInvitePending({ groupPk, pubkey: member, sending: true })
); );
}
window?.inboxStore?.dispatch( window?.inboxStore?.dispatch(
groupInfoActions.refreshGroupDetailsFromWrapper({ groupPk }) as any groupInfoActions.refreshGroupDetailsFromWrapper({ groupPk }) as any
); );

@ -184,10 +184,9 @@ async function pushChangesToGroupSwarmIfNeeded({
const changes = LibSessionUtil.batchResultsToGroupSuccessfulChange(result, { const changes = LibSessionUtil.batchResultsToGroupSuccessfulChange(result, {
allOldHashes, allOldHashes,
messages: pendingConfigData, messages: pendingConfigData,
}); });
if (isEmpty(changes)) { if ((allOldHashes.size || pendingConfigData.length) && isEmpty(changes)) {
return RunJobResult.RetryJobIfPossible; return RunJobResult.RetryJobIfPossible;
} }

@ -52,6 +52,11 @@ function getMemberInviteFailed(state: StateType, pubkey: PubkeyType, convo?: Gro
return findMemberInMembers(members, pubkey)?.inviteFailed || false; return findMemberInMembers(members, pubkey)?.inviteFailed || false;
} }
function getMemberInviteNotSent(state: StateType, pubkey: PubkeyType, convo?: GroupPubkeyType) {
const members = getMembersOfGroup(state, convo);
return findMemberInMembers(members, pubkey)?.inviteNotSent || false;
}
function getMemberInviteSent(state: StateType, pubkey: PubkeyType, convo?: GroupPubkeyType) { function getMemberInviteSent(state: StateType, pubkey: PubkeyType, convo?: GroupPubkeyType) {
const members = getMembersOfGroup(state, convo); const members = getMembersOfGroup(state, convo);
@ -63,6 +68,11 @@ function getMemberIsPromoted(state: StateType, pubkey: PubkeyType, convo?: Group
return findMemberInMembers(members, pubkey)?.promoted || false; return findMemberInMembers(members, pubkey)?.promoted || false;
} }
function getMemberHasAcceptedInvite(state: StateType, pubkey: PubkeyType, convo?: GroupPubkeyType) {
const members = getMembersOfGroup(state, convo);
return findMemberInMembers(members, pubkey)?.inviteAccepted || false;
}
function getMemberPromotionFailed(state: StateType, pubkey: PubkeyType, convo?: GroupPubkeyType) { function getMemberPromotionFailed(state: StateType, pubkey: PubkeyType, convo?: GroupPubkeyType) {
const members = getMembersOfGroup(state, convo); const members = getMembersOfGroup(state, convo);
return findMemberInMembers(members, pubkey)?.promotionFailed || false; return findMemberInMembers(members, pubkey)?.promotionFailed || false;
@ -73,6 +83,11 @@ function getMemberPromotionSent(state: StateType, pubkey: PubkeyType, convo?: Gr
return findMemberInMembers(members, pubkey)?.promotionPending || false; return findMemberInMembers(members, pubkey)?.promotionPending || false;
} }
function getMemberPromotionNotSent(state: StateType, pubkey: PubkeyType, convo?: GroupPubkeyType) {
const members = getMembersOfGroup(state, convo);
return findMemberInMembers(members, pubkey)?.promotionNotSent || false;
}
export function getLibMembersCount(state: StateType, convo?: GroupPubkeyType): Array<string> { export function getLibMembersCount(state: StateType, convo?: GroupPubkeyType): Array<string> {
return getLibMembersPubkeys(state, convo); return getLibMembersPubkeys(state, convo);
} }
@ -136,9 +151,16 @@ export function useMemberInviteSent(member: PubkeyType, groupPk: GroupPubkeyType
return useSelector((state: StateType) => getMemberInviteSent(state, member, groupPk)); return useSelector((state: StateType) => getMemberInviteSent(state, member, groupPk));
} }
export function useMemberInviteNotSent(member: PubkeyType, groupPk: GroupPubkeyType) {
return useSelector((state: StateType) => getMemberInviteNotSent(state, member, groupPk));
}
export function useMemberIsPromoted(member: PubkeyType, groupPk: GroupPubkeyType) { export function useMemberIsPromoted(member: PubkeyType, groupPk: GroupPubkeyType) {
return useSelector((state: StateType) => getMemberIsPromoted(state, member, groupPk)); return useSelector((state: StateType) => getMemberIsPromoted(state, member, groupPk));
} }
export function useMemberHasAcceptedInvite(member: PubkeyType, groupPk: GroupPubkeyType) {
return useSelector((state: StateType) => getMemberHasAcceptedInvite(state, member, groupPk));
}
export function useMemberPromotionFailed(member: PubkeyType, groupPk: GroupPubkeyType) { export function useMemberPromotionFailed(member: PubkeyType, groupPk: GroupPubkeyType) {
return useSelector((state: StateType) => getMemberPromotionFailed(state, member, groupPk)); return useSelector((state: StateType) => getMemberPromotionFailed(state, member, groupPk));
@ -148,6 +170,10 @@ export function useMemberPromotionSent(member: PubkeyType, groupPk: GroupPubkeyT
return useSelector((state: StateType) => getMemberPromotionSent(state, member, groupPk)); return useSelector((state: StateType) => getMemberPromotionSent(state, member, groupPk));
} }
export function useMemberPromotionNotSent(member: PubkeyType, groupPk: GroupPubkeyType) {
return useSelector((state: StateType) => getMemberPromotionNotSent(state, member, groupPk));
}
export function useMemberGroupChangePending() { export function useMemberGroupChangePending() {
return useSelector(getIsMemberGroupChangePendingFromUI); return useSelector(getIsMemberGroupChangePendingFromUI);
} }

Loading…
Cancel
Save