mirror of
https://github.com/stoatchat/for-legacy-web.git
synced 2026-03-06 17:11:55 +00:00
chore: refactor account UI
This commit is contained in:
@@ -162,5 +162,8 @@
|
|||||||
"repository": "https://github.com/revoltchat/revite.git",
|
"repository": "https://github.com/revoltchat/revite.git",
|
||||||
"author": "Paul <paulmakles@gmail.com>",
|
"author": "Paul <paulmakles@gmail.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"packageManager": "yarn@3.2.0"
|
"packageManager": "yarn@3.2.0",
|
||||||
|
"resolutions": {
|
||||||
|
"@revoltchat/ui": "portal:../components"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,7 @@ import { ContextMenuTrigger } from "preact-context-menu";
|
|||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
import {
|
import { Preloader, UIProvider } from "@revoltchat/ui";
|
||||||
LinkProvider,
|
|
||||||
Preloader,
|
|
||||||
TextProvider,
|
|
||||||
TrigProvider,
|
|
||||||
} from "@revoltchat/ui";
|
|
||||||
|
|
||||||
import { hydrateState } from "../mobx/State";
|
import { hydrateState } from "../mobx/State";
|
||||||
|
|
||||||
@@ -20,6 +15,13 @@ import ModalRenderer from "./modals/ModalRenderer";
|
|||||||
import Client from "./revoltjs/RevoltClient";
|
import Client from "./revoltjs/RevoltClient";
|
||||||
import SyncManager from "./revoltjs/SyncManager";
|
import SyncManager from "./revoltjs/SyncManager";
|
||||||
|
|
||||||
|
const uiContext = {
|
||||||
|
Link,
|
||||||
|
Text: Text as any,
|
||||||
|
Trigger: ContextMenuTrigger,
|
||||||
|
emitAction: () => {},
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component provides all of the application's context layers.
|
* This component provides all of the application's context layers.
|
||||||
* @param param0 Provided children
|
* @param param0 Provided children
|
||||||
@@ -35,21 +37,17 @@ export default function Context({ children }: { children: Children }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Router basename={import.meta.env.BASE_URL}>
|
<Router basename={import.meta.env.BASE_URL}>
|
||||||
<LinkProvider value={Link}>
|
<UIProvider value={uiContext}>
|
||||||
<TextProvider value={Text as any}>
|
<Locale>
|
||||||
<TrigProvider value={ContextMenuTrigger}>
|
<Intermediate>
|
||||||
<Locale>
|
<Client>
|
||||||
<Intermediate>
|
{children}
|
||||||
<Client>
|
<SyncManager />
|
||||||
{children}
|
</Client>
|
||||||
<SyncManager />
|
</Intermediate>
|
||||||
</Client>
|
<ModalRenderer />
|
||||||
</Intermediate>
|
</Locale>
|
||||||
<ModalRenderer />
|
</UIProvider>
|
||||||
</Locale>
|
|
||||||
</TrigProvider>
|
|
||||||
</TextProvider>
|
|
||||||
</LinkProvider>
|
|
||||||
<Theme />
|
<Theme />
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,12 @@ import { Key, Keyboard } from "@styled-icons/boxicons-solid";
|
|||||||
import { API } from "revolt.js";
|
import { API } from "revolt.js";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import { useCallback, useEffect, useState } from "preact/hooks";
|
import {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useLayoutEffect,
|
||||||
|
useState,
|
||||||
|
} from "preact/hooks";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Category,
|
Category,
|
||||||
@@ -15,8 +20,6 @@ import {
|
|||||||
|
|
||||||
import { noopTrue } from "../../../lib/js";
|
import { noopTrue } from "../../../lib/js";
|
||||||
|
|
||||||
import { useApplicationState } from "../../../mobx/State";
|
|
||||||
|
|
||||||
import { ModalProps } from "../types";
|
import { ModalProps } from "../types";
|
||||||
|
|
||||||
const ICONS: Record<API.MFAMethod, React.FC<any>> = {
|
const ICONS: Record<API.MFAMethod, React.FC<any>> = {
|
||||||
@@ -34,12 +37,13 @@ function ResponseEntry({
|
|||||||
value?: API.MFAResponse;
|
value?: API.MFAResponse;
|
||||||
onChange: (v: API.MFAResponse) => void;
|
onChange: (v: API.MFAResponse) => void;
|
||||||
}) {
|
}) {
|
||||||
if (type === "Password") {
|
return (
|
||||||
return (
|
<>
|
||||||
<>
|
<Category compact>
|
||||||
<Category compact>
|
<Text id={`login.${type.toLowerCase()}`} />
|
||||||
<Text id={`login.${type.toLowerCase()}`} />
|
</Category>
|
||||||
</Category>
|
|
||||||
|
{type === "Password" && (
|
||||||
<InputBox
|
<InputBox
|
||||||
type="password"
|
type="password"
|
||||||
value={(value as { password: string })?.password}
|
value={(value as { password: string })?.password}
|
||||||
@@ -47,56 +51,51 @@ function ResponseEntry({
|
|||||||
onChange({ password: e.currentTarget.value })
|
onChange({ password: e.currentTarget.value })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</>
|
)}
|
||||||
);
|
</>
|
||||||
} else {
|
);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MFA ticket creation flow
|
* MFA ticket creation flow
|
||||||
*/
|
*/
|
||||||
export default function MFAFlow({
|
export default function MFAFlow({ onClose, ...props }: ModalProps<"mfa_flow">) {
|
||||||
callback,
|
|
||||||
onClose,
|
|
||||||
...props
|
|
||||||
}: ModalProps<"mfa_flow">) {
|
|
||||||
const state = useApplicationState();
|
|
||||||
|
|
||||||
const [methods, setMethods] = useState<API.MFAMethod[] | undefined>(
|
const [methods, setMethods] = useState<API.MFAMethod[] | undefined>(
|
||||||
props.state === "unknown" ? props.available_methods : undefined,
|
props.state === "unknown" ? props.available_methods : undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Current state of the modal
|
||||||
const [selectedMethod, setSelected] = useState<API.MFAMethod>();
|
const [selectedMethod, setSelected] = useState<API.MFAMethod>();
|
||||||
const [response, setResponse] = useState<API.MFAResponse>();
|
const [response, setResponse] = useState<API.MFAResponse>();
|
||||||
|
|
||||||
|
// Fetch available methods if they have not been provided.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!methods && props.state === "known") {
|
if (!methods && props.state === "known") {
|
||||||
props.client.api.get("/auth/mfa/methods").then(setMethods);
|
props.client.api.get("/auth/mfa/methods").then(setMethods);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Always select first available method if only one available.
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (methods && methods.length === 1) {
|
||||||
|
setSelected(methods[0]);
|
||||||
|
}
|
||||||
|
}, [methods]);
|
||||||
|
|
||||||
|
// Callback to generate a new ticket or send response back up the chain.
|
||||||
const generateTicket = useCallback(async () => {
|
const generateTicket = useCallback(async () => {
|
||||||
if (response) {
|
if (response) {
|
||||||
let ticket;
|
|
||||||
|
|
||||||
if (props.state === "known") {
|
if (props.state === "known") {
|
||||||
ticket = await props.client.api.put(
|
const ticket = await props.client.api.put(
|
||||||
"/auth/mfa/ticket",
|
"/auth/mfa/ticket",
|
||||||
response,
|
response,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
props.callback(ticket);
|
||||||
} else {
|
} else {
|
||||||
ticket = await state.config
|
props.callback(response);
|
||||||
.createClient()
|
|
||||||
.api.put("/auth/mfa/ticket", response, {
|
|
||||||
headers: {
|
|
||||||
"X-MFA-Ticket": props.ticket.token,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(ticket);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,8 +121,12 @@ export default function MFAFlow({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
palette: "plain",
|
palette: "plain",
|
||||||
children: "Back",
|
children:
|
||||||
onClick: () => setSelected(undefined),
|
methods!.length === 1 ? "Cancel" : "Back",
|
||||||
|
onClick: () =>
|
||||||
|
methods!.length === 1
|
||||||
|
? true
|
||||||
|
: void setSelected(undefined),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ export type Modal = {
|
|||||||
} & (
|
} & (
|
||||||
| ({
|
| ({
|
||||||
type: "mfa_flow";
|
type: "mfa_flow";
|
||||||
callback: (ticket: API.MFATicket) => void;
|
|
||||||
} & (
|
} & (
|
||||||
| {
|
| {
|
||||||
state: "known";
|
state: "known";
|
||||||
client: Client;
|
client: Client;
|
||||||
|
callback: (ticket: API.MFATicket) => void;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
state: "unknown";
|
state: "unknown";
|
||||||
available_methods: API.MFAMethod[];
|
available_methods: API.MFAMethod[];
|
||||||
ticket: API.MFATicket & { validated: false };
|
callback: (response: API.MFAResponse) => void;
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
| {
|
| {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { API } from "revolt.js";
|
|||||||
import { useApplicationState } from "../../../mobx/State";
|
import { useApplicationState } from "../../../mobx/State";
|
||||||
|
|
||||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||||
|
import { modalController } from "../../../context/modals";
|
||||||
|
|
||||||
import { Form } from "./Form";
|
import { Form } from "./Form";
|
||||||
|
|
||||||
@@ -43,14 +44,34 @@ export function FormLogin() {
|
|||||||
// This should be replaced in the future.
|
// This should be replaced in the future.
|
||||||
const client = state.config.createClient();
|
const client = state.config.createClient();
|
||||||
await client.fetchConfiguration();
|
await client.fetchConfiguration();
|
||||||
const session = await client.api.post("/auth/session/login", {
|
|
||||||
|
let session = await client.api.post("/auth/session/login", {
|
||||||
...data,
|
...data,
|
||||||
friendly_name,
|
friendly_name,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (session.result !== "Success") {
|
if (session.result === "MFA") {
|
||||||
alert("unsupported!");
|
const { allowed_methods } = session;
|
||||||
return;
|
let mfa_response: API.MFAResponse = await new Promise(
|
||||||
|
(callback) =>
|
||||||
|
modalController.push({
|
||||||
|
type: "mfa_flow",
|
||||||
|
state: "unknown",
|
||||||
|
available_methods: allowed_methods,
|
||||||
|
callback,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
session = await client.api.post("/auth/session/login", {
|
||||||
|
mfa_response,
|
||||||
|
mfa_ticket: session.ticket,
|
||||||
|
friendly_name,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (session.result === "MFA") {
|
||||||
|
// unreachable code
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const s = session;
|
const s = session;
|
||||||
|
|||||||
@@ -1,22 +1,19 @@
|
|||||||
import { At, Key, Block } from "@styled-icons/boxicons-regular";
|
import { At, Key, Block } from "@styled-icons/boxicons-regular";
|
||||||
import {
|
import { Envelope, Lock, Trash, Pencil } from "@styled-icons/boxicons-solid";
|
||||||
Envelope,
|
|
||||||
HelpCircle,
|
|
||||||
Lock,
|
|
||||||
Trash,
|
|
||||||
Pencil,
|
|
||||||
} from "@styled-icons/boxicons-solid";
|
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useHistory } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { API } from "revolt.js";
|
|
||||||
|
|
||||||
import styles from "./Panes.module.scss";
|
import styles from "./Panes.module.scss";
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import { useContext, useEffect, useState } from "preact/hooks";
|
import { useContext, useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
import { Button, CategoryButton, LineDivider, Tip } from "@revoltchat/ui";
|
import {
|
||||||
|
AccountDetail,
|
||||||
import { stopPropagation } from "../../../lib/stopPropagation";
|
CategoryButton,
|
||||||
|
Column,
|
||||||
|
HiddenValue,
|
||||||
|
Tip,
|
||||||
|
} from "@revoltchat/ui";
|
||||||
|
|
||||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||||
import { modalController } from "../../../context/modals";
|
import { modalController } from "../../../context/modals";
|
||||||
@@ -27,26 +24,14 @@ import {
|
|||||||
useClient,
|
useClient,
|
||||||
} from "../../../context/revoltjs/RevoltClient";
|
} from "../../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
import Tooltip from "../../../components/common/Tooltip";
|
|
||||||
import UserIcon from "../../../components/common/user/UserIcon";
|
|
||||||
|
|
||||||
export const Account = observer(() => {
|
export const Account = observer(() => {
|
||||||
const { openScreen, writeClipboard } = useIntermediate();
|
const { openScreen } = useIntermediate();
|
||||||
const logOut = useContext(LogOutContext);
|
const logOut = useContext(LogOutContext);
|
||||||
const status = useContext(StatusContext);
|
const status = useContext(StatusContext);
|
||||||
|
|
||||||
const client = useClient();
|
const client = useClient();
|
||||||
|
|
||||||
const [email, setEmail] = useState("...");
|
const [email, setEmail] = useState("...");
|
||||||
const [revealEmail, setRevealEmail] = useState(false);
|
|
||||||
const [profile, setProfile] = useState<undefined | API.UserProfile>(
|
|
||||||
undefined,
|
|
||||||
);
|
|
||||||
const history = useHistory();
|
|
||||||
|
|
||||||
function switchPage(to: string) {
|
|
||||||
history.replace(`/settings/${to}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (email === "..." && status === ClientStatus.ONLINE) {
|
if (email === "..." && status === ClientStatus.ONLINE) {
|
||||||
@@ -54,124 +39,48 @@ export const Account = observer(() => {
|
|||||||
.get("/auth/account/")
|
.get("/auth/account/")
|
||||||
.then((account) => setEmail(account.email));
|
.then((account) => setEmail(account.email));
|
||||||
}
|
}
|
||||||
|
}, [client, email, status]);
|
||||||
if (profile === undefined && status === ClientStatus.ONLINE) {
|
|
||||||
client
|
|
||||||
.user!.fetchProfile()
|
|
||||||
.then((profile) => setProfile(profile ?? {}));
|
|
||||||
}
|
|
||||||
}, [client, email, profile, status]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.user}>
|
<div className={styles.user}>
|
||||||
<div className={styles.banner}>
|
<Column group>
|
||||||
<div className={styles.container}>
|
<AccountDetail user={client.user!} />
|
||||||
<UserIcon
|
</Column>
|
||||||
className={styles.avatar}
|
|
||||||
target={client.user!}
|
{(
|
||||||
size={72}
|
[
|
||||||
onClick={() => switchPage("profile")}
|
["username", client.user!.username, At],
|
||||||
/>
|
["email", email, Envelope],
|
||||||
<div className={styles.userDetail}>
|
["password", "•••••••••", Key],
|
||||||
<div className={styles.userContainer}>
|
] as const
|
||||||
<UserIcon
|
).map(([field, value, Icon]) => (
|
||||||
className={styles.tinyavatar}
|
<CategoryButton
|
||||||
target={client.user!}
|
key={field}
|
||||||
size={25}
|
icon={<Icon size={24} />}
|
||||||
onClick={() => switchPage("profile")}
|
description={
|
||||||
/>
|
field === "email" ? (
|
||||||
<div className={styles.username}>
|
<HiddenValue
|
||||||
@{client.user!.username}
|
value={value}
|
||||||
</div>
|
placeholder={"•••••••••••@••••••.•••"}
|
||||||
</div>
|
/>
|
||||||
<div className={styles.userid}>
|
) : (
|
||||||
<Tooltip
|
value
|
||||||
content={
|
)
|
||||||
<Text id="app.settings.pages.account.unique_id" />
|
}
|
||||||
}>
|
account
|
||||||
<HelpCircle size={16} />
|
action={<Pencil size={20} />}
|
||||||
</Tooltip>
|
onClick={() =>
|
||||||
<Tooltip content={<Text id="app.special.copy" />}>
|
openScreen({
|
||||||
<a
|
id: "modify_account",
|
||||||
onClick={() =>
|
field,
|
||||||
writeClipboard(client.user!._id)
|
})
|
||||||
}>
|
}>
|
||||||
{client.user!._id}
|
<Text id={`login.${field}`} />
|
||||||
</a>
|
</CategoryButton>
|
||||||
</Tooltip>
|
))}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
onClick={() => switchPage("profile")}
|
|
||||||
palette="secondary">
|
|
||||||
<Text id="app.settings.pages.profile.edit_profile" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{(
|
|
||||||
[
|
|
||||||
[
|
|
||||||
"username",
|
|
||||||
client.user!.username,
|
|
||||||
<At key="at" size={24} />,
|
|
||||||
],
|
|
||||||
["email", email, <Envelope key="envelope" size={24} />],
|
|
||||||
["password", "•••••••••", <Key key="key" size={24} />],
|
|
||||||
] as const
|
|
||||||
).map(([field, value, icon]) => (
|
|
||||||
<CategoryButton
|
|
||||||
key={field}
|
|
||||||
icon={icon}
|
|
||||||
description={
|
|
||||||
field === "email" ? (
|
|
||||||
revealEmail ? (
|
|
||||||
<>
|
|
||||||
{value}{" "}
|
|
||||||
<a
|
|
||||||
style={{ fontSize: "13px" }}
|
|
||||||
onClick={(ev) =>
|
|
||||||
stopPropagation(
|
|
||||||
ev,
|
|
||||||
setRevealEmail(false),
|
|
||||||
)
|
|
||||||
}>
|
|
||||||
<Text id="app.special.modals.actions.hide" />
|
|
||||||
</a>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
•••••••••••@••••••.•••{" "}
|
|
||||||
<a
|
|
||||||
style={{ fontSize: "13px" }}
|
|
||||||
onClick={(ev) =>
|
|
||||||
stopPropagation(
|
|
||||||
ev,
|
|
||||||
setRevealEmail(true),
|
|
||||||
)
|
|
||||||
}>
|
|
||||||
<Text id="app.special.modals.actions.reveal" />
|
|
||||||
</a>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
account
|
|
||||||
action={<Pencil size={20} />}
|
|
||||||
onClick={() =>
|
|
||||||
openScreen({
|
|
||||||
id: "modify_account",
|
|
||||||
field,
|
|
||||||
})
|
|
||||||
}>
|
|
||||||
<Text id={`login.${field}`} />
|
|
||||||
</CategoryButton>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<h3>
|
<h3>
|
||||||
<Text id="app.settings.pages.account.2fa.title" />
|
<Text id="app.settings.pages.account.2fa.title" />
|
||||||
</h3>
|
</h3>
|
||||||
@@ -197,10 +106,13 @@ export const Account = observer(() => {
|
|||||||
action="chevron">
|
action="chevron">
|
||||||
View my backup codes
|
View my backup codes
|
||||||
</CategoryButton>*/}
|
</CategoryButton>*/}
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<h3>
|
<h3>
|
||||||
<Text id="app.settings.pages.account.manage.title" />
|
<Text id="app.settings.pages.account.manage.title" />
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<h5>
|
<h5>
|
||||||
<Text id="app.settings.pages.account.manage.description" />
|
<Text id="app.settings.pages.account.manage.description" />
|
||||||
</h5>
|
</h5>
|
||||||
@@ -227,6 +139,7 @@ export const Account = observer(() => {
|
|||||||
}>
|
}>
|
||||||
<Text id="app.settings.pages.account.manage.disable" />
|
<Text id="app.settings.pages.account.manage.disable" />
|
||||||
</CategoryButton>
|
</CategoryButton>
|
||||||
|
|
||||||
<CategoryButton
|
<CategoryButton
|
||||||
icon={<Trash size={24} color="var(--error)" />}
|
icon={<Trash size={24} color="var(--error)" />}
|
||||||
description={
|
description={
|
||||||
@@ -250,13 +163,14 @@ export const Account = observer(() => {
|
|||||||
}>
|
}>
|
||||||
<Text id="app.settings.pages.account.manage.delete" />
|
<Text id="app.settings.pages.account.manage.delete" />
|
||||||
</CategoryButton>
|
</CategoryButton>
|
||||||
|
|
||||||
<Tip>
|
<Tip>
|
||||||
<span>
|
<span>
|
||||||
<Text id="app.settings.tips.account.a" />
|
<Text id="app.settings.tips.account.a" />
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
<a onClick={() => switchPage("profile")}>
|
<Link to="/settings/profile" replace>
|
||||||
<Text id="app.settings.tips.account.b" />
|
<Text id="app.settings.tips.account.b" />
|
||||||
</a>
|
</Link>
|
||||||
</Tip>
|
</Tip>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2220,9 +2220,9 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@revoltchat/ui@npm:1.0.39":
|
"@revoltchat/ui@portal:../components::locator=client%40workspace%3A.":
|
||||||
version: 1.0.39
|
version: 0.0.0-use.local
|
||||||
resolution: "@revoltchat/ui@npm:1.0.39"
|
resolution: "@revoltchat/ui@portal:../components::locator=client%40workspace%3A."
|
||||||
dependencies:
|
dependencies:
|
||||||
"@styled-icons/boxicons-logos": ^10.38.0
|
"@styled-icons/boxicons-logos": ^10.38.0
|
||||||
"@styled-icons/boxicons-regular": ^10.38.0
|
"@styled-icons/boxicons-regular": ^10.38.0
|
||||||
@@ -2235,9 +2235,8 @@ __metadata:
|
|||||||
react-device-detect: "*"
|
react-device-detect: "*"
|
||||||
react-virtuoso: "*"
|
react-virtuoso: "*"
|
||||||
revolt.js: "*"
|
revolt.js: "*"
|
||||||
checksum: 0376ef1e6c90a139da613a0b76d498327c7bad63941d02eb27b9d5b8208f09c01fb45330fc4e0643554a298beee416814dd41fd9992750378491450c6f773ee0
|
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: soft
|
||||||
|
|
||||||
"@rollup/plugin-babel@npm:^5.2.0":
|
"@rollup/plugin-babel@npm:^5.2.0":
|
||||||
version: 5.3.0
|
version: 5.3.0
|
||||||
|
|||||||
Reference in New Issue
Block a user