38 Commits

Author SHA1 Message Date
Lea
fe0d0501fa fix: delete gigachad 2023-08-22 15:25:10 +02:00
Paul Makles
bc604c0157 chore: bump language submodule 2023-08-01 23:10:06 +01:00
Paul Makles
8c183b530b feat: add admin shortcuts for convenience 2023-08-01 23:09:50 +01:00
Paul Makles
9bbc22b091 chore: update lang submodule; disable typechecking for docker build 2023-07-06 19:01:29 +01:00
Paul Makles
0d5ffb4df8 chore: update alerts to use new health service 2023-07-06 19:00:30 +01:00
Paul Makles
9d83d7cc1c chore: bump language submodule 2023-07-02 08:46:57 +01:00
FluffyCookie000
534b9227de fix: usernames are evolving blog post link (#895) 2023-07-02 08:46:34 +01:00
Paul Makles
5363ff2572 feat: raise katex limit to 512, upgrade dependencies 2023-06-18 10:22:01 +02:00
Paul Makles
3f1c2be709 chore: update language submodule 2023-06-17 16:11:32 +01:00
Paul Makles
04027658e6 feat: add more validation to KaTeX in markdown 2023-06-17 16:11:22 +01:00
Paul Makles
f1594a7b36 chore: show error on modify account 2023-06-15 18:43:36 +01:00
Paul Makles
fa0fcedce5 chore: we cooking now frfr 2023-06-13 22:04:14 +01:00
Paul Makles
70f5e6fc7e chore: monkey patch ulid parse error 2023-06-12 09:58:13 +01:00
Paul Makles
9b9ec867da chore: update typing indicator to at least use display name 2023-06-12 09:52:00 +01:00
Paul Makles
fead4ca879 feat: always show link warning for masked links 2023-06-12 09:51:17 +01:00
Paul Makles
449eee006d fix: copy full username from user profile 2023-06-11 22:26:56 +01:00
Paul Makles
cf691a8462 fix: use display name where available for friends 2023-06-11 22:22:52 +01:00
Paul Makles
c5ca579c95 chore: add helpful hint for new username format 2023-06-11 19:00:34 +01:00
Paul Makles
636367faea chore: strip emoji picker experiment
chore: use discrim
2023-06-11 18:36:07 +01:00
Paul Makles
f20ada7c49 feat: add changelog entry in preparation for update 2023-06-11 17:31:22 +01:00
Paul Makles
75d07ffe0f chore: move badges to top half of user profile 2023-06-11 13:02:38 +01:00
Paul Makles
5b600bec20 feat: back port discriminators and display names 2023-06-11 12:44:05 +01:00
temptrash
86218a7272 feat: swap pomelo usernames for discriminators 2023-06-10 15:01:12 +02:00
Paul Makles
9bc052ea87 chore: don't update the default .env file 2023-06-03 19:29:50 +01:00
Paul Makles
1862db89a3 feat: add support for webhooks
fix: removing reactions crashes client
2023-06-03 19:29:14 +01:00
Paul Makles
00708bb8f4 feat: include message context (if any) when reporting user 2023-05-31 12:06:31 +01:00
Paul Makles
7e87265ccb chore: update lang submodule 2023-05-31 11:50:35 +01:00
kate
d55d86aa3c fix: comment out link for nightly desktop build (#882) 2023-05-06 19:19:45 +01:00
Paul Makles
c426b6c184 chore: bump components and lang submodules 2023-04-21 21:12:20 +01:00
Paul Makles
4d58ff06f3 fix: weird typing errors that cropped up 2023-04-21 21:05:29 +01:00
Paul Makles
9f9902ffb1 fix: reset member sidebar fetch status when we become ready 2023-04-21 20:52:23 +01:00
Paul Makles
c13896244c chore: update components submodule 2023-03-18 22:22:02 +00:00
Paul Makles
e17e556ee6 chore: add notice to emoji menu 2023-03-14 21:57:47 +00:00
Paul Makles
7441569cfa chore: enable emoji picker by default 2023-03-14 21:48:19 +00:00
Paul Makles
eff1de7c78 chore: add notices throughout app about development 2023-03-14 21:48:13 +00:00
Paul Makles
b733e13ec2 merge: branch 'master' of https://github.com/revoltchat/revite 2023-03-14 21:16:16 +00:00
Declan Chidlow
e68ceac4f6 fix: overflow management in voice menus (#849) 2023-03-14 21:16:05 +00:00
Paul Makles
3f536589e5 chore: update languages submodule 2023-03-14 21:15:04 +00:00
53 changed files with 868 additions and 208 deletions

5
.env
View File

@@ -1,2 +1,5 @@
VITE_API_URL=https://api.revolt.chat # VITE_API_URL=https://api.revolt.chat
VITE_API_URL=https://app.revolt.chat/api
# VITE_API_URL=http://local.revolt.chat:8000
# VITE_API_URL=https://revolt.chat/api
VITE_THEMES_URL=https://themes.revolt.chat VITE_THEMES_URL=https://themes.revolt.chat

View File

@@ -6,7 +6,7 @@ COPY .env.build .env
RUN yarn install --frozen-lockfile RUN yarn install --frozen-lockfile
RUN yarn build:deps RUN yarn build:deps
RUN yarn typecheck # RUN yarn typecheck # lol no
RUN yarn build:highmem RUN yarn build:highmem
RUN yarn workspaces focus --production --all RUN yarn workspaces focus --production --all

2
external/lang vendored

View File

@@ -44,8 +44,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@revoltchat/rehype-katex": "6.0.3-patch.1",
"fs-extra": "^10.0.0", "fs-extra": "^10.0.0",
"klaw": "^3.0.0", "klaw": "^3.0.0",
"lottie-react": "^2.4.0",
"sirv-cli": "^1.0.14", "sirv-cli": "^1.0.14",
"vite": "^3.0.5" "vite": "^3.0.5"
}, },
@@ -136,7 +138,6 @@
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-scroll": "^1.8.2", "react-scroll": "^1.8.2",
"react-virtuoso": "^2.12.0", "react-virtuoso": "^2.12.0",
"rehype-katex": "^6.0.2",
"rehype-prism": "^2.1.3", "rehype-prism": "^2.1.3",
"rehype-react": "^7.1.1", "rehype-react": "^7.1.1",
"remark-breaks": "^3.0.2", "remark-breaks": "^3.0.2",

View File

@@ -1,10 +1,17 @@
import Lottie, { LottieRefCurrentProps } from "lottie-react";
import { JSX } from "preact";
import usernameAnim from "../controllers/modals/components/legacy/usernameUpdateLottie.json";
type Element = type Element =
| string | string
| { | {
type: "image"; type: "image";
src: string; src: string;
shadow?: boolean; shadow?: boolean;
}; }
| { type: "element"; element: JSX.Element };
export interface ChangelogPost { export interface ChangelogPost {
date: Date; date: Date;
@@ -43,6 +50,33 @@ export const changelogEntries: Record<number, ChangelogPost> = {
"If you want to learn more about how we're making Revolt safer for you, check out our new blog post :point_right: [https://revolt.chat/posts/improving-user-safety](https://revolt.chat/posts/improving-user-safety)", "If you want to learn more about how we're making Revolt safer for you, check out our new blog post :point_right: [https://revolt.chat/posts/improving-user-safety](https://revolt.chat/posts/improving-user-safety)",
], ],
}, },
3: {
date: new Date("2023-06-11T15:00:00.000Z"),
title: "Usernames are Changing",
content: [
{
type: "element",
element: (
<Lottie
animationData={usernameAnim}
style={{
background: "var(--secondary-background)",
borderRadius: "6px",
}}
/>
),
},
"Revolt has undergone a significant change to its username system, transitioning from unique username handles to a new system of display names and usernames with four-digit number tags called discriminators. The four-digit number tags serve as identifiers to differentiate users with the same username, allowing individuals to select desired usernames that reflect their identity.",
{
type: "element",
element: (
<a href="https://revolt.chat/posts/evolving-usernames">
Read more on our blog!
</a>
),
},
],
},
}; };
export const changelogEntryArray = Object.keys(changelogEntries).map( export const changelogEntryArray = Object.keys(changelogEntries).map(

View File

@@ -1941,8 +1941,6 @@ export const emojiDictionary = {
charle: "custom:charle.gif", charle: "custom:charle.gif",
sadge: "custom:sadge.webp", sadge: "custom:sadge.webp",
sus: "custom:sus.webp", sus: "custom:sus.webp",
chade: "custom:chade.gif",
gigachad: "custom:gigachad.webp",
sippy: "custom:sippy.webp", sippy: "custom:sippy.webp",
ayame_heart: "custom:ayame_heart.png", ayame_heart: "custom:ayame_heart.png",
catgirl_peek: "custom:catgirl_peek.png", catgirl_peek: "custom:catgirl_peek.png",

View File

@@ -5,7 +5,7 @@ import { useTriggerEvents } from "preact-context-menu";
import { memo } from "preact/compat"; import { memo } from "preact/compat";
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import { Category, Button } from "@revoltchat/ui"; import { Category } from "@revoltchat/ui";
import { internalEmit } from "../../../lib/eventEmitter"; import { internalEmit } from "../../../lib/eventEmitter";
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice"; import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
@@ -33,7 +33,7 @@ import InviteList from "./embed/EmbedInvite";
interface Props { interface Props {
attachContext?: boolean; attachContext?: boolean;
queued?: QueuedMessage; queued?: QueuedMessage;
message: MessageObject; message: MessageObject & { webhook: { name: string; avatar?: string } };
highlight?: boolean; highlight?: boolean;
contrast?: boolean; contrast?: boolean;
content?: Children; content?: Children;
@@ -63,6 +63,7 @@ const Message = observer(
? useTriggerEvents("Menu", { ? useTriggerEvents("Menu", {
user: message.author_id, user: message.author_id,
contextualChannel: message.channel_id, contextualChannel: message.channel_id,
contextualMessage: message._id,
// eslint-disable-next-line // eslint-disable-next-line
}) })
: undefined; : undefined;
@@ -137,6 +138,11 @@ const Message = observer(
<UserIcon <UserIcon
className="avatar" className="avatar"
url={message.generateMasqAvatarURL()} url={message.generateMasqAvatarURL()}
override={
message.webhook?.avatar
? `https://autumn.revolt.chat/avatars/${message.webhook.avatar}`
: undefined
}
target={user} target={user}
size={36} size={36}
onClick={handleUserClick} onClick={handleUserClick}
@@ -157,6 +163,7 @@ const Message = observer(
showServerIdentity showServerIdentity
onClick={handleUserClick} onClick={handleUserClick}
masquerade={message.masquerade!} masquerade={message.masquerade!}
override={message.webhook?.name}
{...userContext} {...userContext}
/> />
<MessageDetail <MessageDetail

View File

@@ -744,13 +744,11 @@ export default observer(({ channel }: Props) => {
onFocus={onFocus} onFocus={onFocus}
onBlur={onBlur} onBlur={onBlur}
/> />
{state.experiments.isEnabled("picker") && ( <Action>
<Action> <IconButton onClick={() => setPicker(!picker)}>
<IconButton onClick={() => setPicker(!picker)}> <HappyBeaming size={24} />
<HappyBeaming size={24} /> </IconButton>
</IconButton> </Action>
</Action>
)}
<Action> <Action>
<IconButton <IconButton
className={ className={

View File

@@ -119,19 +119,18 @@ export const MessageOverlayBar = observer(
</Tooltip> </Tooltip>
)} )}
{message.channel?.havePermission("React") && {message.channel?.havePermission("React") && (
state.experiments.isEnabled("picker") && ( <ReactionWrapper
<ReactionWrapper open={reactionsOpen}
open={reactionsOpen} setOpen={setReactionsOpen}
setOpen={setReactionsOpen} message={message}>
message={message}> <Tooltip content="React">
<Tooltip content="React"> <Entry>
<Entry> <HappyBeaming size={18} />
<HappyBeaming size={18} /> </Entry>
</Entry> </Tooltip>
</Tooltip> </ReactionWrapper>
</ReactionWrapper> )}
)}
{isAuthor && ( {isAuthor && (
<Tooltip content="Edit"> <Tooltip content="Edit">

View File

@@ -17,6 +17,7 @@ import { Bar } from "./JumpToBottom";
export default observer( export default observer(
({ channel, last_id }: { channel: Channel; last_id?: string }) => { ({ channel, last_id }: { channel: Channel; last_id?: string }) => {
const [hidden, setHidden] = useState(false); const [hidden, setHidden] = useState(false);
const [timeAgo, setTimeAgo] = useState("");
const hide = () => setHidden(true); const hide = () => setHidden(true);
useEffect(() => setHidden(false), [last_id]); useEffect(() => setHidden(false), [last_id]);
@@ -29,6 +30,14 @@ export default observer(
return () => document.removeEventListener("keydown", onKeyDown); return () => document.removeEventListener("keydown", onKeyDown);
}, []); }, []);
useEffect(() => {
if (last_id) {
try {
setTimeAgo(dayjs(decodeTime(last_id)).fromNow());
} catch (err) {}
}
}, [last_id]);
const renderer = getRenderer(channel); const renderer = getRenderer(channel);
const history = useHistory(); const history = useHistory();
if (renderer.state !== "RENDER") return null; if (renderer.state !== "RENDER") return null;
@@ -52,7 +61,7 @@ export default observer(
<Text <Text
id="app.main.channel.misc.new_messages" id="app.main.channel.misc.new_messages"
fields={{ fields={{
time_ago: dayjs(decodeTime(last_id)).fromNow(), time_ago: timeAgo,
}} }}
/> />
</div> </div>

View File

@@ -76,7 +76,9 @@ export default observer(({ channel }: Props) => {
if (users.length >= 5) { if (users.length >= 5) {
text = <Text id="app.main.channel.typing.several" />; text = <Text id="app.main.channel.typing.several" />;
} else if (users.length > 1) { } else if (users.length > 1) {
const userlist = [...users].map((x) => x!.username); const userlist = [...users].map(
(x) => x!.display_name ?? x!.username,
);
const user = userlist.pop(); const user = userlist.pop();
text = ( text = (
@@ -92,7 +94,9 @@ export default observer(({ channel }: Props) => {
text = ( text = (
<Text <Text
id="app.main.channel.typing.single" id="app.main.channel.typing.single"
fields={{ user: users[0]!.username }} fields={{
user: users[0]!.display_name ?? users[0]!.username,
}}
/> />
); );
} }

View File

@@ -142,7 +142,11 @@ export default function Embed({ embed }: Props) {
<a <a
onMouseDown={(ev) => onMouseDown={(ev) =>
(ev.button === 0 || ev.button === 1) && (ev.button === 0 || ev.button === 1) &&
modalController.openLink(embed.url!) modalController.openLink(
embed.url!,
undefined,
true,
)
} }
className={styles.title}> className={styles.title}>
{embed.title} {embed.title}
@@ -194,7 +198,8 @@ export default function Embed({ embed }: Props) {
modalController.push({ type: "image_viewer", embed }) modalController.push({ type: "image_viewer", embed })
} }
onMouseDown={(ev) => onMouseDown={(ev) =>
ev.button === 1 && modalController.openLink(embed.url) ev.button === 1 &&
modalController.openLink(embed.url, undefined, true)
} }
/> />
); );

View File

@@ -22,12 +22,11 @@ enum Badges {
const BadgesBase = styled.div` const BadgesBase = styled.div`
gap: 8px; gap: 8px;
display: flex; display: flex;
margin-top: 4px;
flex-direction: row; flex-direction: row;
img { img {
width: 32px; width: 24px;
height: 32px; height: 24px;
} }
`; `;
@@ -103,7 +102,7 @@ export default function UserBadges({ badges, uid }: Props) {
content={ content={
<Text id="app.special.popovers.user_profile.badges.responsible_disclosure" /> <Text id="app.special.popovers.user_profile.badges.responsible_disclosure" />
}> }>
<Shield size={32} color="gray" /> <Shield size={24} color="gray" />
</Tooltip> </Tooltip>
) : ( ) : (
<></> <></>

View File

@@ -29,9 +29,14 @@ const HeaderBase = styled.div`
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.new-name {
font-size: 16px;
font-weight: 600;
}
.username { .username {
cursor: pointer; cursor: pointer;
font-size: 16px; font-size: 13px;
font-weight: 600; font-weight: 600;
} }
@@ -50,6 +55,9 @@ export default observer(({ user }: Props) => {
return ( return (
<Header topBorder palette="secondary"> <Header topBorder palette="secondary">
<HeaderBase> <HeaderBase>
<div className="new-name">
{user.display_name ?? user.username}
</div>
<Localizer> <Localizer>
<Tooltip content={<Text id="app.special.copy_username" />}> <Tooltip content={<Text id="app.special.copy_username" />}>
<span <span
@@ -57,7 +65,9 @@ export default observer(({ user }: Props) => {
onClick={() => onClick={() =>
modalController.writeText(user.username) modalController.writeText(user.username)
}> }>
@{user.username} {user.username}
{"#"}
{user.discriminator}
</span> </span>
</Tooltip> </Tooltip>
</Localizer> </Localizer>

View File

@@ -14,6 +14,7 @@ import IconBase, { IconBaseProps } from "../IconBase";
type VoiceStatus = "muted" | "deaf"; type VoiceStatus = "muted" | "deaf";
interface Props extends IconBaseProps<User> { interface Props extends IconBaseProps<User> {
status?: boolean; status?: boolean;
override?: string;
voice?: VoiceStatus; voice?: VoiceStatus;
masquerade?: API.Masquerade; masquerade?: API.Masquerade;
showServerIdentity?: boolean; showServerIdentity?: boolean;
@@ -70,12 +71,15 @@ export default observer(
showServerIdentity, showServerIdentity,
masquerade, masquerade,
innerRef, innerRef,
override,
...svgProps ...svgProps
} = props; } = props;
let { url } = props; let { url } = props;
if (masquerade?.avatar) { if (masquerade?.avatar) {
url = client.proxyFile(masquerade.avatar); url = client.proxyFile(masquerade.avatar);
} else if (override) {
url = override;
} else if (!url) { } else if (!url) {
let override; let override;
if (target && showServerIdentity) { if (target && showServerIdentity) {

View File

@@ -39,6 +39,7 @@ type UsernameProps = Omit<
masquerade?: API.Masquerade; masquerade?: API.Masquerade;
showServerIdentity?: boolean | "both"; showServerIdentity?: boolean | "both";
override?: string;
innerRef?: Ref<any>; innerRef?: Ref<any>;
}; };
@@ -64,13 +65,18 @@ export const Username = observer(
masquerade, masquerade,
showServerIdentity, showServerIdentity,
innerRef, innerRef,
override,
...otherProps ...otherProps
}: UsernameProps) => { }: UsernameProps) => {
let username = user?.username; let username =
(user as unknown as { display_name: string })?.display_name ??
user?.username;
let color = masquerade?.colour; let color = masquerade?.colour;
let timed_out: Date | undefined; let timed_out: Date | undefined;
if (user && showServerIdentity) { if (override) {
username = override;
} else if (user && showServerIdentity) {
const { server } = useParams<{ server?: string }>(); const { server } = useParams<{ server?: string }>();
if (server) { if (server) {
const client = useClient(); const client = useClient();
@@ -146,6 +152,17 @@ export const Username = observer(
); );
} }
if (override) {
return (
<>
{el}
<BotBadge>
<Text id="app.main.channel.bot" />
</BotBadge>
</>
);
}
return el; return el;
}, },
); );

View File

@@ -1,5 +1,4 @@
import "katex/dist/katex.min.css"; import "katex/dist/katex.min.css";
import rehypeKatex from "rehype-katex";
import rehypePrism from "rehype-prism"; import rehypePrism from "rehype-prism";
import rehypeReact from "rehype-react"; import rehypeReact from "rehype-react";
import remarkBreaks from "remark-breaks"; import remarkBreaks from "remark-breaks";
@@ -14,6 +13,9 @@ import { createElement } from "preact";
import { memo } from "preact/compat"; import { memo } from "preact/compat";
import { useLayoutEffect, useMemo, useState } from "preact/hooks"; import { useLayoutEffect, useMemo, useState } from "preact/hooks";
// @ts-expect-error no typings
import rehypeKatex from "@revoltchat/rehype-katex";
import { MarkdownProps } from "./Markdown"; import { MarkdownProps } from "./Markdown";
import { handlers } from "./hast"; import { handlers } from "./hast";
import { RenderCodeblock } from "./plugins/Codeblock"; import { RenderCodeblock } from "./plugins/Codeblock";
@@ -146,6 +148,7 @@ const render = unified()
.use(rehypeKatex, { .use(rehypeKatex, {
maxSize: 10, maxSize: 10,
maxExpand: 0, maxExpand: 0,
maxLength: 512,
trust: false, trust: false,
strict: false, strict: false,
output: "html", output: "html",

View File

@@ -27,7 +27,11 @@ export function RenderAnchor({
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
onClick={(ev) => onClick={(ev) =>
modalController.openLink(href) && ev.preventDefault() modalController.openLink(
href,
undefined,
ev.currentTarget.innerText !== href,
) && ev.preventDefault()
} }
/> />
); );

View File

@@ -37,7 +37,13 @@ export default observer(() => {
{( {(
[ [
["username", client.user!.username, At], [
"username",
client.user!.username +
"#" +
client.user!.discriminator,
At,
],
["email", email, Envelope], ["email", email, Envelope],
["password", "•••••••••", Key], ["password", "•••••••••", Key],
] as const ] as const

View File

@@ -252,7 +252,7 @@ class ClientController {
// Start client lifecycle // Start client lifecycle
this.addSession( this.addSession(
{ {
session, session: session as never,
}, },
"new", "new",
); );

View File

@@ -3,6 +3,7 @@ import { API, Client } from "revolt.js";
import { state } from "../../mobx/State"; import { state } from "../../mobx/State";
import { resetMemberSidebarFetched } from "../../components/navigation/right/MemberSidebar";
import { modalController } from "../modals/ModalController"; import { modalController } from "../modals/ModalController";
/** /**
@@ -97,6 +98,7 @@ export default class Session {
* Called when the client signals it has received the Ready packet * Called when the client signals it has received the Ready packet
*/ */
private onReady() { private onReady() {
resetMemberSidebarFetched();
this.emit({ this.emit({
action: "SUCCESS", action: "SUCCESS",
}); });

View File

@@ -18,6 +18,7 @@ import { history } from "../../context/history";
import AddFriend from "./components/AddFriend"; import AddFriend from "./components/AddFriend";
import BanMember from "./components/BanMember"; import BanMember from "./components/BanMember";
import Changelog from "./components/Changelog"; import Changelog from "./components/Changelog";
import ChangelogUsernames from "./components/ChangelogUsernames";
import ChannelInfo from "./components/ChannelInfo"; import ChannelInfo from "./components/ChannelInfo";
import Clipboard from "./components/Clipboard"; import Clipboard from "./components/Clipboard";
import ConfirmLeave from "./components/ConfirmLeave"; import ConfirmLeave from "./components/ConfirmLeave";
@@ -39,6 +40,7 @@ import MFAEnableTOTP from "./components/MFAEnableTOTP";
import MFAFlow from "./components/MFAFlow"; import MFAFlow from "./components/MFAFlow";
import MFARecovery from "./components/MFARecovery"; import MFARecovery from "./components/MFARecovery";
import ModifyAccount from "./components/ModifyAccount"; import ModifyAccount from "./components/ModifyAccount";
import ModifyDisplayname from "./components/ModifyDisplayname";
import OutOfDate from "./components/OutOfDate"; import OutOfDate from "./components/OutOfDate";
import PendingFriendRequests from "./components/PendingFriendRequests"; import PendingFriendRequests from "./components/PendingFriendRequests";
import ReportContent from "./components/Report"; import ReportContent from "./components/Report";
@@ -206,12 +208,25 @@ class ModalControllerExtended extends ModalController<Modal> {
* Safely open external or internal link * Safely open external or internal link
* @param href Raw URL * @param href Raw URL
* @param trusted Whether we trust this link * @param trusted Whether we trust this link
* @param mismatch Whether to always open link warning
* @returns Whether to cancel default event * @returns Whether to cancel default event
*/ */
openLink(href?: string, trusted?: boolean) { openLink(href?: string, trusted?: boolean, mismatch?: boolean) {
const link = determineLink(href); const link = determineLink(href);
const settings = getApplicationState().settings; const settings = getApplicationState().settings;
if (mismatch) {
if (href) {
modalController.push({
type: "link_warning",
link: href,
callback: () => this.openLink(href, true) as true,
});
}
return true;
}
switch (link.type) { switch (link.type) {
case "navigate": { case "navigate": {
history.push(link.path); history.push(link.path);
@@ -280,4 +295,6 @@ export const modalController = new ModalControllerExtended({
user_profile: UserProfile, user_profile: UserProfile,
report: ReportContent, report: ReportContent,
report_success: ReportSuccess, report_success: ReportSuccess,
modify_displayname: ModifyDisplayname,
changelog_usernames: ChangelogUsernames,
}); });

View File

@@ -23,6 +23,7 @@ export default function AddFriend({ ...props }: ModalProps<"add_friend">) {
data={{ data={{
username: { username: {
field: "Username", field: "Username",
placeholder: "username#1234",
}, },
}} }}
callback={({ username }) => callback={({ username }) =>

View File

@@ -33,6 +33,8 @@ function RenderLog({ post }: { post: ChangelogPost }) {
{post.content.map((entry) => {post.content.map((entry) =>
typeof entry === "string" ? ( typeof entry === "string" ? (
<Markdown content={entry} /> <Markdown content={entry} />
) : entry.type === "element" ? (
entry.element
) : ( ) : (
<Image src={entry.src} shadow={entry.shadow} /> <Image src={entry.src} shadow={entry.shadow} />
), ),

View File

@@ -0,0 +1,146 @@
import Lottie, { LottieRefCurrentProps } from "lottie-react";
import { useEffect, useRef } from "preact/hooks";
import { Button, Column, InputBox, Modal, Row } from "@revoltchat/ui";
import { useClient } from "../../client/ClientController";
import { modalController } from "../ModalController";
import { ModalProps } from "../types";
import usernameAnim from "./legacy/usernameUpdateLottie.json";
/**
* Changelog: Username update
*/
export default function ChangelogUsernames({
onClose,
signal,
}: ModalProps<"changelog_usernames">) {
const client = useClient();
const lottieRef = useRef<LottieRefCurrentProps>();
useEffect(() => {
if (lottieRef.current) {
const timer = setTimeout(() => lottieRef.current!.play(), 2500);
return () => clearTimeout(timer);
}
}, [lottieRef]);
return (
<Modal onClose={onClose} signal={signal} transparent>
{
(
<Column gap="0">
<div
style={{
background: "black",
borderStartStartRadius: "12px",
borderStartEndRadius: "12px",
display: "grid",
placeItems: "center",
padding: "1.5em",
}}>
<Lottie
lottieRef={lottieRef as never}
animationData={usernameAnim}
autoplay={false}
loop={false}
style={{ width: "240px" }}
/>
</div>
<div
style={{
padding: "1em",
background: "var(--secondary-header)",
textAlign: "center",
}}>
<Column
gap="6px"
style={{
alignItems: "center",
}}>
<span
style={{
fontSize: "1.5em",
fontWeight: 700,
marginBottom: "12px",
}}>
Usernames are Changing
</span>
<span
style={{
color: "var(--secondary-foreground)",
fontSize: "0.9em",
fontWeight: 500,
}}>
We've changed how usernames work on Revolt.
Now you can set a display name in addition
to a username with a number tag for easier
sharing.
</span>
<span
style={{
color: "var(--secondary-foreground)",
fontSize: "0.9em",
fontWeight: 500,
}}>
Here's how your new username looks:
</span>
<InputBox
value={
client.user!.display_name ??
client.user!.username
}
style={{
maxWidth: "180px",
}}
disabled
/>
<InputBox
value={
client.user.username +
"#" +
client.user.discriminator
}
style={{
maxWidth: "180px",
}}
disabled
/>
<a
href="https://revolt.chat/posts/evolving-usernames"
target="_blank">
Read more about this change
</a>
</Column>
</div>
<Row
style={{
padding: "1em",
borderEndStartRadius: "12px",
borderEndEndRadius: "12px",
background: "var(--secondary-background)",
textAlign: "center",
justifyContent: "end",
}}>
<Button palette="plain" onClick={onClose}>
Skip for now
</Button>
<Button
palette="accent"
onClick={() => {
modalController.openLink(
"/settings/profile",
);
onClose();
}}>
Edit Profile
</Button>
</Row>
</Column>
) as any
}
</Modal>
);
}

View File

@@ -3,7 +3,7 @@ import { SubmitHandler, useForm } from "react-hook-form";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Category, Error, Modal } from "@revoltchat/ui"; import { Category, Error, InputBox, Modal, Tip } from "@revoltchat/ui";
import { noopTrue } from "../../../lib/js"; import { noopTrue } from "../../../lib/js";
@@ -120,14 +120,34 @@ export default function ModifyAccount({
/> />
)} )}
{field === "username" && ( {field === "username" && (
<FormField <div
type="username" style={{
name="new_username" display: "flex",
register={register} alignItems: "end",
showOverline gap: "8px",
error={errors.new_username?.message} }}>
disabled={processing} <div style={{ flexGrow: 1 }}>
/> <FormField
type="username"
name="new_username"
register={register}
showOverline
error={errors.new_username?.message}
disabled={processing}
/>
</div>
<div
style={{
flexShrink: 0,
width: "80px",
textAlign: "center",
}}>
<InputBox
disabled
value={"#" + client.user.discriminator}
/>
</div>
</div>
)} )}
<FormField <FormField
type="current_password" type="current_password"
@@ -141,11 +161,24 @@ export default function ModifyAccount({
<Category compact> <Category compact>
<Error <Error
error={ error={
<Text id="app.special.modals.account.failed" /> <>
<Text id="app.special.modals.account.failed" />{" "}
<Text id={`error.${error}`}>{error}</Text>
</>
} }
/> />
</Category> </Category>
)} )}
{field === "username" && (
<div style={{ marginTop: "8px" }}>
<Tip palette="warning">
Changing your username may change your number tag.
You can freely change the case of your username.
Your number tag may change at most once a day.
</Tip>
</div>
)}
</form> </form>
</Modal> </Modal>
); );

View File

@@ -0,0 +1,47 @@
import { Text } from "preact-i18n";
import { ModalForm } from "@revoltchat/ui";
import { useClient } from "../../client/ClientController";
import { ModalProps } from "../types";
/**
* Modify display name modal
*/
export default function ModifyDisplayname({
...props
}: ModalProps<"modify_displayname">) {
const client = useClient();
return (
<ModalForm
{...props}
title="Update display name"
schema={{
display_name: "text",
}}
defaults={{
display_name: (
client.user as unknown as { display_name: string }
)?.display_name as string,
}}
data={{
display_name: {
field: "Display Name",
},
}}
callback={({ display_name }) =>
display_name && display_name !== client.user!.username
? client.users.edit({
display_name,
} as never)
: client.users.edit({
remove: ["DisplayName"],
} as never)
}
submit={{
children: <Text id="app.special.modals.actions.save" />,
}}
/>
);
}

View File

@@ -41,6 +41,7 @@ const MessageContainer = styled.div`
*/ */
export default function ReportContent({ export default function ReportContent({
target, target,
messageId,
...props ...props
}: ModalProps<"report">) { }: ModalProps<"report">) {
const client = useClient(); const client = useClient();
@@ -124,6 +125,7 @@ export default function ReportContent({
? "User" ? "User"
: "Server", : "Server",
report_reason: reason as any, report_reason: reason as any,
message_id: messageId,
}, },
additional_context, additional_context,
}); });

View File

@@ -71,6 +71,10 @@ export function OnboardingModal({
error={error} error={error}
/> />
</div> </div>
<p>
You will be automatically assigned a number
tag which you can find from settings.
</p>
<Button palette="accent"> <Button palette="accent">
{"Looks good!"} {"Looks good!"}
</Button> </Button>

View File

@@ -46,13 +46,28 @@
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.username { .usernameDetail {
display: flex;
flex-direction: column;
margin-bottom: 4px;
}
.displayname {
font-size: 22px; font-size: 22px;
font-weight: 600; font-weight: 600;
} }
.username {
display: block;
font-size: 13px;
font-weight: 600;
width: fit-content;
color: var(--secondary-foreground);
}
.status { .status {
font-size: 13px; font-size: 13px;
color: var(--secondary-foreground);
> div { > div {
display: inline !important; display: inline !important;

View File

@@ -163,16 +163,33 @@ export const UserProfile = observer(
} }
/> />
<div className={styles.details}> <div className={styles.details}>
<Localizer> <div className={styles.usernameDetail}>
<span <span
className={styles.username} className={styles.displayname}
onClick={() => onClick={() =>
modalController.writeText(user.username) modalController.writeText(user.username)
}> }>
{"@"} {user.display_name ?? user.username}
{user.username}
</span> </span>
</Localizer> <span
className={styles.username}
onClick={() =>
modalController.writeText(
user.username +
"#" +
user.discriminator,
)
}>
<Localizer>
<Tooltip
content={
<Text id="app.special.copy_username" />
}>
{user.username}#{user.discriminator}
</Tooltip>
</Localizer>
</span>
</div>
{user.status?.text && ( {user.status?.text && (
<span className={styles.status}> <span className={styles.status}>
<UserStatus user={user} tooltip /> <UserStatus user={user} tooltip />
@@ -230,6 +247,21 @@ export const UserProfile = observer(
</IconButton> </IconButton>
)} )}
</div> </div>
{badges > 0 && (
<div
style={{
marginInline: "1em",
padding: "0.5em",
background: "var(--primary-background)",
borderRadius: "8px",
width: "fit-content",
backgroundColor:
"rgba(var(--primary-header-rgb), max(var(--min-opacity), 0.65))",
backdropFilter: "blur(20px)",
}}>
<UserBadges badges={badges} uid={user._id} />
</div>
)}
<div className={styles.tabs}> <div className={styles.tabs}>
<div <div
data-active={tab === "profile"} data-active={tab === "profile"}
@@ -261,10 +293,7 @@ export const UserProfile = observer(
</div> </div>
<div className={styles.content}> <div className={styles.content}>
{tab === "profile" && {tab === "profile" &&
(profile?.content || (profile?.content || flags > 0 || user.bot ? (
badges > 0 ||
flags > 0 ||
user.bot ? (
<div> <div>
{flags & 1 ? ( {flags & 1 ? (
/** ! FIXME: i18n this area */ /** ! FIXME: i18n this area */
@@ -314,17 +343,6 @@ export const UserProfile = observer(
</div> </div>
</> </>
) : undefined} ) : undefined}
{badges > 0 && (
<div className={styles.category}>
<Text id="app.special.popovers.user_profile.sub.badges" />
</div>
)}
{badges > 0 && (
<UserBadges
badges={badges}
uid={user._id}
/>
)}
{profile?.content && ( {profile?.content && (
<> <>
<div className={styles.category}> <div className={styles.category}>

File diff suppressed because one or more lines are too long

View File

@@ -9,6 +9,7 @@ export type Modal = {
| "create_group" | "create_group"
| "create_server" | "create_server"
| "custom_status" | "custom_status"
| "modify_displayname"
| "add_friend"; | "add_friend";
} }
| ({ | ({
@@ -40,6 +41,7 @@ export type Modal = {
type: "changelog"; type: "changelog";
initial?: number; initial?: number;
} }
| { type: "changelog_usernames" }
| { | {
type: "sign_out_sessions"; type: "sign_out_sessions";
client: Client; client: Client;
@@ -182,6 +184,7 @@ export type Modal = {
| { | {
type: "report"; type: "report";
target: Server | User | Message; target: Server | User | Message;
messageId?: string;
} }
| { | {
type: "report_success"; type: "report_success";

View File

@@ -20,7 +20,7 @@ import {
} from "preact-context-menu"; } from "preact-context-menu";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { IconButton, LineDivider } from "@revoltchat/ui"; import { Column, IconButton, LineDivider } from "@revoltchat/ui";
import { useApplicationState } from "../mobx/State"; import { useApplicationState } from "../mobx/State";
import { QueuedMessage } from "../mobx/stores/MessageQueue"; import { QueuedMessage } from "../mobx/stores/MessageQueue";
@@ -47,10 +47,12 @@ interface ContextMenuData {
unread?: boolean; unread?: boolean;
queued?: QueuedMessage; queued?: QueuedMessage;
contextualChannel?: string; contextualChannel?: string;
contextualMessage?: string;
} }
type Action = type Action =
| { action: "copy_id"; id: string } | { action: "copy_id"; id: string }
| { action: "admin"; id: string; type: "channel" | "message" | "user" }
| { action: "copy_message_link"; message: Message } | { action: "copy_message_link"; message: Message }
| { action: "copy_selection" } | { action: "copy_selection" }
| { action: "copy_text"; content: string } | { action: "copy_text"; content: string }
@@ -112,7 +114,7 @@ type Action =
key: string; key: string;
state?: NotificationState; state?: NotificationState;
} }
| { action: "report"; target: User | Server | Message }; | { action: "report"; target: User | Server | Message; messageId?: string };
// ! FIXME: I dare someone to re-write this // ! FIXME: I dare someone to re-write this
// Tip: This should just be split into separate context menus per logical area. // Tip: This should just be split into separate context menus per logical area.
@@ -132,6 +134,12 @@ export default function ContextMenus() {
case "copy_id": case "copy_id":
modalController.writeText(data.id); modalController.writeText(data.id);
break; break;
case "admin":
window.open(
`https://admin.revolt.chat/panel/inspect/${data.type}/${data.id}`,
"_blank",
);
break;
case "copy_message_link": case "copy_message_link":
{ {
let pathname = `/channel/${data.message.channel_id}/${data.message._id}`; let pathname = `/channel/${data.message.channel_id}/${data.message._id}`;
@@ -454,6 +462,7 @@ export default function ContextMenus() {
modalController.push({ modalController.push({
type: "report", type: "report",
target: data.target, target: data.target,
messageId: data.messageId,
}); });
break; break;
} }
@@ -478,6 +487,7 @@ export default function ContextMenus() {
queued, queued,
unread, unread,
contextualChannel: cxid, contextualChannel: cxid,
contextualMessage,
}: ContextMenuData) => { }: ContextMenuData) => {
const elements: Children[] = []; const elements: Children[] = [];
let lastDivider = false; let lastDivider = false;
@@ -493,11 +503,15 @@ export default function ContextMenus() {
elements.push( elements.push(
<MenuItem data={action} disabled={disabled}> <MenuItem data={action} disabled={disabled}>
<span style={{ color }}> <span style={{ color }}>
<Text {locale === "admin" ? (
id={`app.context_menu.${ "Open in Admin Panel"
locale ?? action.action ) : (
}`} <Text
/> id={`app.context_menu.${
locale ?? action.action
}`}
/>
)}
</span> </span>
{tip && <div className="tip">{tip}</div>} {tip && <div className="tip">{tip}</div>}
</MenuItem>, </MenuItem>,
@@ -682,6 +696,7 @@ export default function ContextMenus() {
{ {
action: "report", action: "report",
target: user, target: user,
messageId: contextualMessage,
}, },
"report_user", "report_user",
undefined, undefined,
@@ -1103,6 +1118,21 @@ export default function ContextMenus() {
} }
if (!hideIDButton) { if (!hideIDButton) {
if (state.experiments.isEnabled("admin_beta")) {
generateAction(
{
action: "admin",
id,
type: cid
? "channel"
: message
? "message"
: "user",
},
"admin",
);
}
generateAction( generateAction(
{ action: "copy_id", id }, { action: "copy_id", id },
cid cid
@@ -1138,7 +1168,21 @@ export default function ContextMenus() {
content={ content={
<Text id="app.special.copy_username" /> <Text id="app.special.copy_username" />
}> }>
@{user.username} <Column gap="0">
<span>
{user.display_name ??
user.username}
</span>
<span
style={{
fontSize: "0.8em",
}}>
{user.username}
{"#"}
{user.discriminator ??
"0000"}
</span>
</Column>
</Tooltip> </Tooltip>
</div> </div>
<div <div

View File

@@ -1,6 +1,6 @@
import { action, makeAutoObservable, runInAction } from "mobx"; import { action, makeAutoObservable, runInAction } from "mobx";
import { latestChangelog } from "../../assets/changelogs"; import { changelogEntries, latestChangelog } from "../../assets/changelogs";
import { modalController } from "../../controllers/modals/ModalController"; import { modalController } from "../../controllers/modals/ModalController";
import Persistent from "../interfaces/Persistent"; import Persistent from "../interfaces/Persistent";
import Store from "../interfaces/Store"; import Store from "../interfaces/Store";
@@ -58,10 +58,21 @@ export default class Changelog implements Store, Persistent<Data>, Syncable {
*/ */
checkForUpdates() { checkForUpdates() {
if (this.viewed < latestChangelog) { if (this.viewed < latestChangelog) {
modalController.push({ const expires = new Date(+changelogEntries[latestChangelog].date);
type: "changelog", expires.setDate(expires.getDate() + 7);
initial: latestChangelog,
}); if (+new Date() < +expires) {
if (latestChangelog === 3) {
modalController.push({
type: "changelog_usernames",
});
} else {
modalController.push({
type: "changelog",
initial: latestChangelog,
});
}
}
runInAction(() => { runInAction(() => {
this.viewed = latestChangelog; this.viewed = latestChangelog;

View File

@@ -10,7 +10,7 @@ import Store from "../interfaces/Store";
/** /**
* Union type of available experiments. * Union type of available experiments.
*/ */
export type Experiment = "dummy" | "offline_users" | "plugins" | "picker"; export type Experiment = "dummy" | "offline_users" | "plugins" | "admin_beta";
/** /**
* Currently active experiments. * Currently active experiments.
@@ -19,7 +19,7 @@ export const AVAILABLE_EXPERIMENTS: Experiment[] = [
"dummy", "dummy",
"offline_users", "offline_users",
"plugins", "plugins",
"picker", "admin_beta",
]; ];
/** /**
@@ -42,10 +42,10 @@ export const EXPERIMENTS: {
description: description:
"This will enable the experimental plugin API. Only touch this if you know what you're doing.", "This will enable the experimental plugin API. Only touch this if you know what you're doing.",
}, },
picker: { admin_beta: {
title: "Custom Emoji", title: "Admin Panel Shortcuts",
description: description:
"This will enable a work-in-progress emoji picker, custom emoji settings and a reaction picker.", "Adds context menu entries to quickly jump to Revolt Admin Panel. This is intended for use by the team.",
}, },
}; };

View File

@@ -28,7 +28,7 @@ const AppContainer = styled.div`
background-position: center center !important; background-position: center center !important;
`; `;
const StatusBar = styled.div` export const StatusBar = styled.div`
height: 40px; height: 40px;
width: 100%; width: 100%;
display: flex; display: flex;

View File

@@ -29,6 +29,7 @@ const VoiceBase = styled.div`
margin-top: 48px; margin-top: 48px;
padding: 20px; padding: 20px;
background: var(--secondary-background); background: var(--secondary-background);
flex-grow: 1;
.status { .status {
flex: 1 0; flex: 1 0;
@@ -54,8 +55,6 @@ const VoiceBase = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow-y: scroll;
.participants { .participants {
margin: 40px 20px; margin: 40px 20px;
justify-content: center; justify-content: center;

View File

@@ -136,7 +136,7 @@ export const Friend = observer(({ user }: Props) => {
})}> })}>
<UserIcon target={user} size={36} status /> <UserIcon target={user} size={36} status />
<div className={styles.name}> <div className={styles.name}>
<span>{user.username}</span> <span>{user.display_name ?? user.username}</span>
{subtext && <span className={styles.subtext}>{subtext}</span>} {subtext && <span className={styles.subtext}>{subtext}</span>}
</div> </div>
<div className={styles.actions}>{actions}</div> <div className={styles.actions}>{actions}</div>

View File

@@ -1,17 +1,19 @@
import { Twitter, Github, Mastodon } from "@styled-icons/boxicons-logos"; import { Twitter, Github, Mastodon } from "@styled-icons/boxicons-logos";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { Helmet } from "react-helmet"; import { Helmet } from "react-helmet";
import { Route, Switch } from "react-router-dom"; import { Link, Route, Switch } from "react-router-dom";
import styles from "./Login.module.scss"; import styles from "./Login.module.scss";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useApplicationState } from "../../mobx/State"; import { useApplicationState } from "../../mobx/State";
import LocaleSelector from "../../components/common/LocaleSelector";
import wideSVG from "/assets/wide.svg"; import wideSVG from "/assets/wide.svg";
import LocaleSelector from "../../components/common/LocaleSelector";
import { Titlebar } from "../../components/native/Titlebar"; import { Titlebar } from "../../components/native/Titlebar";
import { useSystemAlert } from "../../updateWorker";
import { StatusBar } from "../RevoltApp";
import { FormCreate } from "./forms/FormCreate"; import { FormCreate } from "./forms/FormCreate";
import { FormLogin } from "./forms/FormLogin"; import { FormLogin } from "./forms/FormLogin";
import { FormReset, FormSendReset } from "./forms/FormReset"; import { FormReset, FormSendReset } from "./forms/FormReset";
@@ -21,11 +23,31 @@ export default observer(() => {
const state = useApplicationState(); const state = useApplicationState();
const theme = state.settings.theme; const theme = state.settings.theme;
const alert = useSystemAlert();
return ( return (
<> <>
{window.isNative && !window.native.getConfig().frame && ( {window.isNative && !window.native.getConfig().frame && (
<Titlebar overlay /> <Titlebar overlay />
)} )}
{alert && (
<StatusBar>
<div className="title">{alert.text}</div>
<div className="actions">
{alert.actions?.map((action) =>
action.type === "internal" ? null : action.type ===
"external" ? (
<a
href={action.href}
target="_blank"
rel="noreferrer">
<div className="button">{action.text}</div>{" "}
</a>
) : null,
)}
</div>
</StatusBar>
)}
<div className={styles.login}> <div className={styles.login}>
<Helmet> <Helmet>
<meta <meta
@@ -77,17 +99,20 @@ export default observer(() => {
<div className={styles.socials}> <div className={styles.socials}>
<a <a
href="https://github.com/revoltchat" href="https://github.com/revoltchat"
target="_blank" rel="noreferrer"> target="_blank"
rel="noreferrer">
<Github size={24} /> <Github size={24} />
</a> </a>
<a <a
href="https://twitter.com/revoltchat" href="https://twitter.com/revoltchat"
target="_blank" rel="noreferrer"> target="_blank"
rel="noreferrer">
<Twitter size={24} /> <Twitter size={24} />
</a> </a>
<a <a
href="https://mastodon.social/@revoltchat" href="https://mastodon.social/@revoltchat"
target="_blank" rel="noreferrer"> target="_blank"
rel="noreferrer">
<Mastodon size={24} /> <Mastodon size={24} />
</a> </a>
</div> </div>
@@ -116,7 +141,8 @@ export default observer(() => {
<a <a
className={styles.attribution} className={styles.attribution}
href="https://unsplash.com/@fakurian" href="https://unsplash.com/@fakurian"
target="_blank" rel="noreferrer"> target="_blank"
rel="noreferrer">
<Text id="general.image_by" /> &lrm;@fakurian &rlm;· <Text id="general.image_by" /> &lrm;@fakurian &rlm;·
unsplash.com unsplash.com
</a> </a>

View File

@@ -79,7 +79,6 @@ export default observer(() => {
id: "emojis", id: "emojis",
icon: <HappyBeaming size={20} />, icon: <HappyBeaming size={20} />,
title: <Text id="app.settings.server_pages.emojis.title" />, title: <Text id="app.settings.server_pages.emojis.title" />,
hidden: !state.experiments.isEnabled("picker"),
}, },
{ {
category: ( category: (

View File

@@ -80,10 +80,15 @@ const AccountHeader = styled.div`
font-size: 12px; font-size: 12px;
gap: 2px; gap: 2px;
> span { .new {
font-size: 20px; font-size: 20px;
font-weight: 600; font-weight: 600;
} }
.full {
font-size: 14px;
font-weight: 600;
}
} }
} }
@@ -336,7 +341,15 @@ export default observer(() => {
onClick={() => openContextMenu("Status")} onClick={() => openContextMenu("Status")}
/> />
<div className="details"> <div className="details">
<Username user={client.user!} prefixAt /> <span className="new">
{client.user.display_name ??
client.user.username}
</span>
<span className="full">
{client.user.username}
{"#"}
{client.user.discriminator}
</span>
<UserStatus user={client.user!} /> <UserStatus user={client.user!} />
</div> </div>
</div> </div>

View File

@@ -94,6 +94,33 @@ export function Audio() {
return ( return (
<> <>
<div className={styles.audio}> <div className={styles.audio}>
<Tip palette="warning">
<span>
We are currently{" "}
<a
style={{ color: "inherit", fontWeight: "600" }}
href="https://github.com/revoltchat/frontend/issues/14"
target="_blank"
rel="noreferrer">
rebuilding the client
</a>{" "}
and{" "}
<a
style={{ color: "inherit", fontWeight: "600" }}
href="https://trello.com/c/Ay6KdiOV/1-voice-overhaul-and-video-calling"
target="_blank"
rel="noreferrer">
the voice server
</a>{" "}
from scratch.
<br />
<br />
The old voice should work in most cases, but it may
inexplicably not connect in some scenarios and / or
exhibit weird behaviour.
</span>
</Tip>
{!permission && ( {!permission && (
<Tip palette="error"> <Tip palette="error">
<Text id="app.settings.pages.audio.tip_grant_permission" /> <Text id="app.settings.pages.audio.tip_grant_permission" />

View File

@@ -5,60 +5,76 @@ import { Link } from "react-router-dom";
import styles from "./Panes.module.scss"; import styles from "./Panes.module.scss";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { CategoryButton } from "@revoltchat/ui"; import { CategoryButton, Column, Tip } from "@revoltchat/ui";
export function Feedback() { export function Feedback() {
return ( return (
<div className={styles.feedback}> <Column>
<a <Tip palette="warning">
href="https://github.com/revoltchat/revolt/discussions" <span>
target="_blank" We are currently rebuilding the client from scratch. You can{" "}
rel="noreferrer"> <a
<CategoryButton style={{ color: "inherit", fontWeight: "600" }}
action="external" href="https://github.com/revoltchat/frontend/issues/14"
icon={<Github size={24} />} target="_blank"
description={ rel="noreferrer">
<Text id="app.settings.pages.feedback.suggest_desc" /> view our progress here
}> </a>
<Text id="app.settings.pages.feedback.suggest" /> ; in the meantime, we are only accepting and fixing critical
</CategoryButton> bugs with the current client.
</a> </span>
<a </Tip>
href="https://github.com/revoltchat/revite/issues/new/choose" <div className={styles.feedback}>
target="_blank" <a
rel="noreferrer"> href="https://github.com/revoltchat/revolt/discussions"
<CategoryButton target="_blank"
action="external" rel="noreferrer">
icon={<ListOl size={24} />}
description={
<Text id="app.settings.pages.feedback.issue_desc" />
}>
<Text id="app.settings.pages.feedback.issue" />
</CategoryButton>
</a>
<a
href="https://github.com/orgs/revoltchat/projects/3"
target="_blank"
rel="noreferrer">
<CategoryButton
action="external"
icon={<BugAlt size={24} />}
description={
<Text id="app.settings.pages.feedback.bug_desc" />
}>
<Text id="app.settings.pages.feedback.bug" />
</CategoryButton>
</a>
<Link to="/invite/Testers">
<a>
<CategoryButton <CategoryButton
action="chevron" action="external"
icon={<Group size={24} />} icon={<Github size={24} />}
description="You can report issues and discuss improvements with us directly here."> description={
{"Join the Revolt Lounge"} <Text id="app.settings.pages.feedback.suggest_desc" />
}>
<Text id="app.settings.pages.feedback.suggest" />
</CategoryButton> </CategoryButton>
</a> </a>
</Link> <a
</div> href="https://github.com/revoltchat/revite/issues/new/choose"
target="_blank"
rel="noreferrer">
<CategoryButton
action="external"
icon={<ListOl size={24} />}
description={
<Text id="app.settings.pages.feedback.issue_desc" />
}>
<Text id="app.settings.pages.feedback.issue" />
</CategoryButton>
</a>
<a
href="https://github.com/orgs/revoltchat/projects/3"
target="_blank"
rel="noreferrer">
<CategoryButton
action="external"
icon={<BugAlt size={24} />}
description={
<Text id="app.settings.pages.feedback.bug_desc" />
}>
<Text id="app.settings.pages.feedback.bug" />
</CategoryButton>
</a>
<Link to="/invite/Testers">
<a>
<CategoryButton
action="chevron"
icon={<Group size={24} />}
description="You can report issues and discuss improvements with us directly here.">
{"Join the Revolt Lounge"}
</CategoryButton>
</a>
</Link>
</div>
</Column>
); );
} }

View File

@@ -66,7 +66,7 @@ export function Native() {
title="Enable Discord status" title="Enable Discord status"
description="Rep Revolt on your Discord status." description="Rep Revolt on your Discord status."
/> />
<Checkbox {/* <Checkbox
value={config.build === "nightly"} value={config.build === "nightly"}
onChange={(nightly) => { onChange={(nightly) => {
const build = nightly ? "nightly" : "stable"; const build = nightly ? "nightly" : "stable";
@@ -79,7 +79,7 @@ export function Native() {
}} }}
title="Revolt Nightly" title="Revolt Nightly"
description="Use the beta branch of Revolt." description="Use the beta branch of Revolt."
/> /> */}
<h3>Titlebar</h3> <h3>Titlebar</h3>
<Checkbox <Checkbox

View File

@@ -694,3 +694,23 @@
section { section {
margin-bottom: 20px; margin-bottom: 20px;
} }
.titleNew {
display: flex;
margin: 15px 0;
gap: 8px;
align-items: center;
font-size: 0.8125rem;
font-weight: 700;
text-transform: uppercase;
color: var(--secondary-foreground);
.new {
font-size: 10px;
background: var(--accent);
padding: 3px 5px;
border-radius: 20px;
color: white;
}
}

View File

@@ -1,4 +1,5 @@
import { Markdown } from "@styled-icons/boxicons-logos"; import { Markdown } from "@styled-icons/boxicons-logos";
import { UserCircle } from "@styled-icons/boxicons-solid";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import { API } from "revolt.js"; import { API } from "revolt.js";
@@ -7,7 +8,7 @@ import styles from "./Panes.module.scss";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useCallback, useContext, useEffect, useState } from "preact/hooks"; import { useCallback, useContext, useEffect, useState } from "preact/hooks";
import { Button, LineDivider, Tip } from "@revoltchat/ui"; import { Button, LineDivider, Tip, CategoryButton } from "@revoltchat/ui";
import TextAreaAutoSize from "../../../lib/TextAreaAutoSize"; import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
import { useTranslation } from "../../../lib/i18n"; import { useTranslation } from "../../../lib/i18n";
@@ -17,6 +18,7 @@ import AutoComplete, {
} from "../../../components/common/AutoComplete"; } from "../../../components/common/AutoComplete";
import { useSession } from "../../../controllers/client/ClientController"; import { useSession } from "../../../controllers/client/ClientController";
import { FileUploader } from "../../../controllers/client/jsx/legacy/FileUploads"; import { FileUploader } from "../../../controllers/client/jsx/legacy/FileUploads";
import { modalController } from "../../../controllers/modals/ModalController";
import { UserProfile } from "../../../controllers/modals/components/legacy/UserProfile"; import { UserProfile } from "../../../controllers/modals/components/legacy/UserProfile";
export const Profile = observer(() => { export const Profile = observer(() => {
@@ -77,6 +79,19 @@ export const Profile = observer(() => {
{...({} as any)} {...({} as any)}
/> />
</div> </div>
<div className={styles.titleNew}>
Display Name
<div className={styles.new}>NEW</div>
</div>
<CategoryButton
onClick={() =>
modalController.push({ type: "modify_displayname" })
}
icon={<UserCircle size={24} />}
action="chevron"
description={"Change your display name to whatever you like"}>
Display Name
</CategoryButton>
{/*<h3>Badges</h3> {/*<h3>Badges</h3>
<div className={styles.badgePicker}> <div className={styles.badgePicker}>
<div className={styles.overlay} /> <div className={styles.overlay} />

View File

@@ -38,7 +38,9 @@ dayjs.extend(relativeTime);
export function Sessions() { export function Sessions() {
const client = useClient(); const client = useClient();
const deviceId = const deviceId =
typeof client.session === "object" ? client.session._id : undefined; typeof client.session === "object"
? (client.session as unknown as { _id: string })._id
: undefined;
const [sessions, setSessions] = useState<API.SessionInfo[] | undefined>( const [sessions, setSessions] = useState<API.SessionInfo[] | undefined>(
undefined, undefined,

View File

@@ -4,7 +4,7 @@ import styled from "styled-components";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { Button, Column, Row, Stacked } from "@revoltchat/ui"; import { Button, Column, Row, Stacked, Tip } from "@revoltchat/ui";
import UserShort from "../../../components/common/user/UserShort"; import UserShort from "../../../components/common/user/UserShort";
import { EmojiUploader } from "../../../components/settings/customisation/EmojiUploader"; import { EmojiUploader } from "../../../components/settings/customisation/EmojiUploader";
@@ -33,6 +33,22 @@ export const Emojis = observer(({ server }: Props) => {
return ( return (
<Column> <Column>
<Tip palette="warning">
<span>
This UI was never finished and will be polished in the{" "}
<a
style={{ color: "inherit", fontWeight: "600" }}
href="https://github.com/revoltchat/frontend/issues/14"
target="_blank"
rel="noreferrer">
new client
</a>
.<br />
<br />
Also please note that emoji names must be lowercase
alphanumeric!
</span>
</Tip>
{server.havePermission("ManageCustomisation") && ( {server.havePermission("ManageCustomisation") && (
<EmojiUploader server={server} /> <EmojiUploader server={server} />
)} )}

View File

@@ -78,7 +78,7 @@ export function useSystemAlert() {
*/ */
async function checkVersion() { async function checkVersion() {
const { version, poll_rate, alert } = (await fetch( const { version, poll_rate, alert } = (await fetch(
"https://api.revolt.chat/release", "https://health.revolt.chat/api/health",
).then((res) => res.json())) as { ).then((res) => res.json())) as {
version: string; version: string;
poll_rate?: number; poll_rate?: number;
@@ -111,7 +111,10 @@ async function checkVersion() {
} }
} }
if (import.meta.env.VITE_API_URL === "https://api.revolt.chat") { if (
import.meta.env.VITE_API_URL === "https://api.revolt.chat" ||
import.meta.env.VITE_API_URL === "https://app.revolt.chat/api"
) {
// Check for critical updates hourly // Check for critical updates hourly
schedule(); schedule();
checkVersion(); checkVersion();

159
yarn.lock
View File

@@ -2299,6 +2299,20 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@revoltchat/rehype-katex@npm:6.0.3-patch.1":
version: 6.0.3-patch.1
resolution: "@revoltchat/rehype-katex@npm:6.0.3-patch.1"
dependencies:
"@types/hast": ^2.0.0
"@types/katex": ^0.14.0
hast-util-from-html-isomorphic: ^1.0.0
hast-util-to-text: ^3.1.0
katex: ^0.16.7
unist-util-visit: ^4.0.0
checksum: 1b7e57ddafa300e96396a5f5d64dad947c201b514562b1d581ac1a683252bc074fcb620fb271c136dd8b84c069dad23cd98ef0b01585be426bb2ce3f5dde004f
languageName: node
linkType: hard
"@revoltchat/ui@portal:external/components::locator=client%40workspace%3A.": "@revoltchat/ui@portal:external/components::locator=client%40workspace%3A.":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@revoltchat/ui@portal:external/components::locator=client%40workspace%3A." resolution: "@revoltchat/ui@portal:external/components::locator=client%40workspace%3A."
@@ -2627,6 +2641,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/katex@npm:^0.14.0":
version: 0.14.0
resolution: "@types/katex@npm:0.14.0"
checksum: 330e0d0337ba48c87f5b793965fbad673653789bf6e50dfe8d726a7b0cbefd37195055e31503aae629814aa79447e4f23a4b87ad1ac565c0d9a9d9978836f39b
languageName: node
linkType: hard
"@types/lodash.defaultsdeep@npm:^4.6.6": "@types/lodash.defaultsdeep@npm:^4.6.6":
version: 4.6.6 version: 4.6.6
resolution: "@types/lodash.defaultsdeep@npm:4.6.6" resolution: "@types/lodash.defaultsdeep@npm:4.6.6"
@@ -3688,6 +3709,7 @@ __metadata:
"@hcaptcha/react-hcaptcha": ^1.4.4 "@hcaptcha/react-hcaptcha": ^1.4.4
"@insertish/vite-plugin-babel-macros": ^1.0.5 "@insertish/vite-plugin-babel-macros": ^1.0.5
"@preact/preset-vite": ^2.0.0 "@preact/preset-vite": ^2.0.0
"@revoltchat/rehype-katex": 6.0.3-patch.1
"@revoltchat/ui": ^1.0.77 "@revoltchat/ui": ^1.0.77
"@rollup/plugin-replace": ^2.4.2 "@rollup/plugin-replace": ^2.4.2
"@styled-icons/boxicons-logos": ^10.38.0 "@styled-icons/boxicons-logos": ^10.38.0
@@ -3732,6 +3754,7 @@ __metadata:
lodash.defaultsdeep: ^4.6.1 lodash.defaultsdeep: ^4.6.1
lodash.isequal: ^4.5.0 lodash.isequal: ^4.5.0
long: ^5.2.0 long: ^5.2.0
lottie-react: ^2.4.0
mdast-util-to-hast: ^12.1.2 mdast-util-to-hast: ^12.1.2
mediasoup-client: "npm:@insertish/mediasoup-client@3.6.36-esnext" mediasoup-client: "npm:@insertish/mediasoup-client@3.6.36-esnext"
mobx: ^6.6.0 mobx: ^6.6.0
@@ -3750,7 +3773,6 @@ __metadata:
react-router-dom: ^5.2.0 react-router-dom: ^5.2.0
react-scroll: ^1.8.2 react-scroll: ^1.8.2
react-virtuoso: ^2.12.0 react-virtuoso: ^2.12.0
rehype-katex: ^6.0.2
rehype-prism: ^2.1.3 rehype-prism: ^2.1.3
rehype-react: ^7.1.1 rehype-react: ^7.1.1
remark-breaks: ^3.0.2 remark-breaks: ^3.0.2
@@ -3875,7 +3897,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"commander@npm:^8.0.0": "commander@npm:^8.0.0, commander@npm:^8.3.0":
version: 8.3.0 version: 8.3.0
resolution: "commander@npm:8.3.0" resolution: "commander@npm:8.3.0"
checksum: 0f82321821fc27b83bd409510bb9deeebcfa799ff0bf5d102128b500b7af22872c0c92cb6a0ebc5a4cf19c6b550fba9cedfa7329d18c6442a625f851377bacf0 checksum: 0f82321821fc27b83bd409510bb9deeebcfa799ff0bf5d102128b500b7af22872c0c92cb6a0ebc5a4cf19c6b550fba9cedfa7329d18c6442a625f851377bacf0
@@ -4232,6 +4254,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"entities@npm:^4.4.0":
version: 4.5.0
resolution: "entities@npm:4.5.0"
checksum: 853f8ebd5b425d350bffa97dd6958143179a5938352ccae092c62d1267c4e392a039be1bae7d51b6e4ffad25f51f9617531fedf5237f15df302ccfb452cbf2d7
languageName: node
linkType: hard
"env-paths@npm:^2.2.0": "env-paths@npm:^2.2.0":
version: 2.2.1 version: 2.2.1
resolution: "env-paths@npm:2.2.1" resolution: "env-paths@npm:2.2.1"
@@ -5324,6 +5353,41 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"hast-util-from-dom@npm:^4.0.0":
version: 4.2.0
resolution: "hast-util-from-dom@npm:4.2.0"
dependencies:
hastscript: ^7.0.0
web-namespaces: ^2.0.0
checksum: 0eac72cfb2bad20cf70ad978332be8c746dba27576fea3c3b123d7d1a52a8e10a88a25622b60f45255994163845e3f784fcd7c013b1301f2df4b81e3d0b1b973
languageName: node
linkType: hard
"hast-util-from-html-isomorphic@npm:^1.0.0":
version: 1.0.0
resolution: "hast-util-from-html-isomorphic@npm:1.0.0"
dependencies:
"@types/hast": ^2.0.0
hast-util-from-dom: ^4.0.0
hast-util-from-html: ^1.0.0
unist-util-remove-position: ^4.0.0
checksum: a72786d6757a1a38d76cf74ba86fecfaf0690d465dcae477bfa7199ec03d364ba964f658331406ee7e62e912186df6d4ff38fc3ad050e0e3d8bc33a653df060d
languageName: node
linkType: hard
"hast-util-from-html@npm:^1.0.0":
version: 1.0.2
resolution: "hast-util-from-html@npm:1.0.2"
dependencies:
"@types/hast": ^2.0.0
hast-util-from-parse5: ^7.0.0
parse5: ^7.0.0
vfile: ^5.0.0
vfile-message: ^3.0.0
checksum: 81cbda7dfa4c02fcb4e4359a48e25ebee7bef133dc607c2c036c53df27efacdfa22313a86398391ef22b31decda98c7110f1b441c5a48f419514be6b5d4b5603
languageName: node
linkType: hard
"hast-util-from-parse5@npm:^7.0.0": "hast-util-from-parse5@npm:^7.0.0":
version: 7.1.0 version: 7.1.0
resolution: "hast-util-from-parse5@npm:7.1.0" resolution: "hast-util-from-parse5@npm:7.1.0"
@@ -5341,12 +5405,12 @@ __metadata:
linkType: hard linkType: hard
"hast-util-is-element@npm:^2.0.0": "hast-util-is-element@npm:^2.0.0":
version: 2.1.2 version: 2.1.3
resolution: "hast-util-is-element@npm:2.1.2" resolution: "hast-util-is-element@npm:2.1.3"
dependencies: dependencies:
"@types/hast": ^2.0.0 "@types/hast": ^2.0.0
"@types/unist": ^2.0.0 "@types/unist": ^2.0.0
checksum: c5fe9f7cde3775d4cbe19a9a55631a80b7a4ea0131fc2e3d097ebe228a35f09b9219f64b788b7a9cf819e6dcb6d1fc7830fd2f10ad536649e436e8c83da41e00 checksum: 9d988f6839a50566a895a3dd19222e6ab1591243f6a3c36bba835b7e9339a2845f1ff1c583425afd602de1a57a76c5bae8a6dc0ab1d6e5d1e252b422cdeadbb7
languageName: node languageName: node
linkType: hard linkType: hard
@@ -5360,13 +5424,14 @@ __metadata:
linkType: hard linkType: hard
"hast-util-to-text@npm:^3.1.0": "hast-util-to-text@npm:^3.1.0":
version: 3.1.1 version: 3.1.2
resolution: "hast-util-to-text@npm:3.1.1" resolution: "hast-util-to-text@npm:3.1.2"
dependencies: dependencies:
"@types/hast": ^2.0.0 "@types/hast": ^2.0.0
"@types/unist": ^2.0.0
hast-util-is-element: ^2.0.0 hast-util-is-element: ^2.0.0
unist-util-find-after: ^4.0.0 unist-util-find-after: ^4.0.0
checksum: 2312a818c8ec7b02307b04175357e5a7a9918f48624d05366668ba60918734ca62b0ee21006a2a448e0e5a198654cd1fa4ba8c813702b465cb487e2320db523a checksum: d17cf3344c1d584ddd811cbb78d25b6c9819e62c8edb9643b53be38083fd978a6fa9a5bf6e6cd7b5ea48d30d9edc2859acae40b8bb89e166bebcda6017d4703d
languageName: node languageName: node
linkType: hard linkType: hard
@@ -6004,14 +6069,14 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"katex@npm:^0.15.0": "katex@npm:^0.16.7":
version: 0.15.6 version: 0.16.7
resolution: "katex@npm:0.15.6" resolution: "katex@npm:0.16.7"
dependencies: dependencies:
commander: ^8.0.0 commander: ^8.3.0
bin: bin:
katex: cli.js katex: cli.js
checksum: 2da808bbd1d3be27715006cd86767dd3fcce3e317fb3bbd64d407328d2d90de17b5d83062b2cfd0e0d0de32e340efbac214862bc96892a5d1492462e553728d4 checksum: 6c3f61e28820ecba074a149d17be6d731d8a950cdd7826851e16f2b0f3627c4119acd8c6e4fb86950063c4e99a6e478720ea6cca3dc82e59bbca81001ee00b70
languageName: node languageName: node
linkType: hard linkType: hard
@@ -6205,6 +6270,25 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"lottie-react@npm:^2.4.0":
version: 2.4.0
resolution: "lottie-react@npm:2.4.0"
dependencies:
lottie-web: ^5.10.2
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
checksum: e9ea4a89be90a29bde4a83956f76a80d1f8031882f18ea38ef5271d2aafd8e68348ae6297f185ed85b149ca4896fe33aee7faf9780b88a1b289b8e146f477448
languageName: node
linkType: hard
"lottie-web@npm:^5.10.2":
version: 5.12.0
resolution: "lottie-web@npm:5.12.0"
checksum: 77c35be880e484d1a766f21f789ede7c7be59e957579580a18c2dfec08c4812c03e1d652e453166c31fd80556446d6b857fb490d901e34154105532519bc5ef5
languageName: node
linkType: hard
"lru-cache@npm:^6.0.0": "lru-cache@npm:^6.0.0":
version: 6.0.0 version: 6.0.0
resolution: "lru-cache@npm:6.0.0" resolution: "lru-cache@npm:6.0.0"
@@ -7268,6 +7352,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"parse5@npm:^7.0.0":
version: 7.1.2
resolution: "parse5@npm:7.1.2"
dependencies:
entities: ^4.4.0
checksum: 59465dd05eb4c5ec87b76173d1c596e152a10e290b7abcda1aecf0f33be49646ea74840c69af975d7887543ea45564801736356c568d6b5e71792fd0f4055713
languageName: node
linkType: hard
"path-exists@npm:^4.0.0": "path-exists@npm:^4.0.0":
version: 4.0.0 version: 4.0.0
resolution: "path-exists@npm:4.0.0" resolution: "path-exists@npm:4.0.0"
@@ -7794,23 +7887,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"rehype-katex@npm:^6.0.2": "rehype-parse@npm:^7 || ^ 8":
version: 6.0.2
resolution: "rehype-katex@npm:6.0.2"
dependencies:
"@types/hast": ^2.0.0
"@types/katex": ^0.11.0
hast-util-to-text: ^3.1.0
katex: ^0.15.0
rehype-parse: ^8.0.0
unified: ^10.0.0
unist-util-remove-position: ^4.0.0
unist-util-visit: ^4.0.0
checksum: ac8b3486441697b8e22cb7ebf6ec58e06d190240f45b128fe60422b9eb887599f38406581e6e3356af967eb1d45d631b0c09387f060190641f402f56c78fa771
languageName: node
linkType: hard
"rehype-parse@npm:^7 || ^ 8, rehype-parse@npm:^8.0.0":
version: 8.0.4 version: 8.0.4
resolution: "rehype-parse@npm:8.0.4" resolution: "rehype-parse@npm:8.0.4"
dependencies: dependencies:
@@ -8016,14 +8093,14 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"revolt-api@npm:0.5.8": "revolt-api@npm:0.6.0":
version: 0.5.8 version: 0.6.0
resolution: "revolt-api@npm:0.5.8" resolution: "revolt-api@npm:0.6.0"
dependencies: dependencies:
"@insertish/oapi": 0.1.18 "@insertish/oapi": 0.1.18
axios: ^0.26.1 axios: ^0.26.1
lodash.defaultsdeep: ^4.6.1 lodash.defaultsdeep: ^4.6.1
checksum: d488fa87774e4e9d0b3136779c555cbed257d2b76a298b17dca346cc3909f81e84aa516c10a415a7c20cd31990d540ade5489f69e06f69e14ba4974a26aa145e checksum: 94865aa1aee5b05682ffe4e40a0fe431809f879437399943fc429d078160a5fe24686011b43639d6fae88e9a7a43fbe4bb9beb9bbe1bb3070965636f0ec051fd
languageName: node languageName: node
linkType: hard linkType: hard
@@ -8040,7 +8117,7 @@ __metadata:
lodash.isequal: ^4.5.0 lodash.isequal: ^4.5.0
long: ^5.2.0 long: ^5.2.0
mobx: ^6.3.2 mobx: ^6.3.2
revolt-api: 0.5.8 revolt-api: 0.6.0
ulid: ^2.3.0 ulid: ^2.3.0
ws: ^8.2.2 ws: ^8.2.2
languageName: node languageName: node
@@ -9120,12 +9197,12 @@ __metadata:
linkType: hard linkType: hard
"unist-util-find-after@npm:^4.0.0": "unist-util-find-after@npm:^4.0.0":
version: 4.0.0 version: 4.0.1
resolution: "unist-util-find-after@npm:4.0.0" resolution: "unist-util-find-after@npm:4.0.1"
dependencies: dependencies:
"@types/unist": ^2.0.0 "@types/unist": ^2.0.0
unist-util-is: ^5.0.0 unist-util-is: ^5.0.0
checksum: 8381ef0bad18a0b1fa1c7ee47f94a2578ab6bf572eb126a1f179526b9dca47584fc070976f2d83bbe381161fa33b9164a894d0279a30ec83e65433356d43df57 checksum: bed7e7a1a87539bea0b33ddc9ce8e2f3fdd4a7c87e143a848ed5bbb4cf9c563ade7ecf80b3ee5a38f9ad9e6af29cdb8cdde9001eea92542cbb14784f5add7019
languageName: node languageName: node
linkType: hard linkType: hard
@@ -9160,12 +9237,12 @@ __metadata:
linkType: hard linkType: hard
"unist-util-remove-position@npm:^4.0.0": "unist-util-remove-position@npm:^4.0.0":
version: 4.0.1 version: 4.0.2
resolution: "unist-util-remove-position@npm:4.0.1" resolution: "unist-util-remove-position@npm:4.0.2"
dependencies: dependencies:
"@types/unist": ^2.0.0 "@types/unist": ^2.0.0
unist-util-visit: ^4.0.0 unist-util-visit: ^4.0.0
checksum: 7d2808662ac65f2b2f615822b78060419f738fb3b074b10cec77c596ea966b8f5c47553d2d322822a5975c49d2b21cdd64c198ae9fb02a9d54d1afa6342cdd6a checksum: 989831da913d09a82a99ed9b47b78471b6409bde95942cde47e09da54b7736516f17e3c7e026af468684c1efcec5fb52df363381b2f9dc7fd96ce791c5a2fa4a
languageName: node languageName: node
linkType: hard linkType: hard