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

@ -186,6 +186,6 @@
{/if}
</div>
<div class="typing-info-container">
<span class="typing-list" class:invisible={ typingList === "?no one?" }>{ typingList }</span><span class="typing-message" class:invisible={ typingList === "?no one?" }>{ typingMessage }</span>
<span class="typing-list" class:invisible={ typingList === "?no one?" }>{ typingList }</span> <span class="typing-message" class:invisible={ typingList === "?no one?" }>{ typingMessage }</span>
</div>
</div>

View file

@ -15,11 +15,12 @@ export const GatewayErrors = {
export const GatewayPayloadType = {
Hello: 0,
Authenticate: 1,
Authenticate: 1, // client
Ready: 2,
Ping: 3,
Ping: 3, // client
RPCRequest: 4, // client
RPCResponse: 5,
RPCSignal: 5, // client
RPCResponse: 6,
ChannelCreate: 110,
ChannelUpdate: 111,
@ -194,11 +195,11 @@ export default {
this.handlers.delete(event);
}
},
sendRPCRequest(calls) {
sendRPCRequest(calls, isSignal) {
return new Promise((resolve, _reject) => {
this.waitingSerials.set(this.serial, resolve);
this.send({
t: GatewayPayloadType.RPCRequest,
t: isSignal ? GatewayPayloadType.RPCSignal : GatewayPayloadType.RPCRequest,
d: calls,
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]];
if (requiresAuthentication && gateway.authenticated && !cacheable) {
const replies = await gateway.sendRPCRequest(calls);
const ok = Array.isArray(replies) && replies[0] && !replies[0].code;
const replies = await gateway.sendRPCRequest(calls, _isSignal);
const ok = Array.isArray(replies) && !(replies[0] && replies[0].code);
return {
json: ok ? replies[0] : null,
ok
@ -111,7 +111,14 @@ export async function remoteCall({methodId, requiresAuthentication, cacheable},
} else {
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;
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 logger from "./logging";
import { methods, remoteCall } from "./request";
import { methods, remoteCall, remoteSignal } from "./request";
import { getItem, setItem } from "./storage";
const storeLog = logger("Store");
@ -539,7 +539,7 @@ class TypingStore extends Store {
this.startedTyping(userInfoStore.value, selectedChannel.value.id, 6500);
if (this.ownNeedsUpdate) {
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);
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) {
messagesStoreForChannel.setMessage(optimisticMessageId, res.json);
} else {
if (!res.ok) {
messagesStoreForChannel.deleteMessage({
id: optimisticMessageId
});

View file

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

View file

@ -409,20 +409,19 @@ export default function(server: Server) {
ws.state.alive = true;
break;
}
case GatewayPayloadType.RPCSignal: /* through */
case GatewayPayloadType.RPCRequest: {
if (!ws.state.ready || !ws.state.user) {
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, {
t: GatewayPayloadType.RPCResponse,
d: results,
s: payload.s
});
}).catch(e => {
console.error("gateway: unexpected error while handling RPCRequest", e);
return closeWithError(ws, gatewayErrors.INTERNAL_ERROR);
});
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) {
return {
...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));
promise.then(value => responses[index] = value);
promise.then(value => {
if (ignoreNonErrors && !value.code) {
responses[index] = null;
} else {
responses[index] = value;
}
});
promises[index] = promise;
});