Compare commits

..

2 commits

Author SHA1 Message Date
hippoz
68c38a1a47
fix typing indicator gap 2023-02-22 02:49:39 +02:00
hippoz
20bbc56bbe
experiment with "signal" rpc that does not send the data returned by the method 2023-02-22 02:47:29 +02:00
7 changed files with 34 additions and 22 deletions

View file

@ -15,11 +15,12 @@ export const GatewayErrors = {
export const GatewayPayloadType = { export const GatewayPayloadType = {
Hello: 0, Hello: 0,
Authenticate: 1, Authenticate: 1, // client
Ready: 2, Ready: 2,
Ping: 3, Ping: 3, // client
RPCRequest: 4, // client RPCRequest: 4, // client
RPCResponse: 5, RPCSignal: 5, // client
RPCResponse: 6,
ChannelCreate: 110, ChannelCreate: 110,
ChannelUpdate: 111, ChannelUpdate: 111,
@ -194,11 +195,11 @@ export default {
this.handlers.delete(event); this.handlers.delete(event);
} }
}, },
sendRPCRequest(calls) { sendRPCRequest(calls, isSignal) {
return new Promise((resolve, _reject) => { return new Promise((resolve, _reject) => {
this.waitingSerials.set(this.serial, resolve); this.waitingSerials.set(this.serial, resolve);
this.send({ this.send({
t: GatewayPayloadType.RPCRequest, t: isSignal ? GatewayPayloadType.RPCSignal : GatewayPayloadType.RPCRequest,
d: calls, d: calls,
s: this.serial s: this.serial
}); });

View file

@ -94,11 +94,11 @@ export default function doRequest(method, endpoint, auth=true, body=null) {
}); });
} }
export async function remoteCall({methodId, requiresAuthentication, cacheable}, ...args) { export async function remoteCall({methodId, requiresAuthentication, cacheable, _isSignal=false}, ...args) {
const calls = [[methodId, ...args]]; const calls = [[methodId, ...args]];
if (requiresAuthentication && gateway.authenticated && !cacheable) { if (requiresAuthentication && gateway.authenticated && !cacheable) {
const replies = await gateway.sendRPCRequest(calls); const replies = await gateway.sendRPCRequest(calls, _isSignal);
const ok = Array.isArray(replies) && replies[0] && !replies[0].code; const ok = Array.isArray(replies) && !(replies[0] && replies[0].code);
return { return {
json: ok ? replies[0] : null, json: ok ? replies[0] : null,
ok ok
@ -111,7 +111,14 @@ export async function remoteCall({methodId, requiresAuthentication, cacheable},
} else { } else {
response = await doRequest("POST", apiRoute("rpc"), requiresAuthentication, calls); response = await doRequest("POST", apiRoute("rpc"), requiresAuthentication, calls);
} }
response.ok = response.ok && Array.isArray(response.json) && response.json[0] && !response.json[0].code; response.ok = response.ok && Array.isArray(response.json) && !(response.json[0] && response.json[0].code);
response.json = response.ok ? response.json[0] : null; response.json = response.ok ? response.json[0] : null;
return response; return response;
} }
export async function remoteSignal(method, ...args) {
return await remoteCall({
...method,
_isSignal: true
}, ...args);
}

View file

@ -1,6 +1,6 @@
import gateway, { GatewayEventType, GatewayPayloadType, GatewayPresenceStatus } from "./gateway"; import gateway, { GatewayEventType, GatewayPayloadType, GatewayPresenceStatus } from "./gateway";
import logger from "./logging"; import logger from "./logging";
import { methods, remoteCall } from "./request"; import { methods, remoteCall, remoteSignal } from "./request";
import { getItem, setItem } from "./storage"; import { getItem, setItem } from "./storage";
const storeLog = logger("Store"); const storeLog = logger("Store");
@ -539,7 +539,7 @@ class TypingStore extends Store {
this.startedTyping(userInfoStore.value, selectedChannel.value.id, 6500); this.startedTyping(userInfoStore.value, selectedChannel.value.id, 6500);
if (this.ownNeedsUpdate) { if (this.ownNeedsUpdate) {
this.ownNeedsUpdate = false; this.ownNeedsUpdate = false;
await remoteCall(methods.putChannelTyping, selectedChannel.value.id); await remoteSignal(methods.putChannelTyping, selectedChannel.value.id);
} }
} }
} }
@ -695,11 +695,9 @@ export const sendMessageAction = createAction("sendMessageAction", async ({chann
const messagesStoreForChannel = messagesStoreProvider.getStore(channelId); const messagesStoreForChannel = messagesStoreProvider.getStore(channelId);
messagesStoreForChannel.addMessage(optimisticMessage); messagesStoreForChannel.addMessage(optimisticMessage);
const res = await remoteCall(methods.createChannelMessage, channelId, content, optimisticMessageId, null); const res = await remoteSignal(methods.createChannelMessage, channelId, content, optimisticMessageId, null);
if (res.ok) { if (!res.ok) {
messagesStoreForChannel.setMessage(optimisticMessageId, res.json);
} else {
messagesStoreForChannel.deleteMessage({ messagesStoreForChannel.deleteMessage({
id: optimisticMessageId id: optimisticMessageId
}); });

View file

@ -4,6 +4,7 @@ export enum GatewayPayloadType {
Ready, Ready,
Ping, // client Ping, // client
RPCRequest, // client RPCRequest, // client
RPCSignal, // client
RPCResponse, RPCResponse,
ChannelCreate = 110, ChannelCreate = 110,

View file

@ -409,20 +409,19 @@ export default function(server: Server) {
ws.state.alive = true; ws.state.alive = true;
break; break;
} }
case GatewayPayloadType.RPCSignal: /* through */
case GatewayPayloadType.RPCRequest: { case GatewayPayloadType.RPCRequest: {
if (!ws.state.ready || !ws.state.user) { if (!ws.state.ready || !ws.state.user) {
return closeWithError(ws, gatewayErrors.NOT_AUTHENTICATED); return closeWithError(ws, gatewayErrors.NOT_AUTHENTICATED);
} }
processMethodBatch(ws.state.user, payload.d).then((results) => { // RPCSignal is like RPCRequest however it does not send RPC method output unless there is an error
processMethodBatch(ws.state.user, payload.d, (payload.t === GatewayPayloadType.RPCSignal ? true : false)).then((results) => {
sendPayload(ws, { sendPayload(ws, {
t: GatewayPayloadType.RPCResponse, t: GatewayPayloadType.RPCResponse,
d: results, d: results,
s: payload.s s: payload.s
}); });
}).catch(e => {
console.error("gateway: unexpected error while handling RPCRequest", e);
return closeWithError(ws, gatewayErrors.INTERNAL_ERROR);
}); });
break; break;
} }

View file

@ -121,7 +121,7 @@ export const userInvokeMethod = async (user: User | null, methodId: number, args
} }
}; };
export const processMethodBatch = async (user: User | null, calls: any) => { export const processMethodBatch = async (user: User | null, calls: any, ignoreNonErrors = false) => {
if (!Array.isArray(calls) || !calls.length || calls.length > 5) { if (!Array.isArray(calls) || !calls.length || calls.length > 5) {
return { return {
...errors.BAD_REQUEST, ...errors.BAD_REQUEST,
@ -141,7 +141,13 @@ export const processMethodBatch = async (user: User | null, calls: any) => {
} }
const promise = userInvokeMethod(user, call[0], call.slice(1, call.length)); const promise = userInvokeMethod(user, call[0], call.slice(1, call.length));
promise.then(value => responses[index] = value); promise.then(value => {
if (ignoreNonErrors && !value.code) {
responses[index] = null;
} else {
responses[index] = value;
}
});
promises[index] = promise; promises[index] = promise;
}); });