add error handling for failed requests
This commit is contained in:
parent
72c9650f71
commit
00f7ca72a5
9 changed files with 233 additions and 106 deletions
|
@ -1,19 +1,19 @@
|
||||||
<script>
|
<script>
|
||||||
import { overlayStore } from "../../stores";
|
import { methods, remoteSignal, responseOk } from "../../request";
|
||||||
import { methods, remoteSignal } from "../../request";
|
import RpcErrorDisplay from "../rpc/RpcErrorDisplay.svelte";
|
||||||
import Modal from "./Modal.svelte";
|
import Modal from "./Modal.svelte";
|
||||||
|
|
||||||
let communityName = "";
|
let communityName = "";
|
||||||
let createButtonEnabled = true;
|
let createButtonEnabled = true;
|
||||||
|
let response;
|
||||||
export let close = () => {};
|
export let close = () => {};
|
||||||
|
|
||||||
const create = async () => {
|
const create = async () => {
|
||||||
createButtonEnabled = false;
|
createButtonEnabled = false;
|
||||||
const { ok } = await remoteSignal(methods.createCommunity, communityName);
|
response = await remoteSignal(methods.createCommunity, communityName);
|
||||||
if (!ok) {
|
createButtonEnabled = true;
|
||||||
overlayStore.toast("Couldn't create community");
|
if (responseOk(response))
|
||||||
}
|
close();
|
||||||
close();
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -21,6 +21,8 @@
|
||||||
<span class="h4" slot="header">Create Community</span>
|
<span class="h4" slot="header">Create Community</span>
|
||||||
|
|
||||||
<svelte:fragment slot="content">
|
<svelte:fragment slot="content">
|
||||||
|
<RpcErrorDisplay response={response} />
|
||||||
|
<RpcErrorDisplay validationIndex={0} response={response} />
|
||||||
<label class="input-label">
|
<label class="input-label">
|
||||||
Community Name
|
Community Name
|
||||||
<input class="input full-width" minlength="1" maxlength="32" bind:value={ communityName } />
|
<input class="input full-width" minlength="1" maxlength="32" bind:value={ communityName } />
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
<script>
|
<script>
|
||||||
import { overlayStore, OverlayType } from "../../stores";
|
import { overlayStore, OverlayType } from "../../stores";
|
||||||
import { methods, remoteCall } from "../../request";
|
import { methods, remoteCall, responseOk } from "../../request";
|
||||||
import Modal from "./Modal.svelte";
|
import Modal from "./Modal.svelte";
|
||||||
|
import RpcErrorDisplay from "../rpc/RpcErrorDisplay.svelte";
|
||||||
|
|
||||||
let username = "";
|
let username = "";
|
||||||
let password = "";
|
let password = "";
|
||||||
let buttonsEnabled = true;
|
let buttonsEnabled = true;
|
||||||
let pendingOtherOpen = false;
|
let pendingOtherOpen = false;
|
||||||
|
let response;
|
||||||
export let close = () => {};
|
export let close = () => {};
|
||||||
|
|
||||||
const create = async () => {
|
const create = async () => {
|
||||||
buttonsEnabled = false;
|
buttonsEnabled = false;
|
||||||
const { ok } = await remoteCall(methods.createUser, username, password);
|
response = await remoteCall(methods.createUser, username, password);
|
||||||
if (ok) {
|
if (responseOk(response)) {
|
||||||
overlayStore.toast("Account created");
|
overlayStore.toast("Account created");
|
||||||
loginInstead();
|
loginInstead();
|
||||||
} else {
|
} else {
|
||||||
overlayStore.toast("Couldn't create account");
|
|
||||||
buttonsEnabled = true;
|
buttonsEnabled = true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const loginInstead = () => {
|
const loginInstead = () => {
|
||||||
|
@ -42,6 +42,9 @@
|
||||||
<span class="h4" slot="header">Create an Account</span>
|
<span class="h4" slot="header">Create an Account</span>
|
||||||
|
|
||||||
<svelte:fragment slot="content">
|
<svelte:fragment slot="content">
|
||||||
|
<RpcErrorDisplay response={response} />
|
||||||
|
|
||||||
|
<RpcErrorDisplay validationIndex={0} response={response} />
|
||||||
<label class="input-label">
|
<label class="input-label">
|
||||||
Username
|
Username
|
||||||
<input class="input full-width" minlength="1" maxlength="32" bind:value={ username } />
|
<input class="input full-width" minlength="1" maxlength="32" bind:value={ username } />
|
||||||
|
@ -49,6 +52,7 @@
|
||||||
|
|
||||||
<div class="separator" />
|
<div class="separator" />
|
||||||
|
|
||||||
|
<RpcErrorDisplay validationIndex={1} response={response} />
|
||||||
<label class="input-label">
|
<label class="input-label">
|
||||||
Password
|
Password
|
||||||
<input class="input full-width" minlength="8" type="password" bind:value={ password } />
|
<input class="input full-width" minlength="8" type="password" bind:value={ password } />
|
||||||
|
@ -56,7 +60,7 @@
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
|
||||||
<svelte:fragment slot="footer">
|
<svelte:fragment slot="footer">
|
||||||
<button class="button modal-secondary-action" on:click="{ loginInstead }">Log in instead</button>
|
<button class="button modal-secondary-action" on:click="{ loginInstead }" disabled="{ !buttonsEnabled }">Log in instead</button>
|
||||||
<button class="button button-accent modal-primary-action" on:click="{ create }" disabled="{ !buttonsEnabled }">Create</button>
|
<button class="button button-accent modal-primary-action" on:click="{ create }" disabled="{ !buttonsEnabled }">Create</button>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
<script>
|
<script>
|
||||||
import { overlayStore } from "../../stores";
|
import { methods, remoteSignal, responseOk } from "../../request";
|
||||||
import { methods, remoteSignal } from "../../request";
|
import RpcErrorDisplay from "../rpc/RpcErrorDisplay.svelte";
|
||||||
import Modal from "./Modal.svelte";
|
import Modal from "./Modal.svelte";
|
||||||
|
|
||||||
let channelName = "";
|
let channelName = "";
|
||||||
let createButtonEnabled = true;
|
let createButtonEnabled = true;
|
||||||
|
let response;
|
||||||
export let close = () => {};
|
export let close = () => {};
|
||||||
export let community = null;
|
export let community = null;
|
||||||
|
|
||||||
const create = async () => {
|
const create = async () => {
|
||||||
createButtonEnabled = false;
|
createButtonEnabled = false;
|
||||||
const { ok } = await remoteSignal(methods.createChannel, channelName, community.id !== -1 ? community.id : null);
|
response = await remoteSignal(methods.createChannel, channelName, community.id !== -1 ? community.id : null);
|
||||||
if (!ok) {
|
createButtonEnabled = true;
|
||||||
overlayStore.toast("Couldn't create channel");
|
if (responseOk(response))
|
||||||
}
|
close();
|
||||||
close();
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -27,6 +27,8 @@
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
|
||||||
<svelte:fragment slot="content">
|
<svelte:fragment slot="content">
|
||||||
|
<RpcErrorDisplay response={response} />
|
||||||
|
<RpcErrorDisplay validationIndex={0} response={response} />
|
||||||
<label class="input-label">
|
<label class="input-label">
|
||||||
Channel Name
|
Channel Name
|
||||||
<input class="input full-width" minlength="1" maxlength="32" bind:value={ channelName } />
|
<input class="input full-width" minlength="1" maxlength="32" bind:value={ channelName } />
|
||||||
|
|
|
@ -1,27 +1,29 @@
|
||||||
<script>
|
<script>
|
||||||
import { overlayStore } from "../../stores";
|
import { overlayStore } from "../../stores";
|
||||||
import { methods, remoteSignal } from "../../request";
|
import { getMessageFromResponse, methods, remoteSignal, responseOk } from "../../request";
|
||||||
import Modal from "./Modal.svelte";
|
import Modal from "./Modal.svelte";
|
||||||
|
import RpcErrorDisplay from "../rpc/RpcErrorDisplay.svelte";
|
||||||
|
|
||||||
export let channel;
|
export let channel;
|
||||||
|
|
||||||
let channelName = channel.name;
|
let channelName = channel.name;
|
||||||
let buttonsEnabled = true;
|
let buttonsEnabled = true;
|
||||||
|
let response;
|
||||||
export let close = () => {};
|
export let close = () => {};
|
||||||
|
|
||||||
const save = async () => {
|
const save = async () => {
|
||||||
buttonsEnabled = false;
|
buttonsEnabled = false;
|
||||||
const { ok } = await remoteSignal(methods.updateChannelName, channel.id, channelName);
|
response = await remoteSignal(methods.updateChannelName, channel.id, channelName);
|
||||||
if (!ok) {
|
buttonsEnabled = true;
|
||||||
overlayStore.toast("Couldn't edit channel");
|
if (responseOk(response)) {
|
||||||
|
close();
|
||||||
}
|
}
|
||||||
close();
|
|
||||||
};
|
};
|
||||||
const deleteChannel = async () => {
|
const deleteChannel = async () => {
|
||||||
buttonsEnabled = false;
|
buttonsEnabled = false;
|
||||||
const { ok } = await remoteSignal(methods.deleteChannel, channel.id);
|
const res = await remoteSignal(methods.deleteChannel, channel.id);
|
||||||
if (!ok) {
|
if (!responseOk(res)) {
|
||||||
overlayStore.toast("Couldn't delete channel");
|
overlayStore.toast(`Couldn't delete channel: ${getMessageFromResponse(res)}`);
|
||||||
}
|
}
|
||||||
close();
|
close();
|
||||||
};
|
};
|
||||||
|
@ -37,8 +39,10 @@
|
||||||
<span class="h4" slot="header">Edit Channel</span>
|
<span class="h4" slot="header">Edit Channel</span>
|
||||||
|
|
||||||
<svelte:fragment slot="content">
|
<svelte:fragment slot="content">
|
||||||
|
<RpcErrorDisplay response={response} />
|
||||||
|
<RpcErrorDisplay validationIndex={1} response={response} />
|
||||||
<label class="input-label">
|
<label class="input-label">
|
||||||
Channel Name
|
<span>Channel Name</span>
|
||||||
<input class="input full-width" minlength="1" maxlength="32" bind:value={ channelName } />
|
<input class="input full-width" minlength="1" maxlength="32" bind:value={ channelName } />
|
||||||
</label>
|
</label>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
|
|
@ -1,27 +1,28 @@
|
||||||
<script>
|
<script>
|
||||||
import { overlayStore } from "../../stores";
|
import { overlayStore } from "../../stores";
|
||||||
import { methods, remoteSignal } from "../../request";
|
import { getMessageFromResponse, methods, remoteSignal, responseOk } from "../../request";
|
||||||
import Modal from "./Modal.svelte";
|
import Modal from "./Modal.svelte";
|
||||||
|
import RpcErrorDisplay from "../rpc/RpcErrorDisplay.svelte";
|
||||||
|
|
||||||
export let message;
|
export let message;
|
||||||
|
|
||||||
let messageContent = message.content;
|
let messageContent = message.content;
|
||||||
let buttonsEnabled = true;
|
let buttonsEnabled = true;
|
||||||
|
let response;
|
||||||
export let close = () => {};
|
export let close = () => {};
|
||||||
|
|
||||||
const save = async () => {
|
const save = async () => {
|
||||||
buttonsEnabled = false;
|
buttonsEnabled = false;
|
||||||
const { ok } = await remoteSignal(methods.updateMessageContent, message.id, messageContent);
|
response = await remoteSignal(methods.updateMessageContent, message.id, messageContent);
|
||||||
if (!ok) {
|
buttonsEnabled = true;
|
||||||
overlayStore.toast("Couldn't edit message");
|
if (responseOk(response))
|
||||||
}
|
close();
|
||||||
close();
|
|
||||||
};
|
};
|
||||||
const deleteMessage = async () => {
|
const deleteMessage = async () => {
|
||||||
buttonsEnabled = false;
|
buttonsEnabled = false;
|
||||||
const { ok } = await remoteSignal(methods.deleteMessage, message.id);
|
const res = await remoteSignal(methods.deleteMessage, message.id);
|
||||||
if (!ok) {
|
if (!responseOk(res)) {
|
||||||
overlayStore.toast("Couldn't delete message");
|
overlayStore.toast(`Couldn't delete message: ${getMessageFromResponse(res)}`);
|
||||||
}
|
}
|
||||||
close();
|
close();
|
||||||
};
|
};
|
||||||
|
@ -37,6 +38,8 @@
|
||||||
<span class="h4" slot="header">Edit Message</span>
|
<span class="h4" slot="header">Edit Message</span>
|
||||||
|
|
||||||
<svelte:fragment slot="content">
|
<svelte:fragment slot="content">
|
||||||
|
<RpcErrorDisplay response={response} />
|
||||||
|
<RpcErrorDisplay validationIndex={1} response={response} />
|
||||||
<label class="input-label">
|
<label class="input-label">
|
||||||
Content
|
Content
|
||||||
<input class="input full-width" minlength="1" bind:value={ messageContent } />
|
<input class="input full-width" minlength="1" bind:value={ messageContent } />
|
||||||
|
|
|
@ -1,29 +1,25 @@
|
||||||
<script>
|
<script>
|
||||||
import { overlayStore, OverlayType } from "../../stores";
|
import { overlayStore, OverlayType } from "../../stores";
|
||||||
import { remoteCall } from "../../request";
|
import { remoteCall, responseOk } from "../../request";
|
||||||
import { authWithToken } from "../../auth";
|
import { authWithToken } from "../../auth";
|
||||||
import { methods } from "../../request";
|
import { methods } from "../../request";
|
||||||
import Modal from "./Modal.svelte";
|
import Modal from "./Modal.svelte";
|
||||||
|
import RpcErrorDisplay from "../rpc/RpcErrorDisplay.svelte";
|
||||||
|
|
||||||
let username = "";
|
let username = "";
|
||||||
let password = "";
|
let password = "";
|
||||||
let buttonsEnabled = true;
|
let buttonsEnabled = true;
|
||||||
let pendingOtherOpen = false;
|
let pendingOtherOpen = false;
|
||||||
|
let response;
|
||||||
export let close = () => {};
|
export let close = () => {};
|
||||||
|
|
||||||
const login = async () => {
|
const login = async () => {
|
||||||
buttonsEnabled = false;
|
buttonsEnabled = false;
|
||||||
const { ok, json } = await remoteCall(methods.loginUser, username, password);
|
response = await remoteCall(methods.loginUser, username, password);
|
||||||
if (ok && json && json.token) {
|
if (responseOk(response) && response.data && response.data.token) {
|
||||||
authWithToken(json.token, true);
|
authWithToken(response.data.token, true);
|
||||||
} else {
|
} else {
|
||||||
if (json && json.code && json.code === 6002) { // 6002 is the code for bad login
|
|
||||||
overlayStore.toast("Invalid username or password");
|
|
||||||
} else {
|
|
||||||
overlayStore.toast("Couldn't log in");
|
|
||||||
}
|
|
||||||
buttonsEnabled = true;
|
buttonsEnabled = true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const createAccountInstead = () => {
|
const createAccountInstead = () => {
|
||||||
|
@ -47,6 +43,9 @@
|
||||||
<span class="h4" slot="header">Welcome back!</span>
|
<span class="h4" slot="header">Welcome back!</span>
|
||||||
|
|
||||||
<svelte:fragment slot="content">
|
<svelte:fragment slot="content">
|
||||||
|
<RpcErrorDisplay response={response} />
|
||||||
|
|
||||||
|
<RpcErrorDisplay validationIndex={0} response={response} />
|
||||||
<label class="input-label">
|
<label class="input-label">
|
||||||
Username
|
Username
|
||||||
<input class="input full-width" minlength="1" maxlength="32" bind:value={ username } />
|
<input class="input full-width" minlength="1" maxlength="32" bind:value={ username } />
|
||||||
|
@ -54,6 +53,7 @@
|
||||||
|
|
||||||
<div class="separator" />
|
<div class="separator" />
|
||||||
|
|
||||||
|
<RpcErrorDisplay validationIndex={1} response={response} />
|
||||||
<label class="input-label">
|
<label class="input-label">
|
||||||
Password
|
Password
|
||||||
<input class="input full-width" minlength="8" type="password" bind:value={ password } />
|
<input class="input full-width" minlength="8" type="password" bind:value={ password } />
|
||||||
|
@ -61,7 +61,7 @@
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
|
||||||
<svelte:fragment slot="footer">
|
<svelte:fragment slot="footer">
|
||||||
<button class="button modal-secondary-action" on:click="{ createAccountInstead }">Create an account instead</button>
|
<button class="button modal-secondary-action" on:click="{ createAccountInstead }" disabled="{ !buttonsEnabled }">Create an account instead</button>
|
||||||
<button class="button button-accent modal-primary-action" on:click="{ login }" disabled="{ !buttonsEnabled }">Log In</button>
|
<button class="button button-accent modal-primary-action" on:click="{ login }" disabled="{ !buttonsEnabled }">Log In</button>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
41
frontend/src/components/rpc/RpcErrorDisplay.svelte
Normal file
41
frontend/src/components/rpc/RpcErrorDisplay.svelte
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<script>
|
||||||
|
import { getErrorFromResponse } from "../../request";
|
||||||
|
|
||||||
|
export let response = null;
|
||||||
|
export let validationIndex = -1;
|
||||||
|
|
||||||
|
let message = null;
|
||||||
|
|
||||||
|
$: {
|
||||||
|
const error = getErrorFromResponse(response);
|
||||||
|
if (error) {
|
||||||
|
if (validationIndex >= 0) {
|
||||||
|
if (error.validationErrors) {
|
||||||
|
const found = error.validationErrors.find(e => e.index === validationIndex);
|
||||||
|
if (found) {
|
||||||
|
message = found.msg;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (error.validationErrors) {
|
||||||
|
message = null;
|
||||||
|
} else {
|
||||||
|
message = error.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
span {
|
||||||
|
color: var(--red-1);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
{#if message}
|
||||||
|
<span>{ message }</span>
|
||||||
|
{/if}
|
|
@ -29,34 +29,71 @@ export const methods = {
|
||||||
getCommunityChannels: withCacheable(method(405, true)),
|
getCommunityChannels: withCacheable(method(405, true)),
|
||||||
};
|
};
|
||||||
|
|
||||||
export function compatibleFetch(endpoint, options) {
|
export const RPCError = {
|
||||||
if (window.fetch && typeof window.fetch === "function") {
|
BAD_REQUEST: { code: 6000, message: "Bad request" },
|
||||||
return fetch(endpoint, options);
|
RPC_VALIDATION_ERROR: { code: 6001, message: "We couldn't validate this request" },
|
||||||
} else {
|
BAD_LOGIN_CREDENTIALS: { code: 6002, message: "Incorrect login credentials" },
|
||||||
return new Promise((resolve, reject) => {
|
BAD_AUTH: { code: 6003, message: "You're not authenticated" },
|
||||||
const req = new XMLHttpRequest();
|
NOT_FOUND: { code: 6004, message: "Not found" },
|
||||||
req.addEventListener("load", () => {
|
FORBIDDEN_DUE_TO_MISSING_PERMISSIONS: { code: 6005, message: "You don't have the required permissions to perform this action" },
|
||||||
resolve({
|
BAD_REQUEST_KEY: { code: 6006, message: "This request requires a special password, however, the password you provided was incorrect" },
|
||||||
status: req.status,
|
GOT_NO_DATABASE_DATA: { code: 7001, message: "Sorry, we couldn't process this request (server expected data from database, however got none)" },
|
||||||
ok: [200, 201, 204].includes(req.status),
|
FEATURE_DISABLED: { code: 7002, message: "This feature is disabled" },
|
||||||
json() {
|
INTERNAL_ERROR: { code: 7003, message: "Sorry, we couldn't process this request (internal server error)" },
|
||||||
return JSON.parse(req.responseText);
|
};
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
req.addEventListener("error", (e) => {
|
|
||||||
reject(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
req.open(options.method || "GET", endpoint);
|
export const RequestStatus = {
|
||||||
if (options.headers) {
|
OK: 0,
|
||||||
for (const [header, value] of Object.entries(options.headers)) {
|
NETWORK_EXCEPTION: 1,
|
||||||
req.setRequestHeader(header, value);
|
JSON_EXCEPTION: 2,
|
||||||
}
|
FAILURE_STATUS: 3,
|
||||||
}
|
RPC_ERROR: 4,
|
||||||
req.send(options.body);
|
INVARIANT_RPC_RESPONSE_COUNT: 5,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
export const RequestStatusToMessage = {
|
||||||
|
[RequestStatus.OK]: "",
|
||||||
|
[RequestStatus.NETWORK_EXCEPTION]: "We couldn't reach the server right now",
|
||||||
|
[RequestStatus.JSON_EXCEPTION]: "We couldn't process this request right now (server gave an invalid response, failed to parse as JSON)",
|
||||||
|
[RequestStatus.FAILURE_STATUS]: "We couldn't process this request right now (server gave a failure status code)",
|
||||||
|
[RequestStatus.RPC_ERROR]: "We couldn't process this request right now (RPC error)",
|
||||||
|
[RequestStatus.INVARIANT_RPC_RESPONSE_COUNT]: "We couldn't process this request right now (invalid RPC response)",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export function getErrorFromResponse(response) {
|
||||||
|
if (!response) return;
|
||||||
|
if (response.status === RequestStatus.OK) return;
|
||||||
|
console.log(response);
|
||||||
|
|
||||||
|
let message = RequestStatusToMessage[response.status];
|
||||||
|
if (!message) message = "Something went wrong (unknown request error)";
|
||||||
|
|
||||||
|
if (response.status === RequestStatus.RPC_ERROR) {
|
||||||
|
let rpcErrorMessage = Object.values(RPCError).find(({ code }) => code === response.data.code);
|
||||||
|
if (rpcErrorMessage) {
|
||||||
|
rpcErrorMessage = rpcErrorMessage.message;
|
||||||
|
} else {
|
||||||
|
rpcErrorMessage = "Something went wrong (unknown RPC error)";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.data.code === RPCError.RPC_VALIDATION_ERROR.code) {
|
||||||
|
return { message: rpcErrorMessage, validationErrors: response.data.errors };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { message: rpcErrorMessage };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { message };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMessageFromResponse(response) {
|
||||||
|
return getErrorFromResponse(response).message || "Something went wrong";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function responseOk(response) {
|
||||||
|
if (response.status !== RequestStatus.OK) return false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function doRequest(method, endpoint, auth=true, body=null) {
|
export default function doRequest(method, endpoint, auth=true, body=null) {
|
||||||
|
@ -83,31 +120,45 @@ export default function doRequest(method, endpoint, auth=true, body=null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let res;
|
||||||
try {
|
try {
|
||||||
const res = await compatibleFetch(endpoint, options);
|
res = await fetch(endpoint, options);
|
||||||
const json = res.status === 204 ? {} : await res.json();
|
} catch(o_O) {
|
||||||
|
|
||||||
return resolve({
|
return resolve({
|
||||||
json,
|
status: RequestStatus.NETWORK_EXCEPTION
|
||||||
ok: res.ok,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
return resolve({
|
|
||||||
json: null,
|
|
||||||
ok: false,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let json;
|
||||||
|
try {
|
||||||
|
json = res.status === 204 ? {} : await res.json();
|
||||||
|
} catch(o_O) {
|
||||||
|
return resolve({
|
||||||
|
status: RequestStatus.JSON_EXCEPTION
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
data: json,
|
||||||
|
status: res.ok ? RequestStatus.OK : RequestStatus.FAILURE_STATUS
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function remoteCall({methodId, requiresAuthentication, cacheable, _isSignal=false}, ...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, _isSignal);
|
const replies = await gateway.sendRPCRequest(calls, _isSignal);
|
||||||
const ok = Array.isArray(replies) && !(replies[0] && replies[0].code);
|
if (!Array.isArray(replies) || replies.length !== 1) {
|
||||||
|
return { status: RequestStatus.INVARIANT_RPC_RESPONSE_COUNT };
|
||||||
|
}
|
||||||
|
|
||||||
|
const reply = replies[0];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
json: ok ? replies[0] : null,
|
data: reply,
|
||||||
ok
|
status: reply && reply.code ? RequestStatus.RPC_ERROR : RequestStatus.OK
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,9 +168,21 @@ 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.json = response.ok ? response.json[0] : null;
|
if (response.status !== RequestStatus.OK) {
|
||||||
return response;
|
return { status: response.status };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(response.data) || response.data.length !== 1) {
|
||||||
|
return { status: RequestStatus.INVARIANT_RPC_RESPONSE_COUNT };
|
||||||
|
}
|
||||||
|
|
||||||
|
const reply = response.data[0];
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: reply,
|
||||||
|
status: reply && reply.code ? RequestStatus.RPC_ERROR : RequestStatus.OK
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function remoteSignal(method, ...args) {
|
export async function remoteSignal(method, ...args) {
|
||||||
|
@ -131,14 +194,22 @@ export async function remoteSignal(method, ...args) {
|
||||||
|
|
||||||
export async function remoteBlobUpload({methodId, requiresAuthentication, _isSignal=false}, blob) {
|
export async function remoteBlobUpload({methodId, requiresAuthentication, _isSignal=false}, blob) {
|
||||||
const calls = [[methodId, [0, blob.size]]];
|
const calls = [[methodId, [0, blob.size]]];
|
||||||
|
|
||||||
if (requiresAuthentication && gateway.authenticated) {
|
if (requiresAuthentication && gateway.authenticated) {
|
||||||
const replies = await gateway.sendRPCRequest(calls, _isSignal, blob);
|
const replies = await gateway.sendRPCRequest(calls, _isSignal, blob);
|
||||||
const ok = Array.isArray(replies) && !(replies[0] && replies[0].code);
|
if (!Array.isArray(replies) || replies.length !== 1) {
|
||||||
|
return { status: RequestStatus.INVARIANT_RPC_RESPONSE_COUNT };
|
||||||
|
}
|
||||||
|
|
||||||
|
const reply = replies[0];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
json: ok ? replies[0] : null,
|
data: reply,
|
||||||
ok
|
status: reply && reply.code ? RequestStatus.RPC_ERROR : RequestStatus.OK
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return { json: null, ok: false };
|
return {
|
||||||
|
status: RequestStatus.NETWORK_EXCEPTION
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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, remoteSignal } from "./request";
|
import { getMessageFromResponse, methods, remoteCall, remoteSignal, responseOk } from "./request";
|
||||||
import { getItem, setItem } from "./storage";
|
import { getItem, setItem } from "./storage";
|
||||||
|
|
||||||
const storeLog = logger("Store");
|
const storeLog = logger("Store");
|
||||||
|
@ -335,17 +335,17 @@ class MessageStore extends Store {
|
||||||
|
|
||||||
const oldestMessage = this.value[0];
|
const oldestMessage = this.value[0];
|
||||||
const res = await remoteCall(methods.getChannelMessages, this.channelId, null, oldestMessage ? oldestMessage.id : null);
|
const res = await remoteCall(methods.getChannelMessages, this.channelId, null, oldestMessage ? oldestMessage.id : null);
|
||||||
if (res.ok) {
|
if (responseOk(res)) {
|
||||||
if (res.json.length < 1)
|
if (res.data.length < 1)
|
||||||
return;
|
return;
|
||||||
if (beforeCommitToStore)
|
if (beforeCommitToStore)
|
||||||
beforeCommitToStore(res.json);
|
beforeCommitToStore(res.data);
|
||||||
res.json.reverse();
|
res.data.reverse();
|
||||||
this.value = res.json.concat(this.value);
|
this.value = res.data.concat(this.value);
|
||||||
this._recomputeMessages();
|
this._recomputeMessages();
|
||||||
this.updated();
|
this.updated();
|
||||||
} else {
|
} else {
|
||||||
overlayStore.toast("Messages failed to load");
|
overlayStore.toast(`Messages failed to load: ${getMessageFromResponse(res)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -877,11 +877,11 @@ export const sendMessageAction = createAction("sendMessageAction", async ({chann
|
||||||
|
|
||||||
const res = await remoteSignal(methods.createChannelMessage, channelId, content, optimisticMessageId, null);
|
const res = await remoteSignal(methods.createChannelMessage, channelId, content, optimisticMessageId, null);
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!responseOk(res)) {
|
||||||
messagesStoreForChannel.deleteMessage({
|
messagesStoreForChannel.deleteMessage({
|
||||||
id: optimisticMessageId
|
id: optimisticMessageId
|
||||||
});
|
});
|
||||||
overlayStore.toast("Couldn't send message");
|
overlayStore.toast(`Couldn't send message: ${getMessageFromResponse(res)}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue