Merge branch 'revoltchat:master' into master

pull/816/head
kate 2022-12-09 15:11:59 +08:00 committed by GitHub
commit 69acae86ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 99 additions and 21 deletions

View File

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

View File

@ -1,4 +1,7 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { Link } from "react-router-dom";
import { Channel, User } from "revolt.js";
import { Emoji as CustomEmoji } from "revolt.js/esm/maps/Emojis";
import styled, { css } from "styled-components/macro";
import { StateUpdater, useState } from "preact/hooks";
@ -7,6 +10,8 @@ import { emojiDictionary } from "../../assets/emojis";
import { useClient } from "../../controllers/client/ClientController";
import ChannelIcon from "./ChannelIcon";
import Emoji from "./Emoji";
import ServerIcon from "./ServerIcon";
import Tooltip from "./Tooltip";
import UserIcon from "./user/UserIcon";
export type AutoCompleteState =
@ -14,7 +19,7 @@ export type AutoCompleteState =
| ({ selected: number; within: boolean } & (
| {
type: "emoji";
matches: string[];
matches: (string | CustomEmoji)[];
}
| {
type: "user";
@ -104,16 +109,23 @@ export function useAutoComplete(
if (type === "emoji") {
// ! TODO: we should convert it to a Binary Search Tree and use that
const matches = Object.keys(emojiDictionary)
.filter((emoji: string) => emoji.match(regex))
.splice(0, 5);
const matches = [
...Object.keys(emojiDictionary).filter((emoji: string) =>
emoji.match(regex),
),
...Array.from(client.emojis.values()).filter((emoji) =>
emoji.name.match(regex),
),
].splice(0, 5);
if (matches.length > 0) {
const currentPosition =
state.type !== "none" ? state.selected : 0;
setState({
// @ts-ignore-next-line are you high
type: "emoji",
// @ts-ignore-next-line
matches,
selected: Math.min(currentPosition, matches.length - 1),
within: false,
@ -233,10 +245,13 @@ export function useAutoComplete(
const content = el.value.split("");
if (state.type === "emoji") {
const selected = state.matches[state.selected];
content.splice(
index,
search.length,
state.matches[state.selected],
selected instanceof CustomEmoji
? selected._id
: selected,
": ",
);
} else if (state.type === "user") {
@ -388,12 +403,17 @@ export default function AutoComplete({
setState,
onClick,
}: Pick<AutoCompleteProps, "detached" | "state" | "setState" | "onClick">) {
const client = useClient();
return (
<Base detached={detached}>
<div>
{state.type === "emoji" &&
state.matches.map((match, i) => (
<button
style={{
display: "flex",
justifyContent: "space-between",
}}
key={match}
className={i === state.selected ? "active" : ""}
onMouseEnter={() =>
@ -412,15 +432,61 @@ export default function AutoComplete({
})
}
onClick={onClick}>
<Emoji
emoji={
(emojiDictionary as Record<string, string>)[
match
]
}
size={20}
/>
:{match}:
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "center",
}}>
{match instanceof CustomEmoji ? (
<img
loading="lazy"
src={match.imageURL}
style={{
width: `20px`,
height: `20px`,
}}
/>
) : (
<Emoji
emoji={
(
emojiDictionary as Record<
string,
string
>
)[match]
}
size={20}
/>
)}
<span style={{ paddingLeft: "4px" }}>{`:${
match instanceof CustomEmoji
? match.name
: match
}:`}</span>
</div>
{match instanceof CustomEmoji &&
match.parent.type == "Server" && (
<>
<Tooltip
content={
client.servers.get(
match.parent.id,
)?.name
}>
<Link
to={`/server/${match.parent.id}`}>
<ServerIcon
target={client.servers.get(
match.parent.id,
)}
size={20}
/>
</Link>
</Tooltip>
</>
)}
</button>
))}
{state.type === "user" &&

View File

@ -45,6 +45,8 @@ import { PermissionTooltip } from "../Tooltip";
import FilePreview from "./bars/FilePreview";
import ReplyBar from "./bars/ReplyBar";
import { DraftObject } from "../../../mobx/stores/Draft";
type Props = {
channel: Channel;
};
@ -277,7 +279,12 @@ export default observer(({ channel }: Props) => {
// Push message content to draft.
const setMessage = useCallback(
(content?: string) => state.draft.set(channel._id, content),
(content?: string) => {
const dobj: DraftObject = {
content
}
state.draft.set(channel._id, dobj)
},
[state.draft, channel._id],
);
@ -317,7 +324,7 @@ export default observer(({ channel }: Props) => {
if (uploadState.type === "uploading" || uploadState.type === "sending")
return;
const content = state.draft.get(channel._id)?.trim() ?? "";
const content = state.draft.get(channel._id)?.content?.trim() ?? "";
if (uploadState.type === "attached") return sendFile(content);
if (content.length === 0) return;
@ -526,7 +533,7 @@ export default observer(({ channel }: Props) => {
}
function isInCodeBlock(cursor: number): boolean {
const content = state.draft.get(channel._id) || "";
const content = state.draft.get(channel._id)?.content || "";
const contentBeforeCursor = content.substring(0, cursor);
let delimiterCount = 0;
@ -607,9 +614,12 @@ export default observer(({ channel }: Props) => {
<HackAlertThisFileWillBeReplaced
onSelect={(emoji) => {
const v = state.draft.get(channel._id);
const cnt: DraftObject = {
content: (v == null ? "" : `${v.content} `) + `:${emoji}:`
}
state.draft.set(
channel._id,
`${v ? `${v} ` : ""}:${emoji}:`,
cnt,
);
}}
onClose={closePicker}
@ -664,7 +674,7 @@ export default observer(({ channel }: Props) => {
id="message"
maxLength={2000}
onKeyUp={onKeyUp}
value={state.draft.get(channel._id) ?? ""}
value={state.draft.get(channel._id)?.content ?? ""}
padding="var(--message-box-padding)"
onKeyDown={(e) => {
if (e.ctrlKey && e.key === "Enter") {

View File

@ -17,6 +17,7 @@ const ALLOWED_ORIGINS = [
"app.revolt.chat",
"nightly.revolt.chat",
"local.revolt.chat",
"rolt.chat",
];
/**

View File

@ -5,7 +5,7 @@ import { mapToRecord } from "../../lib/conversion";
import Persistent from "../interfaces/Persistent";
import Store from "../interfaces/Store";
interface DraftObject {
export interface DraftObject {
content?: string;
masquerade?: {
avatar: string;

View File

@ -53,7 +53,7 @@ export default observer(() => {
const isDecember = !isTouchscreenDevice && new Date().getMonth() === 11;
const isOctober = !isTouchscreenDevice && new Date().getMonth() === 9
const snowflakes = useMemo(() => {
const flakes = [];
const flakes: string[] = [];
if (isDecember) {
for (let i = 0; i < 15; i++) {