session-desktop/ts/session/apis/open_group_api/sogsv3/sogsV3SendReaction.ts

144 lines
4.7 KiB
TypeScript

import { AbortSignal } from 'abort-controller';
import { getEmojiDataFromNative } from 'emoji-mart';
import { Data } from '../../../../data/data';
import { ConversationModel } from '../../../../models/conversation';
import {
Action,
FixedBaseEmoji,
OpenGroupReactionResponse,
Reaction,
} from '../../../../types/Reaction';
import { Reactions } from '../../../../util/reactions';
import { OnionSending } from '../../../onions/onionSend';
import { UserUtils } from '../../../utils';
import { OpenGroupPollingUtils } from '../opengroupV2/OpenGroupPollingUtils';
import { getUsBlindedInThatServer } from './knownBlindedkeys';
import { batchGlobalIsSuccess, parseBatchGlobalStatusCode } from './sogsV3BatchPoll';
import {
addToMutationCache,
ChangeType,
SogsV3Mutation,
updateMutationCache,
} from './sogsV3MutationCache';
export const hasReactionSupport = async (
serverId: number
): Promise<{ supported: boolean; conversation: ConversationModel | null }> => {
const found = await Data.getMessageByServerId(serverId);
if (!found) {
window.log.warn(`Open Group Message ${serverId} not found in db`);
return { supported: false, conversation: null };
}
const conversationModel = found?.getConversation();
if (!conversationModel) {
window.log.warn(`Conversation for ${serverId} not found in db`);
return { supported: false, conversation: null };
}
if (!conversationModel.hasReactions()) {
window.log.warn("This open group doesn't have reaction support. Server Message ID", serverId);
return { supported: false, conversation: null };
}
return { supported: true, conversation: conversationModel };
};
export const sendSogsReactionOnionV4 = async (
serverUrl: string,
room: string, // this is the roomId
abortSignal: AbortSignal,
reaction: Reaction,
blinded: boolean
): Promise<boolean> => {
const allValidRoomInfos = OpenGroupPollingUtils.getAllValidRoomInfos(serverUrl, new Set([room]));
if (!allValidRoomInfos?.length) {
window?.log?.info('getSendReactionRequest: no valid roominfos got.');
throw new Error(`Could not find sogs pubkey of url:${serverUrl}`);
}
const { supported, conversation } = await hasReactionSupport(reaction.id);
if (!supported) {
return false;
}
if (Reactions.hitRateLimit()) {
return false;
}
if (!conversation) {
window.log.warn(`Conversation for ${reaction.id} not found in db`);
return false;
}
// The SOGS endpoint supports any text input so we need to make sure we are sending a valid unicode emoji
// for an invalid input we use https://emojipedia.org/frame-with-an-x/ as a replacement since it cannot rendered as an emoji but is valid unicode
// NOTE emoji-mart v5.2.2 types for getEmojiDataFromNative are broken
// @ts-ignore
const emoji = (getEmojiDataFromNative(reaction.emoji) as FixedBaseEmoji) ? reaction.emoji : '🖾';
const endpoint = `/room/${room}/reaction/${reaction.id}/${emoji}`;
const method = reaction.action === Action.REACT ? 'PUT' : 'DELETE';
const serverPubkey = allValidRoomInfos[0].serverPublicKey;
const cacheEntry: SogsV3Mutation = {
server: serverUrl,
room: room,
changeType: ChangeType.REACTIONS,
seqno: null,
metadata: {
messageId: reaction.id,
emoji,
action: reaction.action === Action.REACT ? 'ADD' : 'REMOVE',
},
};
addToMutationCache(cacheEntry);
// Since responses can take a long time we immediately update the sender's UI and if there is a problem it is overwritten by handleOpenGroupMessageReactions later.
const me = UserUtils.getOurPubKeyStrFromCache();
await Reactions.handleMessageReaction({
reaction,
sender: blinded ? getUsBlindedInThatServer(conversation) || me : me,
you: true,
isOpenGroup: true,
});
// reaction endpoint requires an empty dict {}
const stringifiedBody = null;
const result = await OnionSending.sendJsonViaOnionV4ToSogs({
serverUrl,
endpoint,
serverPubkey,
method,
abortSignal,
blinded,
stringifiedBody,
headers: null,
throwErrors: true,
});
if (!batchGlobalIsSuccess(result)) {
window?.log?.warn('sendSogsReactionWithOnionV4 Got unknown status code; res:', result);
throw new Error(
`sendSogsReactionOnionV4: invalid status code: ${parseBatchGlobalStatusCode(result)}`
);
}
if (!result) {
throw new Error('Could not putReaction, res is invalid');
}
const rawMessage = result.body as OpenGroupReactionResponse;
if (!rawMessage) {
throw new Error('putReaction parsing failed');
}
const success = Boolean(reaction.action === Action.REACT ? rawMessage.added : rawMessage.removed);
if (success) {
updateMutationCache(cacheEntry, rawMessage.seqno);
}
return success;
};