mirror of
https://github.com/oxen-io/session-desktop.git
synced 2023-12-14 02:12:57 +01:00
fix leave opengroup button right panel, and add memberCount
This commit is contained in:
parent
127b7d41fa
commit
3aa9ca785f
12 changed files with 180 additions and 25 deletions
|
@ -601,7 +601,7 @@
|
|||
}
|
||||
});
|
||||
|
||||
Whisper.events.on('leaveGroup', async groupConvo => {
|
||||
Whisper.events.on('leaveClosedGroup', async groupConvo => {
|
||||
if (appView) {
|
||||
appView.showLeaveGroupDialog(groupConvo);
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
this.remove();
|
||||
},
|
||||
submit() {
|
||||
this.convo.leaveGroup();
|
||||
this.convo.leaveClosedGroup();
|
||||
},
|
||||
});
|
||||
})();
|
||||
|
|
|
@ -178,7 +178,7 @@
|
|||
window.confirmationDialog({
|
||||
title,
|
||||
message,
|
||||
resolve: () => groupConvo.leaveGroup(),
|
||||
resolve: () => groupConvo.leaveClosedGroup(),
|
||||
theme: this.getThemeObject(),
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -61,7 +61,7 @@ describe('ConversationCollection', () => {
|
|||
// type: 'group',
|
||||
// id: '052d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab',
|
||||
// });
|
||||
// await convo.leaveGroup();
|
||||
// await convo.leaveClosedGroup();
|
||||
// assert.notEqual(convo.messageCollection.length, 0);
|
||||
// });
|
||||
// it('has a title', () => {
|
||||
|
|
|
@ -384,7 +384,7 @@ export class SessionConversation extends React.Component<Props, State> {
|
|||
conversation.copyPublicKey();
|
||||
},
|
||||
onLeaveGroup: () => {
|
||||
window.Whisper.events.trigger('leaveGroup', conversation);
|
||||
window.Whisper.events.trigger('leaveClosedGroup', conversation);
|
||||
},
|
||||
onInviteContacts: () => {
|
||||
window.Whisper.events.trigger('inviteContacts', conversation);
|
||||
|
@ -492,8 +492,9 @@ export class SessionConversation extends React.Component<Props, State> {
|
|||
onInviteContacts: () => {
|
||||
window.Whisper.events.trigger('inviteContacts', conversation);
|
||||
},
|
||||
onDeleteContact: conversation.deleteContact,
|
||||
onLeaveGroup: () => {
|
||||
window.Whisper.events.trigger('leaveGroup', conversation);
|
||||
window.Whisper.events.trigger('leaveClosedGroup', conversation);
|
||||
},
|
||||
onAddModerators: () => {
|
||||
window.Whisper.events.trigger('addModerators', conversation);
|
||||
|
|
|
@ -39,6 +39,7 @@ interface Props {
|
|||
onGoBack: () => void;
|
||||
onInviteContacts: () => void;
|
||||
onLeaveGroup: () => void;
|
||||
onDeleteContact: () => void;
|
||||
onUpdateGroupName: () => void;
|
||||
onAddModerators: () => void;
|
||||
onRemoveModerators: () => void;
|
||||
|
@ -218,6 +219,7 @@ class SessionRightPanel extends React.Component<Props, State> {
|
|||
name,
|
||||
timerOptions,
|
||||
onLeaveGroup,
|
||||
onDeleteContact,
|
||||
isKickedFromGroup,
|
||||
left,
|
||||
isPublic,
|
||||
|
@ -310,7 +312,7 @@ class SessionRightPanel extends React.Component<Props, State> {
|
|||
buttonColor={SessionButtonColor.Danger}
|
||||
disabled={isKickedFromGroup || left}
|
||||
buttonType={SessionButtonType.SquareOutline}
|
||||
onClick={onLeaveGroup}
|
||||
onClick={isPublic ? onDeleteContact : onLeaveGroup}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -419,7 +419,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
onCopyPublicKey: this.copyPublicKey,
|
||||
onDeleteContact: this.deleteContact,
|
||||
onLeaveGroup: () => {
|
||||
window.Whisper.events.trigger('leaveGroup', this);
|
||||
window.Whisper.events.trigger('leaveClosedGroup', this);
|
||||
},
|
||||
onDeleteMessages: this.deleteMessages,
|
||||
onInviteContacts: () => {
|
||||
|
@ -953,7 +953,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
return model;
|
||||
}
|
||||
|
||||
public async leaveGroup() {
|
||||
public async leaveClosedGroup() {
|
||||
if (this.isMediumGroup()) {
|
||||
await leaveClosedGroup(this.id);
|
||||
} else {
|
||||
|
|
|
@ -419,7 +419,9 @@ export const getAllRoomInfos = async (roomInfos: OpenGroupV2Room) => {
|
|||
return parseRooms(result);
|
||||
};
|
||||
|
||||
export const getMemberCount = async (roomInfos: OpenGroupRequestCommonType): Promise<void> => {
|
||||
export const getMemberCount = async (
|
||||
roomInfos: OpenGroupRequestCommonType
|
||||
): Promise<number | undefined> => {
|
||||
const request: OpenGroupV2Request = {
|
||||
method: 'GET',
|
||||
room: roomInfos.roomId,
|
||||
|
@ -438,18 +440,7 @@ export const getMemberCount = async (roomInfos: OpenGroupRequestCommonType): Pro
|
|||
return;
|
||||
}
|
||||
|
||||
const conversationId = getOpenGroupV2ConversationId(roomInfos.serverUrl, roomInfos.roomId);
|
||||
|
||||
const convo = ConversationController.getInstance().get(conversationId);
|
||||
if (!convo) {
|
||||
window.log.warn('cannot update conversation memberCount as it does not exist');
|
||||
return;
|
||||
}
|
||||
if (convo.get('subscriberCount') !== count) {
|
||||
convo.set({ subscriberCount: count });
|
||||
// triggers the save to db and the refresh of the UI
|
||||
await convo.commit();
|
||||
}
|
||||
return count;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,7 +8,7 @@ import { parseStatusCodeFromOnionRequest } from './OpenGroupAPIV2Parser';
|
|||
import _ from 'lodash';
|
||||
import { sendViaOnion } from '../../session/onions/onionSend';
|
||||
import { OpenGroupMessageV2 } from './OpenGroupMessageV2';
|
||||
import { downloadPreviewOpenGroupV2, getAuthToken } from './OpenGroupAPIV2';
|
||||
import { downloadPreviewOpenGroupV2, getAuthToken, getMemberCount } from './OpenGroupAPIV2';
|
||||
|
||||
const COMPACT_POLL_ENDPOINT = 'compact_poll';
|
||||
|
||||
|
@ -72,6 +72,50 @@ export const getAllBase64AvatarForRooms = async (
|
|||
return validPreviewBase64 ? validPreviewBase64 : null;
|
||||
};
|
||||
|
||||
export const getAllMemberCount = async (
|
||||
serverUrl: string,
|
||||
rooms: Set<string>,
|
||||
abortSignal: AbortSignal
|
||||
): Promise<Array<ParsedMemberCount> | null> => {
|
||||
// fetch all we need
|
||||
const allValidRoomInfos = await getAllValidRoomInfos(serverUrl, rooms);
|
||||
if (!allValidRoomInfos?.length) {
|
||||
window.log.info('getAllMemberCount: no valid roominfos got.');
|
||||
return null;
|
||||
}
|
||||
if (abortSignal.aborted) {
|
||||
window.log.info('memberCount aborted, returning null');
|
||||
return null;
|
||||
}
|
||||
// Currently this call will not abort if AbortSignal is aborted,
|
||||
// but the call will return null.
|
||||
const validMemberCount = _.compact(
|
||||
await Promise.all(
|
||||
allValidRoomInfos.map(async room => {
|
||||
try {
|
||||
const memberCount = await getMemberCount(room);
|
||||
if (memberCount !== undefined) {
|
||||
return {
|
||||
roomId: room.roomId,
|
||||
memberCount,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
window.log.warn('getPreview failed for room', room);
|
||||
}
|
||||
return null;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
if (abortSignal.aborted) {
|
||||
window.log.info('getMemberCount aborted, returning null');
|
||||
return null;
|
||||
}
|
||||
|
||||
return validMemberCount ? validMemberCount : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* This function fetches the valid roomInfos from the database.
|
||||
* It also makes sure that the pubkey for all those rooms are the same, or returns null.
|
||||
|
@ -258,6 +302,11 @@ export type ParsedBase64Avatar = {
|
|||
base64: string;
|
||||
};
|
||||
|
||||
export type ParsedMemberCount = {
|
||||
roomId: string;
|
||||
memberCount: number;
|
||||
};
|
||||
|
||||
const parseCompactPollResult = async (
|
||||
singleRoomResult: any,
|
||||
serverUrl: string
|
||||
|
|
|
@ -5,8 +5,10 @@ import { OpenGroupRequestCommonType } from './ApiUtil';
|
|||
import {
|
||||
compactFetchEverything,
|
||||
getAllBase64AvatarForRooms,
|
||||
getAllMemberCount,
|
||||
ParsedBase64Avatar,
|
||||
ParsedDeletions,
|
||||
ParsedMemberCount,
|
||||
ParsedRoomCompactPollResults,
|
||||
} from './OpenGroupAPIV2CompactPoll';
|
||||
import _ from 'lodash';
|
||||
|
@ -15,13 +17,14 @@ import { getMessageIdsFromServerIds, removeMessage } from '../../data/data';
|
|||
import { getV2OpenGroupRoom, saveV2OpenGroupRoom } from '../../data/opengroups';
|
||||
import { OpenGroupMessageV2 } from './OpenGroupMessageV2';
|
||||
import { handleOpenGroupV2Message } from '../../receiver/receiver';
|
||||
import { DAYS, SECONDS } from '../../session/utils/Number';
|
||||
import { DAYS, MINUTES, SECONDS } from '../../session/utils/Number';
|
||||
import autoBind from 'auto-bind';
|
||||
import { sha256 } from '../../session/crypto';
|
||||
import { fromBase64ToArrayBuffer } from '../../session/utils/String';
|
||||
|
||||
const pollForEverythingInterval = SECONDS * 6;
|
||||
const pollForRoomAvatarInterval = DAYS * 1;
|
||||
const pollForMemberCountInterval = MINUTES * 10;
|
||||
|
||||
/**
|
||||
* An OpenGroupServerPollerV2 polls for everything for a particular server. We should
|
||||
|
@ -31,10 +34,26 @@ const pollForRoomAvatarInterval = DAYS * 1;
|
|||
* for this server.
|
||||
*/
|
||||
export class OpenGroupServerPoller {
|
||||
/**
|
||||
* The server url to poll for this opengroup poller.
|
||||
* Remember, we have one poller per opengroup poller, no matter how many rooms we have joined on this same server
|
||||
*/
|
||||
private readonly serverUrl: string;
|
||||
|
||||
/**
|
||||
* The set of rooms to poll from.
|
||||
*
|
||||
*/
|
||||
private readonly roomIdsToPoll: Set<string> = new Set();
|
||||
|
||||
/**
|
||||
* This timer is used to tick for compact Polling for this opengroup server
|
||||
* It ticks every `pollForEverythingInterval` except.
|
||||
* If the last run is still in progress, the new one won't start and just return.
|
||||
*/
|
||||
private pollForEverythingTimer?: NodeJS.Timeout;
|
||||
private pollForRoomAvatarTimer?: NodeJS.Timeout;
|
||||
private pollForMemberCountTimer?: NodeJS.Timeout;
|
||||
private readonly abortController: AbortController;
|
||||
|
||||
/**
|
||||
|
@ -45,6 +64,7 @@ export class OpenGroupServerPoller {
|
|||
*/
|
||||
private isPolling = false;
|
||||
private isPreviewPolling = false;
|
||||
private isMemberCountPolling = false;
|
||||
private wasStopped = false;
|
||||
|
||||
constructor(roomInfos: Array<OpenGroupRequestCommonType>) {
|
||||
|
@ -71,6 +91,10 @@ export class OpenGroupServerPoller {
|
|||
this.previewPerRoomPoll,
|
||||
pollForRoomAvatarInterval
|
||||
);
|
||||
this.pollForMemberCountTimer = global.setInterval(
|
||||
this.pollForAllMemberCount,
|
||||
pollForMemberCountInterval
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,6 +114,7 @@ export class OpenGroupServerPoller {
|
|||
// if we are not already polling right now, trigger a polling
|
||||
void this.compactPoll();
|
||||
void this.previewPerRoomPoll();
|
||||
void this.pollForAllMemberCount();
|
||||
}
|
||||
|
||||
public removeRoomFromPoll(room: OpenGroupRequestCommonType) {
|
||||
|
@ -120,6 +145,10 @@ export class OpenGroupServerPoller {
|
|||
if (this.pollForRoomAvatarTimer) {
|
||||
global.clearInterval(this.pollForRoomAvatarTimer);
|
||||
}
|
||||
|
||||
if (this.pollForMemberCountTimer) {
|
||||
global.clearInterval(this.pollForMemberCountTimer);
|
||||
}
|
||||
if (this.pollForEverythingTimer) {
|
||||
// cancel next ticks for each timer
|
||||
global.clearInterval(this.pollForEverythingTimer);
|
||||
|
@ -128,6 +157,7 @@ export class OpenGroupServerPoller {
|
|||
this.abortController?.abort();
|
||||
this.pollForEverythingTimer = undefined;
|
||||
this.pollForRoomAvatarTimer = undefined;
|
||||
this.pollForMemberCountTimer = undefined;
|
||||
this.wasStopped = true;
|
||||
}
|
||||
}
|
||||
|
@ -162,6 +192,21 @@ export class OpenGroupServerPoller {
|
|||
return true;
|
||||
}
|
||||
|
||||
private shouldPollForMemberCount() {
|
||||
if (this.wasStopped) {
|
||||
window.log.error('Serverpoller was stopped. PolLForMemberCount should not happen');
|
||||
return false;
|
||||
}
|
||||
if (!this.roomIdsToPoll.size) {
|
||||
return false;
|
||||
}
|
||||
// return early if a poll is already in progress
|
||||
if (this.isMemberCountPolling) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private async previewPerRoomPoll() {
|
||||
if (!this.shouldPollPreview()) {
|
||||
return;
|
||||
|
@ -201,6 +246,46 @@ export class OpenGroupServerPoller {
|
|||
}
|
||||
}
|
||||
|
||||
private async pollForAllMemberCount() {
|
||||
if (!this.shouldPollForMemberCount()) {
|
||||
return;
|
||||
}
|
||||
// do everything with throwing so we can check only at one place
|
||||
// what we have to clean
|
||||
try {
|
||||
this.isMemberCountPolling = true;
|
||||
// don't try to make the request if we are aborted
|
||||
if (this.abortController.signal.aborted) {
|
||||
throw new Error('Poller aborted');
|
||||
}
|
||||
|
||||
let memberCountGotResults = await getAllMemberCount(
|
||||
this.serverUrl,
|
||||
this.roomIdsToPoll,
|
||||
this.abortController.signal
|
||||
);
|
||||
|
||||
// check that we are still not aborted
|
||||
if (this.abortController.signal.aborted) {
|
||||
throw new Error('Abort controller was canceled. Dropping memberCount request');
|
||||
}
|
||||
if (!memberCountGotResults) {
|
||||
throw new Error('MemberCount: no results');
|
||||
}
|
||||
// we were not aborted, make sure to filter out roomIds we are not polling for anymore
|
||||
memberCountGotResults = memberCountGotResults.filter(result =>
|
||||
this.roomIdsToPoll.has(result.roomId)
|
||||
);
|
||||
|
||||
// ==> At this point all those results need to trigger conversation updates, so update what we have to update
|
||||
await handleAllMemberCount(this.serverUrl, memberCountGotResults);
|
||||
} catch (e) {
|
||||
window.log.warn('Got error while memberCount fetch:', e);
|
||||
} finally {
|
||||
this.isMemberCountPolling = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async compactPoll() {
|
||||
if (!this.shouldPoll()) {
|
||||
return;
|
||||
|
@ -396,3 +481,29 @@ const handleBase64AvatarUpdate = async (
|
|||
})
|
||||
);
|
||||
};
|
||||
|
||||
async function handleAllMemberCount(
|
||||
serverUrl: string,
|
||||
memberCountGotResults: Array<ParsedMemberCount>
|
||||
) {
|
||||
if (!memberCountGotResults.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
memberCountGotResults.map(async roomCount => {
|
||||
const conversationId = getOpenGroupV2ConversationId(serverUrl, roomCount.roomId);
|
||||
|
||||
const convo = ConversationController.getInstance().get(conversationId);
|
||||
if (!convo) {
|
||||
window.log.warn('cannot update conversation memberCount as it does not exist');
|
||||
return;
|
||||
}
|
||||
if (convo.get('subscriberCount') !== roomCount.memberCount) {
|
||||
convo.set({ subscriberCount: roomCount.memberCount });
|
||||
// triggers the save to db and the refresh of the UI
|
||||
await convo.commit();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -189,7 +189,7 @@ export class ConversationController {
|
|||
|
||||
// Close group leaving
|
||||
if (conversation.isClosedGroup()) {
|
||||
await conversation.leaveGroup();
|
||||
await conversation.leaveClosedGroup();
|
||||
} else if (conversation.isPublic() && !conversation.isOpenGroupV2()) {
|
||||
const channelAPI = await conversation.getPublicSendData();
|
||||
if (channelAPI === null) {
|
||||
|
|
|
@ -439,6 +439,7 @@ const sendOnionRequest = async (
|
|||
const guardUrl = `https://${nodePath[0].ip}:${nodePath[0].port}${target}`;
|
||||
// no logs for that one as we do need to call insecureNodeFetch to our guardNode
|
||||
// window.log.info('insecureNodeFetch => plaintext for sendOnionRequest');
|
||||
console.warn('sendViaOnion payload: ', payload.length);
|
||||
|
||||
const response = await insecureNodeFetch(guardUrl, guardFetchOptions);
|
||||
return processOnionResponse(reqIdx, response, destCtx.symmetricKey, false, abortSignal);
|
||||
|
|
Loading…
Reference in a new issue