Compare commits
3 Commits
b5f5001249
...
490b813f77
Author | SHA1 | Date | |
---|---|---|---|
490b813f77 | |||
adda4f8ce3 | |||
|
4e11051d9f |
@ -26,7 +26,6 @@ import {
|
|||||||
useTranslationContext,
|
useTranslationContext,
|
||||||
} from "@gnu-taler/web-util/browser";
|
} from "@gnu-taler/web-util/browser";
|
||||||
import { Fragment, VNode, h } from "preact";
|
import { Fragment, VNode, h } from "preact";
|
||||||
import { route } from "preact-router";
|
|
||||||
import { useMemo } from "preact/hooks";
|
import { useMemo } from "preact/hooks";
|
||||||
import { ApplicationReadyRoutes } from "./ApplicationReadyRoutes.js";
|
import { ApplicationReadyRoutes } from "./ApplicationReadyRoutes.js";
|
||||||
import { Loading } from "./components/exception/loading.js";
|
import { Loading } from "./components/exception/loading.js";
|
||||||
@ -41,8 +40,7 @@ import {
|
|||||||
import { ConfigContextProvider } from "./context/config.js";
|
import { ConfigContextProvider } from "./context/config.js";
|
||||||
import { useBackendConfig } from "./hooks/backend.js";
|
import { useBackendConfig } from "./hooks/backend.js";
|
||||||
import { strings } from "./i18n/strings.js";
|
import { strings } from "./i18n/strings.js";
|
||||||
import { ConnectionPage, LoginPage } from "./paths/login/index.js";
|
import { LoginPage } from "./paths/login/index.js";
|
||||||
import { LoginToken } from "./declaration.js";
|
|
||||||
|
|
||||||
export function Application(): VNode {
|
export function Application(): VNode {
|
||||||
return (
|
return (
|
||||||
@ -60,7 +58,6 @@ export function Application(): VNode {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function ApplicationStatusRoutes(): VNode {
|
function ApplicationStatusRoutes(): VNode {
|
||||||
const { changeBackend, selected: backendSelected } = useBackendContext();
|
|
||||||
const result = useBackendConfig();
|
const result = useBackendConfig();
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
|
|
||||||
@ -69,15 +66,6 @@ function ApplicationStatusRoutes(): VNode {
|
|||||||
: { currency: "unknown", version: "unknown" };
|
: { currency: "unknown", version: "unknown" };
|
||||||
const ctx = useMemo(() => ({ currency, version }), [currency, version]);
|
const ctx = useMemo(() => ({ currency, version }), [currency, version]);
|
||||||
|
|
||||||
if (!backendSelected) {
|
|
||||||
return (
|
|
||||||
<Fragment>
|
|
||||||
<NotConnectedAppMenu title="Welcome!" />
|
|
||||||
<ConnectionPage onConfirm={changeBackend} />
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.ok) {
|
if (!result.ok) {
|
||||||
if (result.loading) return <Loading />;
|
if (result.loading) return <Loading />;
|
||||||
if (
|
if (
|
||||||
@ -87,7 +75,13 @@ function ApplicationStatusRoutes(): VNode {
|
|||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<NotConnectedAppMenu title="Login" />
|
<NotConnectedAppMenu title="Login" />
|
||||||
<ConnectionPage onConfirm={changeBackend} />
|
<NotificationCard
|
||||||
|
notification={{
|
||||||
|
message: i18n.str`Checking the /config endpoint got authorization error`,
|
||||||
|
type: "ERROR",
|
||||||
|
description: `The /config endpoint of the backend server should be accesible`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -100,12 +94,11 @@ function ApplicationStatusRoutes(): VNode {
|
|||||||
<NotConnectedAppMenu title="Error" />
|
<NotConnectedAppMenu title="Error" />
|
||||||
<NotificationCard
|
<NotificationCard
|
||||||
notification={{
|
notification={{
|
||||||
message: i18n.str`Server not found`,
|
message: i18n.str`Could not find /config enpoint on this URL`,
|
||||||
type: "ERROR",
|
type: "ERROR",
|
||||||
description: `Check your url`,
|
description: `Check the URL or contact the system administrator.`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ConnectionPage onConfirm={changeBackend} />
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -119,7 +112,6 @@ function ApplicationStatusRoutes(): VNode {
|
|||||||
description: i18n.str`Got message "${result.message}" from ${result.info?.url}`,
|
description: i18n.str`Got message "${result.message}" from ${result.info?.url}`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ConnectionPage onConfirm={changeBackend} />
|
|
||||||
</Fragment>;
|
</Fragment>;
|
||||||
}
|
}
|
||||||
if (result.type === ErrorType.UNREADABLE) {
|
if (result.type === ErrorType.UNREADABLE) {
|
||||||
@ -132,7 +124,6 @@ function ApplicationStatusRoutes(): VNode {
|
|||||||
description: i18n.str`Got message "${result.message}" from ${result.info?.url}`,
|
description: i18n.str`Got message "${result.message}" from ${result.info?.url}`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ConnectionPage onConfirm={changeBackend} />
|
|
||||||
</Fragment>;
|
</Fragment>;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@ -145,7 +136,6 @@ function ApplicationStatusRoutes(): VNode {
|
|||||||
description: i18n.str`Got message "${result.message}" from ${result.info?.url}`,
|
description: i18n.str`Got message "${result.message}" from ${result.info?.url}`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ConnectionPage onConfirm={changeBackend} />
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -164,9 +154,7 @@ function ApplicationStatusRoutes(): VNode {
|
|||||||
description: i18n.str`Merchant backend server version ${result.data.version} is not compatible with the supported version ${SUPPORTED_VERSION}`,
|
description: i18n.str`Merchant backend server version ${result.data.version} is not compatible with the supported version ${SUPPORTED_VERSION}`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ConnectionPage onConfirm={changeBackend} />
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -26,13 +26,14 @@ import { Route, Router, route } from "preact-router";
|
|||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
import { InstanceRoutes } from "./InstanceRoutes.js";
|
import { InstanceRoutes } from "./InstanceRoutes.js";
|
||||||
import {
|
import {
|
||||||
|
NotConnectedAppMenu,
|
||||||
NotYetReadyAppMenu,
|
NotYetReadyAppMenu,
|
||||||
NotificationCard,
|
NotificationCard,
|
||||||
} from "./components/menu/index.js";
|
} from "./components/menu/index.js";
|
||||||
import { useBackendContext } from "./context/backend.js";
|
import { useBackendContext } from "./context/backend.js";
|
||||||
import { LoginToken } from "./declaration.js";
|
import { LoginToken } from "./declaration.js";
|
||||||
import { useBackendInstancesTestForAdmin } from "./hooks/backend.js";
|
import { useBackendInstancesTestForAdmin } from "./hooks/backend.js";
|
||||||
import { ConnectionPage, LoginPage } from "./paths/login/index.js";
|
import { LoginPage } from "./paths/login/index.js";
|
||||||
import { Settings } from "./paths/settings/index.js";
|
import { Settings } from "./paths/settings/index.js";
|
||||||
import { INSTANCE_ID_LOOKUP } from "./utils/constants.js";
|
import { INSTANCE_ID_LOOKUP } from "./utils/constants.js";
|
||||||
|
|
||||||
@ -42,10 +43,11 @@ import { INSTANCE_ID_LOOKUP } from "./utils/constants.js";
|
|||||||
*/
|
*/
|
||||||
export function ApplicationReadyRoutes(): VNode {
|
export function ApplicationReadyRoutes(): VNode {
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
const { url: backendURL, changeBackend } = useBackendContext()
|
|
||||||
const [unauthorized, setUnauthorized] = useState(false)
|
const [unauthorized, setUnauthorized] = useState(false)
|
||||||
const {
|
const {
|
||||||
|
url: backendURL,
|
||||||
updateToken,
|
updateToken,
|
||||||
|
alreadyTriedLogin,
|
||||||
} = useBackendContext();
|
} = useBackendContext();
|
||||||
|
|
||||||
function updateLoginStatus(token: LoginToken | undefined) {
|
function updateLoginStatus(token: LoginToken | undefined) {
|
||||||
@ -64,6 +66,15 @@ export function ApplicationReadyRoutes(): VNode {
|
|||||||
&& result.type === ErrorType.CLIENT
|
&& result.type === ErrorType.CLIENT
|
||||||
&& result.status === HttpStatusCode.Unauthorized;
|
&& result.status === HttpStatusCode.Unauthorized;
|
||||||
|
|
||||||
|
if (!alreadyTriedLogin) {
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<NotConnectedAppMenu title="Welcome!" />
|
||||||
|
<LoginPage onConfirm={updateToken} />
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (showSettings) {
|
if (showSettings) {
|
||||||
return <Fragment>
|
return <Fragment>
|
||||||
<NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="UI Settings" onLogout={clearTokenAndGoToRoot} isPasswordOk={false} />
|
<NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="UI Settings" onLogout={clearTokenAndGoToRoot} isPasswordOk={false} />
|
||||||
@ -100,7 +111,7 @@ export function ApplicationReadyRoutes(): VNode {
|
|||||||
type: "ERROR",
|
type: "ERROR",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ConnectionPage onConfirm={changeBackend} />
|
{/* <ConnectionPage onConfirm={changeBackend} /> */}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -205,7 +205,7 @@ export function InstanceRoutes({
|
|||||||
<NotificationCard
|
<NotificationCard
|
||||||
notification={{
|
notification={{
|
||||||
message: i18n.str`Access denied`,
|
message: i18n.str`Access denied`,
|
||||||
description: i18n.str`Redirecting to login page.`,
|
description: i18n.str`Session expired or password changed.`,
|
||||||
type: "ERROR",
|
type: "ERROR",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -49,7 +49,7 @@ export function Sidebar({
|
|||||||
isPasswordOk
|
isPasswordOk
|
||||||
}: Props): VNode {
|
}: Props): VNode {
|
||||||
const config = useConfigContext();
|
const config = useConfigContext();
|
||||||
const { url: backendURL, resetBackend } = useBackendContext()
|
const { url: backendURL } = useBackendContext()
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
const kycStatus = useInstanceKYCDetails();
|
const kycStatus = useInstanceKYCDetails();
|
||||||
const needKYC = kycStatus.ok && kycStatus.data.type === "redirect";
|
const needKYC = kycStatus.ok && kycStatus.data.type === "redirect";
|
||||||
@ -283,21 +283,8 @@ export function Sidebar({
|
|||||||
<i18n.Translate>Log out</i18n.Translate>
|
<i18n.Translate>Log out</i18n.Translate>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li> :
|
</li> : undefined
|
||||||
<li>
|
}
|
||||||
<a
|
|
||||||
class="has-icon is-state-info is-hoverable"
|
|
||||||
onClick={(): void => resetBackend()}
|
|
||||||
>
|
|
||||||
<span class="icon">
|
|
||||||
<i class="mdi mdi-logout default" />
|
|
||||||
</span>
|
|
||||||
<span class="menu-item-label">
|
|
||||||
<i18n.Translate>Change server</i18n.Translate>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
@ -19,56 +19,39 @@
|
|||||||
* @author Sebastian Javier Marchano (sebasjm)
|
* @author Sebastian Javier Marchano (sebasjm)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { useMemoryStorage } from "@gnu-taler/web-util/browser";
|
||||||
import { createContext, h, VNode } from "preact";
|
import { createContext, h, VNode } from "preact";
|
||||||
import { useContext, useState } from "preact/hooks";
|
import { useContext } from "preact/hooks";
|
||||||
import { LoginToken } from "../declaration.js";
|
import { LoginToken } from "../declaration.js";
|
||||||
import { useBackendDefaultToken, useBackendURL } from "../hooks/index.js";
|
import { useBackendDefaultToken, useBackendURL } from "../hooks/index.js";
|
||||||
import { buildStorageKey, useLocalStorage } from "@gnu-taler/web-util/browser";
|
|
||||||
import { codecForBoolean } from "@gnu-taler/taler-util";
|
|
||||||
|
|
||||||
interface BackendContextType {
|
interface BackendContextType {
|
||||||
url: string,
|
url: string,
|
||||||
selected: boolean;
|
alreadyTriedLogin: boolean;
|
||||||
token?: LoginToken;
|
token?: LoginToken;
|
||||||
updateToken: (token: LoginToken | undefined) => void;
|
updateToken: (token: LoginToken | undefined) => void;
|
||||||
changeBackend: (url: string) => void;
|
|
||||||
resetBackend: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const BackendContext = createContext<BackendContextType>({
|
const BackendContext = createContext<BackendContextType>({
|
||||||
url: "",
|
url: "",
|
||||||
selected: false,
|
alreadyTriedLogin: false,
|
||||||
token: undefined,
|
token: undefined,
|
||||||
updateToken: () => null,
|
updateToken: () => null,
|
||||||
changeBackend: () => null,
|
|
||||||
resetBackend: () => null,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const BACKEND_SELECTED = buildStorageKey("backend-selected", codecForBoolean());
|
|
||||||
|
|
||||||
function useBackendContextState(
|
function useBackendContextState(
|
||||||
defaultUrl?: string,
|
defaultUrl?: string,
|
||||||
): BackendContextType {
|
): BackendContextType {
|
||||||
const [url, changeBackend2] = useBackendURL(defaultUrl);
|
const [url] = useBackendURL(defaultUrl);
|
||||||
const [token, updateToken] = useBackendDefaultToken();
|
const [token, updateToken] = useBackendDefaultToken();
|
||||||
const {value, update} = useLocalStorage(BACKEND_SELECTED)
|
|
||||||
|
|
||||||
function changeBackend(s:string) {
|
console.log(JSON.stringify(token))
|
||||||
changeBackend2(s)
|
|
||||||
update(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetBackend() {
|
|
||||||
update(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
url,
|
url,
|
||||||
token,
|
token,
|
||||||
selected: value ?? false,
|
alreadyTriedLogin: token !== undefined,
|
||||||
updateToken,
|
updateToken,
|
||||||
changeBackend,
|
|
||||||
resetBackend
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,7 +345,7 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
|
|||||||
//and avoid network
|
//and avoid network
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
ok: true,
|
ok: true,
|
||||||
data: {orders:[]} as T,
|
data: { orders: [] } as T,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return requestHandler<T>(baseUrl, endpoint, { params, token });
|
return requestHandler<T>(baseUrl, endpoint, { params, token });
|
||||||
@ -398,7 +398,7 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
|
|||||||
//and avoid network
|
//and avoid network
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
ok: true,
|
ok: true,
|
||||||
data: {transfers:[]} as T,
|
data: { transfers: [] } as T,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (delta !== undefined) {
|
if (delta !== undefined) {
|
||||||
@ -424,7 +424,7 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
|
|||||||
//and avoid network
|
//and avoid network
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
ok: true,
|
ok: true,
|
||||||
data: {templates:[]} as T,
|
data: { templates: [] } as T,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (delta !== undefined) {
|
if (delta !== undefined) {
|
||||||
@ -450,7 +450,7 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
|
|||||||
//and avoid network
|
//and avoid network
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
ok: true,
|
ok: true,
|
||||||
data: {webhooks:[]} as T,
|
data: { webhooks: [] } as T,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (delta !== undefined) {
|
if (delta !== undefined) {
|
||||||
|
@ -36,7 +36,7 @@ function normalizeToken(r: string): AccessToken {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function LoginPage({ onConfirm }: Props): VNode {
|
export function LoginPage({ onConfirm }: Props): VNode {
|
||||||
const { url: backendURL, changeBackend, resetBackend } = useBackendContext();
|
const { url: backendURL } = useBackendContext();
|
||||||
const { admin, id } = useInstanceContext();
|
const { admin, id } = useInstanceContext();
|
||||||
const { requestNewLoginToken } = useCredentialsChecker();
|
const { requestNewLoginToken } = useCredentialsChecker();
|
||||||
const [token, setToken] = useState("");
|
const [token, setToken] = useState("");
|
||||||
@ -54,11 +54,7 @@ export function LoginPage({ onConfirm }: Props): VNode {
|
|||||||
} else {
|
} else {
|
||||||
onConfirm(undefined);
|
onConfirm(undefined);
|
||||||
}
|
}
|
||||||
}, [backendURL, id, token])
|
}, [id, token])
|
||||||
|
|
||||||
async function changeServer() {
|
|
||||||
resetBackend()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (admin && id !== "default") {
|
if (admin && id !== "default") {
|
||||||
//admin trying to access another instance
|
//admin trying to access another instance
|
||||||
@ -139,26 +135,7 @@ export function LoginPage({ onConfirm }: Props): VNode {
|
|||||||
style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }}
|
style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }}
|
||||||
>
|
>
|
||||||
<i18n.Translate>Please enter your access token.</i18n.Translate>
|
<i18n.Translate>Please enter your access token.</i18n.Translate>
|
||||||
<div class="field is-horizontal">
|
|
||||||
<div class="field-label is-normal">
|
|
||||||
<label class="label">URL</label>
|
|
||||||
</div>
|
|
||||||
<div class="field-body">
|
|
||||||
<div class="field">
|
|
||||||
<p class="control is-expanded">
|
|
||||||
<input
|
|
||||||
class="input"
|
|
||||||
type="text"
|
|
||||||
placeholder="set new url"
|
|
||||||
name="id"
|
|
||||||
value={backendURL}
|
|
||||||
disabled
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label is-normal">
|
<div class="field-label is-normal">
|
||||||
<label class="label">
|
<label class="label">
|
||||||
@ -194,10 +171,7 @@ export function LoginPage({ onConfirm }: Props): VNode {
|
|||||||
borderTop: 0,
|
borderTop: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AsyncButton onClick={changeServer}>
|
<div />
|
||||||
<i18n.Translate>Change server</i18n.Translate>
|
|
||||||
</AsyncButton>
|
|
||||||
|
|
||||||
<AsyncButton
|
<AsyncButton
|
||||||
type="is-info"
|
type="is-info"
|
||||||
onClick={doLogin}
|
onClick={doLogin}
|
||||||
@ -226,73 +200,3 @@ function AsyncButton({ onClick, disabled, type = "", children }: { type?: string
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function ConnectionPage({ onConfirm }: { onConfirm: (s: string) => void }): VNode {
|
|
||||||
const { url: backendURL } = useBackendContext()
|
|
||||||
|
|
||||||
const [error, setError] = useState<string>();
|
|
||||||
const [url, setURL] = useState(backendURL ?? "");
|
|
||||||
const { i18n } = useTranslationContext();
|
|
||||||
|
|
||||||
async function doConnect() {
|
|
||||||
const withHttp = url.startsWith("http") ? url : "https://" + url
|
|
||||||
|
|
||||||
onConfirm(withHttp)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class="columns is-centered" style={{ margin: "auto" }}>
|
|
||||||
<div class="column is-two-thirds ">
|
|
||||||
<div class="modal-card" style={{ width: "100%", margin: 0 }}>
|
|
||||||
<header
|
|
||||||
class="modal-card-head"
|
|
||||||
style={{ border: "1px solid", borderBottom: 0 }}
|
|
||||||
>
|
|
||||||
<p class="modal-card-title">{i18n.str`Connect to backend`}</p>
|
|
||||||
</header>
|
|
||||||
<section
|
|
||||||
class="modal-card-body"
|
|
||||||
style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }}
|
|
||||||
>
|
|
||||||
<i18n.Translate>Location of the backend server</i18n.Translate>
|
|
||||||
<div class="field is-horizontal">
|
|
||||||
<div class="field-label is-normal">
|
|
||||||
<label class="label">URL</label>
|
|
||||||
</div>
|
|
||||||
<div class="field-body">
|
|
||||||
<div class="field">
|
|
||||||
<p class="control is-expanded">
|
|
||||||
<input
|
|
||||||
class="input"
|
|
||||||
type="text"
|
|
||||||
placeholder="set new url"
|
|
||||||
name="id"
|
|
||||||
value={url ?? ""}
|
|
||||||
onKeyPress={(e) =>
|
|
||||||
e.keyCode === 13
|
|
||||||
? doConnect()
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
onInput={(e): void => setURL(e?.currentTarget.value)}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<footer
|
|
||||||
class="modal-card-foot "
|
|
||||||
style={{
|
|
||||||
justifyContent: "flex-end",
|
|
||||||
border: "1px solid",
|
|
||||||
borderTop: 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AsyncButton onClick={doConnect}>
|
|
||||||
<i18n.Translate>Connect</i18n.Translate>
|
|
||||||
</AsyncButton>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1291,10 +1291,12 @@ export async function getTransactions(
|
|||||||
const txNotPending = transactions.filter((x) => !isPending(x));
|
const txNotPending = transactions.filter((x) => !isPending(x));
|
||||||
|
|
||||||
const txCmp = (h1: Transaction, h2: Transaction) => {
|
const txCmp = (h1: Transaction, h2: Transaction) => {
|
||||||
const tsCmp = AbsoluteTime.cmp(
|
// Order transactions by timestamp. Newest transactions come first.
|
||||||
|
const tsCmp = -AbsoluteTime.cmp(
|
||||||
AbsoluteTime.fromPreciseTimestamp(h1.timestamp),
|
AbsoluteTime.fromPreciseTimestamp(h1.timestamp),
|
||||||
AbsoluteTime.fromPreciseTimestamp(h2.timestamp),
|
AbsoluteTime.fromPreciseTimestamp(h2.timestamp),
|
||||||
);
|
);
|
||||||
|
// If the timestamp is exactly the same, order by transaction type.
|
||||||
if (tsCmp === 0) {
|
if (tsCmp === 0) {
|
||||||
return Math.sign(txOrder[h1.type] - txOrder[h2.type]);
|
return Math.sign(txOrder[h1.type] - txOrder[h2.type]);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
/* eslint-disable no-undef */
|
/* eslint-disable no-undef */
|
||||||
|
|
||||||
function setupLiveReload(): void {
|
function setupLiveReload(): void {
|
||||||
|
const stopWs = localStorage.getItem("stop-ws")
|
||||||
|
if (!!stopWs) return;
|
||||||
const protocol = window.location.protocol === "http:" ? "ws:" : "wss:";
|
const protocol = window.location.protocol === "http:" ? "ws:" : "wss:";
|
||||||
const ws = new WebSocket(`${protocol}//${window.location.hostname}:${window.location.port}/ws`);
|
const ws = new WebSocket(`${protocol}//${window.location.hostname}:${window.location.port}/ws`);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user