mirror of
https://github.com/revoltchat/revite.git
synced 2024-12-24 06:32:08 -05:00
merge: branch 'master' into production
This commit is contained in:
commit
df20ab7407
36 changed files with 369 additions and 192 deletions
2
external/components
vendored
2
external/components
vendored
|
@ -1 +1 @@
|
|||
Subproject commit ab0fc1af739f877c8f416dcd7e47d25aacbb7eea
|
||||
Subproject commit e79862b5972b57c016b4c08676ac1b90bd52ee83
|
2
external/lang
vendored
2
external/lang
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 843cece2854185651e17765bcb78109d22eba769
|
||||
Subproject commit f95ec6dc8200adba7925425d4cf2ae0c16f049c3
|
|
@ -73,7 +73,7 @@
|
|||
"@fontsource/space-mono": "^4.4.5",
|
||||
"@fontsource/ubuntu": "^4.4.5",
|
||||
"@fontsource/ubuntu-mono": "^4.4.5",
|
||||
"@hcaptcha/react-hcaptcha": "^0.3.6",
|
||||
"@hcaptcha/react-hcaptcha": "^1.4.4",
|
||||
"@insertish/vite-plugin-babel-macros": "^1.0.5",
|
||||
"@preact/preset-vite": "^2.0.0",
|
||||
"@revoltchat/ui": "^1.0.77",
|
||||
|
@ -81,7 +81,7 @@
|
|||
"@styled-icons/boxicons-logos": "^10.38.0",
|
||||
"@styled-icons/boxicons-regular": "^10.38.0",
|
||||
"@styled-icons/boxicons-solid": "^10.38.0",
|
||||
"@styled-icons/simple-icons": "^10.33.0",
|
||||
"@styled-icons/simple-icons": "^10.45.0",
|
||||
"@tippyjs/react": "4.2.6",
|
||||
"@traptitech/markdown-it-katex": "^3.4.3",
|
||||
"@traptitech/markdown-it-spoiler": "^1.1.6",
|
||||
|
@ -89,9 +89,9 @@
|
|||
"@types/lodash": "^4",
|
||||
"@types/lodash.defaultsdeep": "^4.6.6",
|
||||
"@types/lodash.isequal": "^4.5.5",
|
||||
"@types/node": "^15.12.4",
|
||||
"@types/node": "^15.14.9",
|
||||
"@types/preact-i18n": "^2.3.0",
|
||||
"@types/prismjs": "^1.16.5",
|
||||
"@types/prismjs": "^1.26.0",
|
||||
"@types/react-beautiful-dnd": "^13",
|
||||
"@types/react-helmet": "^6.1.1",
|
||||
"@types/react-router-dom": "^5.1.7",
|
||||
|
|
1
packages/components
Submodule
1
packages/components
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit d314b2d191124f1b487ebd72409e748c1bfccb87
|
1
packages/hast-util-table-cell-style
Submodule
1
packages/hast-util-table-cell-style
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 7803fa54410a7ef9fc3149c482253e74ca1d7d71
|
1
packages/revolt.js
Submodule
1
packages/revolt.js
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 39d1f596e280a28278d913e1e60e4d5298d71578
|
|
@ -14,12 +14,21 @@ import {
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { Message, API } from "revolt.js";
|
||||
import styled from "styled-components/macro";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
import { useTriggerEvents } from "preact-context-menu";
|
||||
import { Text } from "preact-i18n";
|
||||
|
||||
import { Row } from "@revoltchat/ui";
|
||||
|
||||
import { TextReact } from "../../../lib/i18n";
|
||||
|
||||
import { useApplicationState } from "../../../mobx/State";
|
||||
|
||||
import { dayjs } from "../../../context/Locale";
|
||||
|
||||
import Markdown from "../../markdown/Markdown";
|
||||
import Tooltip from "../Tooltip";
|
||||
import UserShort from "../user/UserShort";
|
||||
import MessageBase, { MessageDetail, MessageInfo } from "./MessageBase";
|
||||
|
||||
|
@ -78,6 +87,8 @@ export const SystemMessage = observer(
|
|||
const data = message.asSystemMessage;
|
||||
if (!data) return null;
|
||||
|
||||
const settings = useApplicationState().settings;
|
||||
|
||||
const SystemMessageIcon =
|
||||
iconDictionary[data.type as API.SystemMessage["type"]] ??
|
||||
InfoCircle;
|
||||
|
@ -103,16 +114,39 @@ export const SystemMessage = observer(
|
|||
case "user_joined":
|
||||
case "user_left":
|
||||
case "user_kicked":
|
||||
case "user_banned":
|
||||
case "user_banned": {
|
||||
const createdAt = data.user ? decodeTime(data.user._id) : null;
|
||||
children = (
|
||||
<TextReact
|
||||
id={`app.main.channel.system.${data.type}`}
|
||||
fields={{
|
||||
user: <UserShort user={data.user} />,
|
||||
}}
|
||||
/>
|
||||
<Row centred>
|
||||
<TextReact
|
||||
id={`app.main.channel.system.${data.type}`}
|
||||
fields={{
|
||||
user: <UserShort user={data.user} />,
|
||||
}}
|
||||
/>
|
||||
{data.type == "user_joined" &&
|
||||
createdAt &&
|
||||
(settings.get("appearance:show_account_age") ||
|
||||
Date.now() - createdAt <
|
||||
1000 * 60 * 60 * 24 * 7) && (
|
||||
<Tooltip
|
||||
content={
|
||||
<Text
|
||||
id="app.main.channel.system.registered_at"
|
||||
fields={{
|
||||
time: dayjs(
|
||||
createdAt,
|
||||
).fromNow(),
|
||||
}}
|
||||
/>
|
||||
}>
|
||||
<InfoCircle size={16} />
|
||||
</Tooltip>
|
||||
)}
|
||||
</Row>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "channel_renamed":
|
||||
children = (
|
||||
<TextReact
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { Channel } from "revolt.js";
|
||||
import { Channel, Member } from "revolt.js";
|
||||
import styled from "styled-components/macro";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
@ -60,6 +60,8 @@ const Base = styled.div`
|
|||
`;
|
||||
|
||||
export default observer(({ channel }: Props) => {
|
||||
const client = channel.client;
|
||||
|
||||
const users = channel.typing.filter(
|
||||
(x) =>
|
||||
typeof x !== "undefined" &&
|
||||
|
@ -67,24 +69,47 @@ export default observer(({ channel }: Props) => {
|
|||
x.relationship !== "Blocked",
|
||||
);
|
||||
|
||||
if (users.length > 0) {
|
||||
users.sort((a, b) =>
|
||||
a!._id.toUpperCase().localeCompare(b!._id.toUpperCase()),
|
||||
const members = users.map((user) => {
|
||||
return client.members.getKey({
|
||||
server: channel.server_id!,
|
||||
user: user!._id,
|
||||
});
|
||||
});
|
||||
|
||||
const getName = (member: Member) => {
|
||||
return member.nickname === null
|
||||
? member.user?.username
|
||||
: member.nickname;
|
||||
};
|
||||
|
||||
const getAvatar = (member: Member) => {
|
||||
const memberAvatarURL = member.generateAvatarURL({
|
||||
max_side: 256,
|
||||
});
|
||||
|
||||
return memberAvatarURL === undefined
|
||||
? member.user?.generateAvatarURL({ max_side: 256 })
|
||||
: memberAvatarURL;
|
||||
};
|
||||
|
||||
if (members.length > 0) {
|
||||
members.sort((a, b) =>
|
||||
a!._id.user.toUpperCase().localeCompare(b!._id.user.toUpperCase()),
|
||||
);
|
||||
|
||||
let text;
|
||||
if (users.length >= 5) {
|
||||
if (members.length >= 5) {
|
||||
text = <Text id="app.main.channel.typing.several" />;
|
||||
} else if (users.length > 1) {
|
||||
const userlist = [...users].map((x) => x!.username);
|
||||
const user = userlist.pop();
|
||||
} else if (members.length > 1) {
|
||||
const memberlist = [...members].map((x) => getName(x!));
|
||||
const member = memberlist.pop();
|
||||
|
||||
text = (
|
||||
<Text
|
||||
id="app.main.channel.typing.multiple"
|
||||
fields={{
|
||||
user,
|
||||
userlist: userlist.join(", "),
|
||||
user: member,
|
||||
userlist: memberlist.join(", "),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -92,7 +117,7 @@ export default observer(({ channel }: Props) => {
|
|||
text = (
|
||||
<Text
|
||||
id="app.main.channel.typing.single"
|
||||
fields={{ user: users[0]!.username }}
|
||||
fields={{ user: getName(members[0]!) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -101,11 +126,11 @@ export default observer(({ channel }: Props) => {
|
|||
<Base>
|
||||
<div>
|
||||
<div className="avatars">
|
||||
{users.map((user) => (
|
||||
{members.map((member) => (
|
||||
<img
|
||||
key={user!._id}
|
||||
key={member!._id.user}
|
||||
loading="eager"
|
||||
src={user!.generateAvatarURL({ max_side: 256 })}
|
||||
src={getAvatar(member!)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
@ -49,7 +49,7 @@ export default function EmbedMedia({ embed, width, height }: Props) {
|
|||
case "Lightspeed":
|
||||
return (
|
||||
<iframe
|
||||
src={`https://next.lightspeed.tv/embed/${embed.special.id}`}
|
||||
src={`https://new.lightspeed.tv/embed/${embed.special.id}/stream`}
|
||||
frameBorder="0"
|
||||
allowFullScreen
|
||||
scrolling="no"
|
||||
|
|
|
@ -20,6 +20,7 @@ const Base = styled.pre`
|
|||
* Copy codeblock contents button styles
|
||||
*/
|
||||
const Lang = styled.div`
|
||||
font-family: var(--monospace-font);
|
||||
width: fit-content;
|
||||
padding-bottom: 8px;
|
||||
|
||||
|
|
|
@ -118,20 +118,20 @@ export default function MemberList({
|
|||
return (
|
||||
<NoOomfie>
|
||||
<div>
|
||||
Offline users temporarily disabled for this
|
||||
server, see issue{" "}
|
||||
Offline users have temporarily been disabled for
|
||||
larger servers - see{" "}
|
||||
<a
|
||||
href="https://github.com/revoltchat/delta/issues/128"
|
||||
href="https://github.com/revoltchat/backend/issues/178"
|
||||
target="_blank"
|
||||
rel="noreferrer">
|
||||
#128
|
||||
issue #178
|
||||
</a>{" "}
|
||||
for when this will be resolved.
|
||||
</div>
|
||||
<div>
|
||||
You may re-enable them in{" "}
|
||||
You may re-enable them{" "}
|
||||
<Link to="/settings/experiments">
|
||||
<a>experiments</a>
|
||||
<a>here</a>
|
||||
</Link>
|
||||
.
|
||||
</div>
|
||||
|
|
|
@ -26,6 +26,20 @@ export default function AppearanceOptions() {
|
|||
<Text id="app.settings.pages.appearance.appearance_options.show_send_desc" />
|
||||
}
|
||||
/>
|
||||
{/* Option to always show the account creation age next to join system messages. */}
|
||||
<ObservedInputElement
|
||||
type="checkbox"
|
||||
value={() =>
|
||||
settings.get("appearance:show_account_age") ?? false
|
||||
}
|
||||
onChange={(v) => settings.set("appearance:show_account_age", v)}
|
||||
title={
|
||||
<Text id="app.settings.pages.appearance.appearance_options.show_account_age" />
|
||||
}
|
||||
description={
|
||||
<Text id="app.settings.pages.appearance.appearance_options.show_account_age_desc" />
|
||||
}
|
||||
/>
|
||||
<hr />
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.theme_options.title" />
|
||||
|
|
|
@ -10,6 +10,7 @@ import { noopAsync } from "../../../lib/js";
|
|||
import { takeError } from "../../client/jsx/error";
|
||||
import { modalController } from "../ModalController";
|
||||
import { ModalProps } from "../types";
|
||||
import { IS_REVOLT } from "../../../version";
|
||||
|
||||
/**
|
||||
* Code block which displays invite
|
||||
|
@ -78,7 +79,7 @@ export default function CreateInvite({
|
|||
children: <Text id="app.context_menu.copy_link" />,
|
||||
onClick: () =>
|
||||
modalController.writeText(
|
||||
`${window.location.protocol}//${window.location.host}/invite/${code}`,
|
||||
IS_REVOLT ? `https://rvlt.gg/${code}` : `${window.location.host}/invite/${code}`
|
||||
),
|
||||
},
|
||||
]}
|
||||
|
|
|
@ -12,34 +12,19 @@
|
|||
flex-direction: column;
|
||||
|
||||
div {
|
||||
flex: 1;
|
||||
&.container {
|
||||
max-width: 750px;
|
||||
flex-grow: 1;
|
||||
align-items: left;
|
||||
}
|
||||
|
||||
&.header {
|
||||
gap: 8px;
|
||||
padding: 3em;
|
||||
padding-top: 5em;
|
||||
display: flex;
|
||||
text-align: center;
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
max-height: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
&.form {
|
||||
flex-grow: 1;
|
||||
max-width: 420px;
|
||||
|
||||
img {
|
||||
margin: auto;
|
||||
display: block;
|
||||
max-height: 420px;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@ import { useState } from "preact/hooks";
|
|||
|
||||
import { Button, Preloader } from "@revoltchat/ui";
|
||||
|
||||
import wideSVG from "/assets/wide.svg";
|
||||
// import wideSVG from "/assets/wide.svg";
|
||||
import background from "./assets/onboarding_background.svg";
|
||||
|
||||
import FormField from "../../../../pages/login/FormField";
|
||||
import { takeError } from "../../../client/jsx/error";
|
||||
|
@ -36,43 +37,49 @@ export function OnboardingModal({
|
|||
|
||||
return (
|
||||
<div className={styles.onboarding}>
|
||||
<div className={styles.header}>
|
||||
<h1>
|
||||
<Text id="app.special.modals.onboarding.welcome" />
|
||||
<br />
|
||||
<img src={wideSVG} loading="eager" />
|
||||
</h1>
|
||||
<div className={styles.container}>
|
||||
<div className={styles.header}>
|
||||
<h1>{"Welcome to Revolt."}</h1>
|
||||
</div>
|
||||
<div className={styles.form}>
|
||||
{loading ? (
|
||||
<Preloader type="spinner" />
|
||||
) : (
|
||||
<>
|
||||
<p>
|
||||
{"It's time to choose a username."}
|
||||
<br />
|
||||
{
|
||||
"Others will be able to find, recognise and mention you with this name, so choose wisely."
|
||||
}
|
||||
<br />
|
||||
{
|
||||
"You can change it at any time in your User Settings."
|
||||
}
|
||||
</p>
|
||||
<form
|
||||
onSubmit={
|
||||
handleSubmit(
|
||||
onSubmit,
|
||||
) as unknown as JSX.GenericEventHandler<HTMLFormElement>
|
||||
}>
|
||||
<div>
|
||||
<FormField
|
||||
type="username"
|
||||
register={register}
|
||||
showOverline
|
||||
error={error}
|
||||
/>
|
||||
</div>
|
||||
<Button palette="accent">
|
||||
{"Looks good!"}
|
||||
</Button>
|
||||
</form>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.form}>
|
||||
{loading ? (
|
||||
<Preloader type="spinner" />
|
||||
) : (
|
||||
<>
|
||||
<p>
|
||||
<Text id="app.special.modals.onboarding.pick" />
|
||||
</p>
|
||||
<form
|
||||
onSubmit={
|
||||
handleSubmit(
|
||||
onSubmit,
|
||||
) as unknown as JSX.GenericEventHandler<HTMLFormElement>
|
||||
}>
|
||||
<div>
|
||||
<FormField
|
||||
type="username"
|
||||
register={register}
|
||||
showOverline
|
||||
error={error}
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit">
|
||||
<Text id="app.special.modals.actions.continue" />
|
||||
</Button>
|
||||
</form>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div />
|
||||
<img src={background} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -41,8 +41,8 @@ import { ModalProps } from "../../types";
|
|||
export const UserProfile = observer(
|
||||
({
|
||||
user_id,
|
||||
dummy,
|
||||
dummyProfile,
|
||||
isPlaceholder,
|
||||
placeholderProfile,
|
||||
...props
|
||||
}: ModalProps<"user_profile">) => {
|
||||
const [profile, setProfile] = useState<
|
||||
|
@ -87,21 +87,21 @@ export const UserProfile = observer(
|
|||
}, [user_id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (dummy) {
|
||||
setProfile(dummyProfile);
|
||||
if (isPlaceholder) {
|
||||
setProfile(placeholderProfile);
|
||||
}
|
||||
}, [dummy, dummyProfile]);
|
||||
}, [isPlaceholder, placeholderProfile]);
|
||||
|
||||
useEffect(() => {
|
||||
if (dummy) return;
|
||||
if (isPlaceholder) return;
|
||||
if (session.state === "Online" && typeof mutual === "undefined") {
|
||||
setMutual(null);
|
||||
user.fetchMutual().then(setMutual);
|
||||
}
|
||||
}, [mutual, session.state, dummy, user]);
|
||||
}, [mutual, session.state, isPlaceholder, user]);
|
||||
|
||||
useEffect(() => {
|
||||
if (dummy) return;
|
||||
if (isPlaceholder) return;
|
||||
if (session.state === "Online" && typeof profile === "undefined") {
|
||||
setProfile(null);
|
||||
|
||||
|
@ -109,7 +109,7 @@ export const UserProfile = observer(
|
|||
user.fetchProfile().then(setProfile).catch(noop);
|
||||
}
|
||||
}
|
||||
}, [profile, session.state, dummy, user]);
|
||||
}, [profile, session.state, isPlaceholder, user]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
|
@ -169,7 +169,8 @@ export const UserProfile = observer(
|
|||
onClick={() =>
|
||||
modalController.writeText(user.username)
|
||||
}>
|
||||
@{user.username}
|
||||
{"@"}
|
||||
{user.username}
|
||||
</span>
|
||||
</Localizer>
|
||||
{user.status?.text && (
|
||||
|
@ -184,11 +185,11 @@ export const UserProfile = observer(
|
|||
palette="accent"
|
||||
compact
|
||||
onClick={props.onClose}>
|
||||
Add to server
|
||||
{"Add to server" /* FIXME: i18n */}
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
{user.relationship === "Friend" && (
|
||||
{(user.relationship === "Friend" || user.bot) && (
|
||||
<Localizer>
|
||||
<Tooltip
|
||||
content={
|
||||
|
@ -204,7 +205,7 @@ export const UserProfile = observer(
|
|||
</Tooltip>
|
||||
</Localizer>
|
||||
)}
|
||||
{user.relationship === "User" && !dummy && (
|
||||
{user.relationship === "User" && !isPlaceholder && (
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
props.onClose?.();
|
||||
|
@ -237,11 +238,13 @@ export const UserProfile = observer(
|
|||
</div>
|
||||
{user.relationship !== "User" && (
|
||||
<>
|
||||
<div
|
||||
data-active={tab === "friends"}
|
||||
onClick={() => setTab("friends")}>
|
||||
<Text id="app.special.popovers.user_profile.mutual_friends" />
|
||||
</div>
|
||||
{!user.bot && (
|
||||
<div
|
||||
data-active={tab === "friends"}
|
||||
onClick={() => setTab("friends")}>
|
||||
<Text id="app.special.popovers.user_profile.mutual_friends" />
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
data-active={tab === "groups"}
|
||||
onClick={() => setTab("groups")}>
|
||||
|
@ -281,8 +284,9 @@ export const UserProfile = observer(
|
|||
) : undefined}
|
||||
{user.bot ? (
|
||||
<>
|
||||
{/* FIXME: this too */}
|
||||
<div className={styles.category}>
|
||||
bot owner
|
||||
{"bot owner"}
|
||||
</div>
|
||||
<div
|
||||
onClick={() =>
|
||||
|
@ -432,12 +436,12 @@ export const UserProfile = observer(
|
|||
</>
|
||||
);
|
||||
|
||||
if (dummy) return <div>{children}</div>;
|
||||
if (isPlaceholder) return <div>{children}</div>;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
{...props}
|
||||
nonDismissable={dummy}
|
||||
nonDismissable={isPlaceholder}
|
||||
transparent
|
||||
maxWidth="560px">
|
||||
{children}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
<!-- The following background was created by Infi -->
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="1921.2435514377348" xmlns="http://www.w3.org/2000/svg" height="421.46138650693956" id="screenshot-f4de8581-11da-11ed-b308-1b06919bb1d2" viewBox="-5 687.5386134930604 1921.2435514377348 421.46138650693956" style="-webkit-print-color-adjust: exact;" fill="none" version="1.1"><g id="shape-f4de8581-11da-11ed-b308-1b06919bb1d2"><g id="fills-f4de8581-11da-11ed-b308-1b06919bb1d2"><path rx="0" ry="0" d="M-5,906C-5,906,339,692,620,741C901,790,1313,868,1482,763C1651,658,1850,681,1899,722C1948,763,1877,1109,1877,1109L-5,1109L-5,906Z" style="fill: rgb(45, 45, 45); fill-opacity: 1;"/></g></g></svg>
|
After Width: | Height: | Size: 702 B |
|
@ -98,8 +98,8 @@ export type Modal = {
|
|||
| {
|
||||
type: "user_profile";
|
||||
user_id: string;
|
||||
dummy?: boolean;
|
||||
dummyProfile?: API.UserProfile;
|
||||
isPlaceholder?: boolean;
|
||||
placeholderProfile?: API.UserProfile;
|
||||
}
|
||||
| {
|
||||
type: "create_bot";
|
||||
|
|
|
@ -974,6 +974,10 @@ export default function ContextMenus() {
|
|||
}
|
||||
}
|
||||
|
||||
// workaround to prevent button duplication
|
||||
let hideIDButton;
|
||||
if (sid && server) hideIDButton = true;
|
||||
|
||||
if (sid && server) {
|
||||
generateAction(
|
||||
{
|
||||
|
@ -1015,15 +1019,28 @@ export default function ContextMenus() {
|
|||
"open_server_settings",
|
||||
);
|
||||
|
||||
// workaround to move this above the delete/leave button
|
||||
generateAction(
|
||||
{ action: "copy_id", id },
|
||||
"copy_sid",
|
||||
);
|
||||
|
||||
pushDivider();
|
||||
if (userId === server.owner) {
|
||||
generateAction(
|
||||
{ action: "delete_server", target: server },
|
||||
"delete_server",
|
||||
undefined,
|
||||
undefined,
|
||||
"var(--error)",
|
||||
);
|
||||
} else {
|
||||
generateAction(
|
||||
{ action: "leave_server", target: server },
|
||||
"leave_server",
|
||||
undefined,
|
||||
undefined,
|
||||
"var(--error)",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1035,16 +1052,16 @@ export default function ContextMenus() {
|
|||
});
|
||||
}
|
||||
|
||||
generateAction(
|
||||
{ action: "copy_id", id },
|
||||
sid
|
||||
? "copy_sid"
|
||||
: cid
|
||||
? "copy_cid"
|
||||
: message
|
||||
? "copy_mid"
|
||||
: "copy_uid",
|
||||
);
|
||||
if (!hideIDButton) {
|
||||
generateAction(
|
||||
{ action: "copy_id", id },
|
||||
cid
|
||||
? "copy_cid"
|
||||
: message
|
||||
? "copy_mid"
|
||||
: "copy_uid",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return elements;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { action, makeAutoObservable, runInAction } from "mobx";
|
||||
import { Channel } from "revolt.js";
|
||||
import { Nullable, toNullable } from "revolt.js";
|
||||
import { Channel, Nullable, toNullable } from "revolt.js";
|
||||
|
||||
import type { ProduceType, VoiceUser } from "./Types";
|
||||
import type VoiceClient from "./VoiceClient";
|
||||
|
|
|
@ -5,15 +5,22 @@ import { mapToRecord } from "../../lib/conversion";
|
|||
import Persistent from "../interfaces/Persistent";
|
||||
import Store from "../interfaces/Store";
|
||||
|
||||
interface DraftObject {
|
||||
content?: string;
|
||||
masquerade?: {
|
||||
avatar: string;
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
export interface Data {
|
||||
drafts: Record<string, string>;
|
||||
drafts: Record<string, DraftObject>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles storing draft (currently being written) messages.
|
||||
*/
|
||||
export default class Draft implements Store, Persistent<Data> {
|
||||
private drafts: ObservableMap<string, string>;
|
||||
private drafts: ObservableMap<string, DraftObject>;
|
||||
|
||||
/**
|
||||
* Construct new Draft store.
|
||||
|
@ -52,7 +59,10 @@ export default class Draft implements Store, Persistent<Data> {
|
|||
* @param channel Channel ID
|
||||
*/
|
||||
@computed has(channel: string) {
|
||||
return this.drafts.has(channel) && this.drafts.get(channel)!.length > 0;
|
||||
return (
|
||||
this.drafts.has(channel) &&
|
||||
this.drafts.get(channel)!.content!.length > 0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,7 +70,7 @@ export default class Draft implements Store, Persistent<Data> {
|
|||
* @param channel Channel ID
|
||||
* @param content Draft content
|
||||
*/
|
||||
@action set(channel: string, content?: string) {
|
||||
@action set(channel: string, content?: DraftObject) {
|
||||
if (typeof content === "undefined") {
|
||||
return this.clear(channel);
|
||||
}
|
||||
|
|
|
@ -29,13 +29,13 @@ export const EXPERIMENTS: {
|
|||
[key in Experiment]: { title: string; description: string };
|
||||
} = {
|
||||
dummy: {
|
||||
title: "Dummy Experiment",
|
||||
description: "This is a dummy experiment.",
|
||||
title: "Placeholder Experiment",
|
||||
description: "This is a placeholder experiment.",
|
||||
},
|
||||
offline_users: {
|
||||
title: "Re-enable offline users in large servers (>10k members)",
|
||||
description:
|
||||
"If you can take the performance hit (for example, you're on desktop), you can re-enable offline users for big servers such as Revolt Lounge.",
|
||||
"If you can take the performance hit - for example, if you're on desktop - you can re-enable offline users for big servers such as the Revolt Lounge.",
|
||||
},
|
||||
plugins: {
|
||||
title: "Experimental Plugin API",
|
||||
|
@ -45,7 +45,7 @@ export const EXPERIMENTS: {
|
|||
picker: {
|
||||
title: "Custom Emoji",
|
||||
description:
|
||||
"This will enable a work-in-progress emoji picker, custom emoji settings and reaction picker.",
|
||||
"This will enable a work-in-progress emoji picker, custom emoji settings and a reaction picker.",
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ export interface ISettings {
|
|||
"appearance:seasonal": boolean;
|
||||
"appearance:transparency": boolean;
|
||||
"appearance:show_send_button": boolean;
|
||||
"appearance:show_account_age": boolean;
|
||||
|
||||
"appearance:theme:base": "dark" | "light";
|
||||
"appearance:theme:overrides": Partial<Overrides>;
|
||||
|
@ -79,7 +80,7 @@ export default class Settings
|
|||
*/
|
||||
@action set<T extends keyof ISettings>(key: T, value: ISettings[T]) {
|
||||
// Emoji needs to be immediately applied.
|
||||
if (key === 'appearance:emoji') {
|
||||
if (key === "appearance:emoji") {
|
||||
setGlobalEmojiPack(value as EmojiPack);
|
||||
}
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ export default function App() {
|
|||
)}
|
||||
{alert.dismissable !== false && (
|
||||
<a onClick={() => setStatusBar(false)}>
|
||||
<div className="button">Dismiss</div>
|
||||
<div className="button">{"Dismiss"}</div>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -2,6 +2,8 @@ import { Wrench } from "@styled-icons/boxicons-solid";
|
|||
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
|
||||
import { Button } from "@revoltchat/ui";
|
||||
|
||||
import PaintCounter from "../../lib/PaintCounter";
|
||||
import { TextReact } from "../../lib/i18n";
|
||||
|
||||
|
@ -45,8 +47,15 @@ export default function Developer() {
|
|||
</div>
|
||||
|
||||
<div style={{ padding: "16px" }}>
|
||||
<a style={"cursor: pointer;"} onClick={() => setCrash(true)}>click to crash app</a>
|
||||
{crash && (window as any).sus.sus()}
|
||||
<Button palette="error" onClick={() => setCrash(true)}>
|
||||
Click to crash app
|
||||
</Button>
|
||||
{
|
||||
crash &&
|
||||
(
|
||||
window as any
|
||||
).sus.sus() /* this runs a function that doesn't exist */
|
||||
}
|
||||
{/*<span>
|
||||
<b>Voice Status:</b> {VoiceStatus[voice.status]}
|
||||
</span>
|
||||
|
|
|
@ -51,21 +51,37 @@ export default observer(() => {
|
|||
state.settings.set("appearance:seasonal", !seasonalTheme);
|
||||
|
||||
const isDecember = !isTouchscreenDevice && new Date().getMonth() === 11;
|
||||
const isOctober = !isTouchscreenDevice && new Date().getMonth() === 9
|
||||
const snowflakes = useMemo(() => {
|
||||
const flakes = [];
|
||||
|
||||
// Disable outside of December
|
||||
if (!isDecember) return [];
|
||||
if (isDecember) {
|
||||
for (let i = 0; i < 15; i++) {
|
||||
flakes.push("❄️");
|
||||
flakes.push("❄");
|
||||
}
|
||||
|
||||
for (let i = 0; i < 15; i++) {
|
||||
flakes.push("❄️");
|
||||
flakes.push("❄");
|
||||
for (let i = 0; i < 2; i++) {
|
||||
flakes.push("🎄");
|
||||
flakes.push("☃️");
|
||||
flakes.push("⛄");
|
||||
}
|
||||
|
||||
return flakes;
|
||||
}
|
||||
if (isOctober) {
|
||||
for (let i = 0; i < 15; i++) {
|
||||
flakes.push("🎃");
|
||||
flakes.push("💀");
|
||||
}
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
flakes.push("🎄");
|
||||
flakes.push("☃️");
|
||||
flakes.push("⛄");
|
||||
for (let i = 0; i < 2; i++) {
|
||||
flakes.push("👻");
|
||||
flakes.push("⚰️");
|
||||
flakes.push("🕷️");
|
||||
}
|
||||
|
||||
return flakes;
|
||||
}
|
||||
|
||||
return flakes;
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 484 KiB |
|
@ -1,7 +1,6 @@
|
|||
import isEqual from "lodash.isequal";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Channel, API } from "revolt.js";
|
||||
import { DEFAULT_PERMISSION_DIRECT_MESSAGE } from "revolt.js";
|
||||
import { Channel, API, DEFAULT_PERMISSION_DIRECT_MESSAGE } from "revolt.js";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
import { useState } from "preact/hooks";
|
||||
|
|
|
@ -24,7 +24,7 @@ export function Feedback() {
|
|||
</CategoryButton>
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/revoltchat/revite/issues/new"
|
||||
href="https://github.com/revoltchat/revite/issues/new/choose"
|
||||
target="_blank"
|
||||
rel="noreferrer">
|
||||
<CategoryButton
|
||||
|
@ -37,7 +37,7 @@ export function Feedback() {
|
|||
</CategoryButton>
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/orgs/revoltchat/projects/1"
|
||||
href="https://github.com/orgs/revoltchat/projects/3"
|
||||
target="_blank"
|
||||
rel="noreferrer">
|
||||
<CategoryButton
|
||||
|
@ -55,7 +55,7 @@ export function Feedback() {
|
|||
action="chevron"
|
||||
icon={<Group size={24} />}
|
||||
description="You can report issues and discuss improvements with us directly here.">
|
||||
Join Testers server.
|
||||
{"Join the Revolt Lounge"}
|
||||
</CategoryButton>
|
||||
</a>
|
||||
</Link>
|
||||
|
|
|
@ -165,18 +165,22 @@ export function Native() {
|
|||
title="I understand there's no going back."
|
||||
description={
|
||||
<>
|
||||
This will change the app to the 'dev' branch,
|
||||
instead loading the app from a local server on
|
||||
your machine.
|
||||
{
|
||||
"This will change the app to the 'dev' branch, instead loading the app from a local server on your machine."
|
||||
}
|
||||
<br />
|
||||
<b>
|
||||
Without a server running,{" "}
|
||||
{"Without a server running, "}
|
||||
<span style={{ color: "var(--error)" }}>
|
||||
the app will not load!
|
||||
{"the app will not load!"}
|
||||
</span>
|
||||
</b>
|
||||
<br />
|
||||
<code>yarn dev --port 3001</code>
|
||||
{
|
||||
"Make sure the app is available on port 3001 by running "
|
||||
}
|
||||
<code>{"yarn dev --port 3001 --host"}</code>
|
||||
{"."}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -72,8 +72,8 @@ export const Profile = observer(() => {
|
|||
<div className={styles.preview}>
|
||||
<UserProfile
|
||||
user_id={client.user!._id}
|
||||
dummy={true}
|
||||
dummyProfile={profile}
|
||||
isPlaceholder={true}
|
||||
placeholderProfile={profile}
|
||||
{...({} as any)}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Chrome, Android, Apple, Windows } from "@styled-icons/boxicons-logos";
|
||||
import { Chrome, Android, Windows } from "@styled-icons/boxicons-logos";
|
||||
import { HelpCircle, Desktop, LogOut } from "@styled-icons/boxicons-regular";
|
||||
import {
|
||||
Safari,
|
||||
|
@ -6,6 +6,7 @@ import {
|
|||
Microsoftedge,
|
||||
Linux,
|
||||
Macos,
|
||||
Ios,
|
||||
Opera,
|
||||
Samsung,
|
||||
Windowsxp,
|
||||
|
@ -99,7 +100,7 @@ export function Sessions() {
|
|||
case /mac.*os/i.test(name):
|
||||
return <Macos size={14} />;
|
||||
case /i(Pad)?os/i.test(name):
|
||||
return <Apple size={14} />;
|
||||
return <Ios size={14} />;
|
||||
case /windows 7/i.test(name):
|
||||
return <Windowsxp size={14} />;
|
||||
case /windows/i.test(name):
|
||||
|
|
|
@ -96,14 +96,14 @@ export const Overview = observer(({ server }: Props) => {
|
|||
<div className={styles.markdown}>
|
||||
<Markdown size="24" />
|
||||
<h5>
|
||||
Descriptions support Markdown formatting,{" "}
|
||||
{"Server descriptions support Markdown formatting. "}
|
||||
<a
|
||||
href="https://developers.revolt.chat/markdown"
|
||||
href="https://support.revolt.chat/kb/interface/messages/formatting-your-messages"
|
||||
target="_blank"
|
||||
rel="noreferrer">
|
||||
learn more here
|
||||
{"Learn more here"}
|
||||
</a>
|
||||
.
|
||||
{"."}
|
||||
</h5>
|
||||
</div>
|
||||
<hr />
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
}
|
||||
|
||||
code {
|
||||
font-size: 1.4em;
|
||||
font-family: var(--monospace-font);
|
||||
user-select: all;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { HelpCircle } from "@styled-icons/boxicons-solid";
|
||||
import isEqual from "lodash.isequal";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Server } from "revolt.js";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
import { useMemo, useState } from "preact/hooks";
|
||||
|
@ -16,6 +18,7 @@ import {
|
|||
Category,
|
||||
} from "@revoltchat/ui";
|
||||
|
||||
import Tooltip from "../../../components/common/Tooltip";
|
||||
import { PermissionList } from "../../../components/settings/roles/PermissionList";
|
||||
import { RoleOrDefault } from "../../../components/settings/roles/RoleSelection";
|
||||
import { modalController } from "../../../controllers/modals/ModalController";
|
||||
|
@ -53,6 +56,20 @@ export const Roles = observer(({ server }: Props) => {
|
|||
// Consolidate all permissions that we can change right now.
|
||||
const currentRoles = useRoles(server);
|
||||
|
||||
const RoleId = styled.div`
|
||||
gap: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--tertiary-foreground);
|
||||
|
||||
a {
|
||||
color: var(--tertiary-foreground);
|
||||
}
|
||||
`;
|
||||
|
||||
return (
|
||||
<PermissionsLayout
|
||||
server={server}
|
||||
|
@ -147,6 +164,30 @@ export const Roles = observer(({ server }: Props) => {
|
|||
/>
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<Category>{"Role ID"}</Category>
|
||||
<RoleId>
|
||||
<Tooltip
|
||||
content={
|
||||
"This is a unique identifier for this role."
|
||||
}>
|
||||
<HelpCircle size={16} />
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
content={
|
||||
<Text id="app.special.copy" />
|
||||
}>
|
||||
<a
|
||||
onClick={() =>
|
||||
modalController.writeText(
|
||||
currentRole.id,
|
||||
)
|
||||
}>
|
||||
{currentRole.id}
|
||||
</a>
|
||||
</Tooltip>
|
||||
</RoleId>
|
||||
</section>
|
||||
<section>
|
||||
<Category>
|
||||
<Text id="app.settings.permissions.role_colour" />
|
||||
|
|
50
yarn.lock
50
yarn.lock
|
@ -1590,7 +1590,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.5, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.14.8, @babel/runtime@npm:^7.8.4":
|
||||
"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.5, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.8.4":
|
||||
version: 7.15.3
|
||||
resolution: "@babel/runtime@npm:7.15.3"
|
||||
dependencies:
|
||||
|
@ -1617,6 +1617,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:^7.17.9":
|
||||
version: 7.18.9
|
||||
resolution: "@babel/runtime@npm:7.18.9"
|
||||
dependencies:
|
||||
regenerator-runtime: ^0.13.4
|
||||
checksum: 36dd736baba7164e82b3cc9d43e081f0cb2d05ff867ad39cac515d99546cee75b7f782018b02a3dcf5f2ef3d27f319faa68965fdfec49d4912c60c6002353a2e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/standalone@npm:^7.17.2":
|
||||
version: 7.17.6
|
||||
resolution: "@babel/standalone@npm:7.17.6"
|
||||
|
@ -2020,13 +2029,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hcaptcha/react-hcaptcha@npm:^0.3.6":
|
||||
version: 0.3.7
|
||||
resolution: "@hcaptcha/react-hcaptcha@npm:0.3.7"
|
||||
"@hcaptcha/react-hcaptcha@npm:^1.4.4":
|
||||
version: 1.4.4
|
||||
resolution: "@hcaptcha/react-hcaptcha@npm:1.4.4"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.17.9
|
||||
peerDependencies:
|
||||
react: ">= 16.3.0"
|
||||
react-dom: ">= 16.3.0"
|
||||
checksum: 4a0ce88dd7a719cae2dc84255466a8636540287c2208f8fe46003a67c72f65509cabf8452cad73982a514cfce2e475d209a3e67cd41a559465f185fb4176a92f
|
||||
checksum: 16b046702957f4ca5041c37f2a5012e07415469667fee0396b4764baa7ce0fe5a8577cf652b977732f026d745c999f7e1522bca3b3ca6effea5cfff189ddff2f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -2414,16 +2425,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@styled-icons/simple-icons@npm:^10.33.0":
|
||||
version: 10.37.0
|
||||
resolution: "@styled-icons/simple-icons@npm:10.37.0"
|
||||
"@styled-icons/simple-icons@npm:^10.45.0":
|
||||
version: 10.45.0
|
||||
resolution: "@styled-icons/simple-icons@npm:10.45.0"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.14.8
|
||||
"@babel/runtime": ^7.15.4
|
||||
"@styled-icons/styled-icon": ^10.6.3
|
||||
peerDependencies:
|
||||
react: "*"
|
||||
styled-components: "*"
|
||||
checksum: d94b94f6ad82aef50c69933f2f99d9ccdcadf3579f1ecd9fc5b5537ff1a2d7c0630579c4f1e0f73d6893a4e1cea4010d4ab927ba848a6bab7b1fba31e59c0f01
|
||||
checksum: fda28f4ce59282916413ac2d638440a186f4ae2b1b5904c0e9695efdeec3849800a659f1292299d9afa45276e926f4110b6cbe9259dc38d9ffdc41ef0b4c0086
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -2692,7 +2703,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:^15.12.4":
|
||||
"@types/node@npm:^15.14.9":
|
||||
version: 15.14.9
|
||||
resolution: "@types/node@npm:15.14.9"
|
||||
checksum: 49f7f0522a3af4b8389aee660e88426490cd54b86356672a1fedb49919a8797c00d090ec2dcc4a5df34edc2099d57fc2203d796c4e7fbd382f2022ccd789eee7
|
||||
|
@ -2722,14 +2733,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/prismjs@npm:^1.16.5":
|
||||
version: 1.16.6
|
||||
resolution: "@types/prismjs@npm:1.16.6"
|
||||
checksum: fcb489d19d21292ceffc661fd9c9aa8ba36bcd2835388fa85812aa60cdf14d4720ceb22be7a088aa753c48462e75bcd8191d178a085e4a8b0d3f34471d30d86a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/prismjs@npm:^1.16.6":
|
||||
"@types/prismjs@npm:^1.16.6, @types/prismjs@npm:^1.26.0":
|
||||
version: 1.26.0
|
||||
resolution: "@types/prismjs@npm:1.26.0"
|
||||
checksum: cd5e7a6214c1f4213ec512a5fcf6d8fe37a56b813fc57ac95b5ff5ee074742bfdbd2f2730d9fd985205bf4586728e09baa97023f739e5aa1c9735a7c1ecbd11a
|
||||
|
@ -3681,7 +3685,7 @@ __metadata:
|
|||
"@fontsource/space-mono": ^4.4.5
|
||||
"@fontsource/ubuntu": ^4.4.5
|
||||
"@fontsource/ubuntu-mono": ^4.4.5
|
||||
"@hcaptcha/react-hcaptcha": ^0.3.6
|
||||
"@hcaptcha/react-hcaptcha": ^1.4.4
|
||||
"@insertish/vite-plugin-babel-macros": ^1.0.5
|
||||
"@preact/preset-vite": ^2.0.0
|
||||
"@revoltchat/ui": ^1.0.77
|
||||
|
@ -3689,7 +3693,7 @@ __metadata:
|
|||
"@styled-icons/boxicons-logos": ^10.38.0
|
||||
"@styled-icons/boxicons-regular": ^10.38.0
|
||||
"@styled-icons/boxicons-solid": ^10.38.0
|
||||
"@styled-icons/simple-icons": ^10.33.0
|
||||
"@styled-icons/simple-icons": ^10.45.0
|
||||
"@tippyjs/react": 4.2.6
|
||||
"@traptitech/markdown-it-katex": ^3.4.3
|
||||
"@traptitech/markdown-it-spoiler": ^1.1.6
|
||||
|
@ -3697,9 +3701,9 @@ __metadata:
|
|||
"@types/lodash": ^4
|
||||
"@types/lodash.defaultsdeep": ^4.6.6
|
||||
"@types/lodash.isequal": ^4.5.5
|
||||
"@types/node": ^15.12.4
|
||||
"@types/node": ^15.14.9
|
||||
"@types/preact-i18n": ^2.3.0
|
||||
"@types/prismjs": ^1.16.5
|
||||
"@types/prismjs": ^1.26.0
|
||||
"@types/react-beautiful-dnd": ^13
|
||||
"@types/react-helmet": ^6.1.1
|
||||
"@types/react-router-dom": ^5.1.7
|
||||
|
|
Loading…
Reference in a new issue