mirror of
https://github.com/stoatchat/for-legacy-web.git
synced 2026-03-07 09:25:27 +00:00
chore(refactor): delete context/revoltjs folder
This commit is contained in:
34
src/controllers/client/jsx/ChannelName.tsx
Normal file
34
src/controllers/client/jsx/ChannelName.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
// ! This should be moved into @revoltchat/ui
|
||||
import { Channel } from "revolt.js";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
||||
interface Props {
|
||||
channel?: Channel;
|
||||
prefix?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel display name
|
||||
*/
|
||||
export function ChannelName({ channel, prefix }: Props) {
|
||||
if (!channel) return <></>;
|
||||
|
||||
if (channel.channel_type === "SavedMessages")
|
||||
return <Text id="app.navigation.tabs.saved" />;
|
||||
|
||||
if (channel.channel_type === "DirectMessage") {
|
||||
return (
|
||||
<>
|
||||
{prefix && "@"}
|
||||
{channel.recipient!.username}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (channel.channel_type === "TextChannel" && prefix) {
|
||||
return <>{`#${channel.name}`}</>;
|
||||
}
|
||||
|
||||
return <>{channel.name}</>;
|
||||
}
|
||||
41
src/controllers/client/jsx/CheckAuth.tsx
Normal file
41
src/controllers/client/jsx/CheckAuth.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Redirect } from "react-router-dom";
|
||||
|
||||
import { Preloader } from "@revoltchat/ui";
|
||||
|
||||
import { clientController } from "../ClientController";
|
||||
|
||||
interface Props {
|
||||
auth?: boolean;
|
||||
blockRender?: boolean;
|
||||
|
||||
children: Children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that we are logged in or out and redirect accordingly.
|
||||
* Also prevent render until the client is ready to display.
|
||||
*/
|
||||
export const CheckAuth = observer((props: Props) => {
|
||||
const loggedIn = clientController.isLoggedIn();
|
||||
|
||||
// Redirect if logged out on authenticated page or vice-versa.
|
||||
if (props.auth && !loggedIn) {
|
||||
if (props.blockRender) return null;
|
||||
return <Redirect to="/login" />;
|
||||
} else if (!props.auth && loggedIn) {
|
||||
if (props.blockRender) return null;
|
||||
return <Redirect to="/" />;
|
||||
}
|
||||
|
||||
// Block render if client is getting ready to work.
|
||||
if (
|
||||
props.auth &&
|
||||
clientController.isLoggedIn() &&
|
||||
!clientController.isReady()
|
||||
) {
|
||||
return <Preloader type="spinner" />;
|
||||
}
|
||||
|
||||
return <>{props.children}</>;
|
||||
});
|
||||
29
src/controllers/client/jsx/error.tsx
Normal file
29
src/controllers/client/jsx/error.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function takeError(error: any): string {
|
||||
if (error.response) {
|
||||
const type = error.response.data?.type;
|
||||
if (type) {
|
||||
return type;
|
||||
}
|
||||
|
||||
switch (error.response.status) {
|
||||
case 429:
|
||||
return "TooManyRequests";
|
||||
case 401:
|
||||
case 403:
|
||||
return "Unauthorized";
|
||||
default:
|
||||
return "UnknownError";
|
||||
}
|
||||
} else if (error.request) {
|
||||
return "NetworkError";
|
||||
}
|
||||
|
||||
console.error(error);
|
||||
return "UnknownError";
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function mapError(error: any): never {
|
||||
throw takeError(error);
|
||||
}
|
||||
87
src/controllers/client/jsx/legacy/FileUploads.module.scss
Normal file
87
src/controllers/client/jsx/legacy/FileUploads.module.scss
Normal file
@@ -0,0 +1,87 @@
|
||||
.uploader {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.icon {
|
||||
.image {
|
||||
border-radius: var(--border-radius-half);
|
||||
}
|
||||
}
|
||||
|
||||
&.banner {
|
||||
.image {
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.modify {
|
||||
gap: 4px;
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.image {
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-color: var(--secondary-background);
|
||||
|
||||
.uploading {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
&:hover .edit {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:active .edit {
|
||||
filter: brightness(0.8);
|
||||
}
|
||||
|
||||
&.desaturate {
|
||||
filter: brightness(0.7) sepia(50%) grayscale(90%);
|
||||
}
|
||||
|
||||
.edit {
|
||||
opacity: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: grid;
|
||||
color: white;
|
||||
place-items: center;
|
||||
background: rgba(95, 95, 95, 0.5);
|
||||
transition: 0.2s ease-in-out opacity;
|
||||
}
|
||||
}
|
||||
|
||||
.modify {
|
||||
display: flex;
|
||||
margin-top: 5px;
|
||||
font-size: 12px;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
:first-child {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.small {
|
||||
display: flex;
|
||||
font-size: 10px;
|
||||
flex-direction: column;
|
||||
color: var(--tertiary-foreground);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-uploading="true"] {
|
||||
.image,
|
||||
.modify:first-child {
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
324
src/controllers/client/jsx/legacy/FileUploads.tsx
Normal file
324
src/controllers/client/jsx/legacy/FileUploads.tsx
Normal file
@@ -0,0 +1,324 @@
|
||||
import { Plus } from "@styled-icons/boxicons-regular";
|
||||
import { Pencil } from "@styled-icons/boxicons-solid";
|
||||
import Axios, { AxiosRequestConfig } from "axios";
|
||||
|
||||
import styles from "./FileUploads.module.scss";
|
||||
import classNames from "classnames";
|
||||
import { Text } from "preact-i18n";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
|
||||
import { IconButton, Preloader } from "@revoltchat/ui";
|
||||
|
||||
import { determineFileSize } from "../../../../lib/fileSize";
|
||||
|
||||
import { modalController } from "../../../modals/ModalController";
|
||||
import { useClient } from "../../ClientController";
|
||||
import { takeError } from "../error";
|
||||
|
||||
type BehaviourType =
|
||||
| { behaviour: "ask"; onChange: (file: File) => void }
|
||||
| { behaviour: "upload"; onUpload: (id: string) => Promise<void> }
|
||||
| {
|
||||
behaviour: "multi";
|
||||
onChange: (files: File[]) => void;
|
||||
append?: (files: File[]) => void;
|
||||
};
|
||||
|
||||
type StyleType =
|
||||
| {
|
||||
style: "icon" | "banner";
|
||||
width?: number;
|
||||
height?: number;
|
||||
previewURL?: string;
|
||||
defaultPreview?: string;
|
||||
desaturateDefault?: boolean;
|
||||
}
|
||||
| {
|
||||
style: "attachment";
|
||||
attached: boolean;
|
||||
uploading: boolean;
|
||||
cancel: () => void;
|
||||
size?: number;
|
||||
};
|
||||
|
||||
type Props = BehaviourType &
|
||||
StyleType & {
|
||||
fileType:
|
||||
| "backgrounds"
|
||||
| "icons"
|
||||
| "avatars"
|
||||
| "attachments"
|
||||
| "banners";
|
||||
maxFileSize: number;
|
||||
remove: () => Promise<void>;
|
||||
};
|
||||
|
||||
export async function uploadFile(
|
||||
autumnURL: string,
|
||||
tag: string,
|
||||
file: File,
|
||||
config?: AxiosRequestConfig,
|
||||
) {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
|
||||
const res = await Axios.post(`${autumnURL}/${tag}`, formData, {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
...config,
|
||||
});
|
||||
|
||||
return res.data.id;
|
||||
}
|
||||
|
||||
let input: HTMLInputElement;
|
||||
export function grabFiles(
|
||||
maxFileSize: number,
|
||||
cb: (files: File[]) => void,
|
||||
tooLarge: () => void,
|
||||
multiple?: boolean,
|
||||
) {
|
||||
if (input) {
|
||||
input.remove();
|
||||
}
|
||||
|
||||
input = document.createElement("input");
|
||||
|
||||
input.accept = "*";
|
||||
input.type = "file";
|
||||
input.multiple = multiple ?? false;
|
||||
input.style.display = "none";
|
||||
|
||||
input.addEventListener("change", async (e) => {
|
||||
const files = (e.currentTarget as HTMLInputElement)?.files;
|
||||
if (!files) return;
|
||||
|
||||
for (const file of files) {
|
||||
if (file.size > maxFileSize) {
|
||||
return tooLarge();
|
||||
}
|
||||
}
|
||||
|
||||
cb(Array.from(files));
|
||||
});
|
||||
|
||||
// iOS requires us to append the file input
|
||||
// to DOM to allow us to add any images
|
||||
document.body.appendChild(input);
|
||||
input.click();
|
||||
}
|
||||
|
||||
export function FileUploader(props: Props) {
|
||||
const { fileType, maxFileSize, remove } = props;
|
||||
const client = useClient();
|
||||
|
||||
const [uploading, setUploading] = useState(false);
|
||||
|
||||
function onClick() {
|
||||
if (uploading) return;
|
||||
|
||||
grabFiles(
|
||||
maxFileSize,
|
||||
async (files) => {
|
||||
setUploading(true);
|
||||
|
||||
try {
|
||||
if (props.behaviour === "multi") {
|
||||
props.onChange(files);
|
||||
} else if (props.behaviour === "ask") {
|
||||
props.onChange(files[0]);
|
||||
} else {
|
||||
await props.onUpload(
|
||||
await uploadFile(
|
||||
client.configuration!.features.autumn.url,
|
||||
fileType,
|
||||
files[0],
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
return modalController.push({
|
||||
type: "error",
|
||||
error: takeError(err),
|
||||
});
|
||||
} finally {
|
||||
setUploading(false);
|
||||
}
|
||||
},
|
||||
() =>
|
||||
modalController.push({
|
||||
type: "error",
|
||||
error: "FileTooLarge",
|
||||
}),
|
||||
props.behaviour === "multi",
|
||||
);
|
||||
}
|
||||
|
||||
function removeOrUpload() {
|
||||
if (uploading) return;
|
||||
|
||||
if (props.style === "attachment") {
|
||||
if (props.attached) {
|
||||
props.remove();
|
||||
} else {
|
||||
onClick();
|
||||
}
|
||||
} else if (props.previewURL) {
|
||||
props.remove();
|
||||
} else {
|
||||
onClick();
|
||||
}
|
||||
}
|
||||
|
||||
if (props.behaviour === "multi" && props.append) {
|
||||
// eslint-disable-next-line
|
||||
useEffect(() => {
|
||||
// File pasting.
|
||||
function paste(e: ClipboardEvent) {
|
||||
const items = e.clipboardData?.items;
|
||||
if (typeof items === "undefined") return;
|
||||
if (props.behaviour !== "multi" || !props.append) return;
|
||||
|
||||
const files = [];
|
||||
for (const item of items) {
|
||||
if (!item.type.startsWith("text/")) {
|
||||
const blob = item.getAsFile();
|
||||
if (blob) {
|
||||
if (blob.size > props.maxFileSize) {
|
||||
modalController.push({
|
||||
type: "error",
|
||||
error: "FileTooLarge",
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
files.push(blob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
props.append(files);
|
||||
}
|
||||
|
||||
// Let the browser know we can drop files.
|
||||
function dragover(e: DragEvent) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (e.dataTransfer) e.dataTransfer.dropEffect = "copy";
|
||||
}
|
||||
|
||||
// File dropping.
|
||||
function drop(e: DragEvent) {
|
||||
e.preventDefault();
|
||||
if (props.behaviour !== "multi" || !props.append) return;
|
||||
|
||||
const dropped = e.dataTransfer?.files;
|
||||
if (dropped) {
|
||||
const files = [];
|
||||
for (const item of dropped) {
|
||||
if (item.size > props.maxFileSize) {
|
||||
modalController.push({
|
||||
type: "error",
|
||||
error: "FileTooLarge",
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
files.push(item);
|
||||
}
|
||||
|
||||
props.append(files);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("paste", paste);
|
||||
document.addEventListener("dragover", dragover);
|
||||
document.addEventListener("drop", drop);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("paste", paste);
|
||||
document.removeEventListener("dragover", dragover);
|
||||
document.removeEventListener("drop", drop);
|
||||
};
|
||||
}, [props, props.append]);
|
||||
}
|
||||
|
||||
if (props.style === "icon" || props.style === "banner") {
|
||||
const { style, previewURL, defaultPreview, width, height } = props;
|
||||
return (
|
||||
<div
|
||||
className={classNames(styles.uploader, {
|
||||
[styles.icon]: style === "icon",
|
||||
[styles.banner]: style === "banner",
|
||||
})}
|
||||
style={{
|
||||
alignItems: props.style === "icon" ? "center" : "none",
|
||||
}}
|
||||
data-uploading={uploading}>
|
||||
<div
|
||||
className={classNames(
|
||||
styles.image,
|
||||
props.desaturateDefault &&
|
||||
previewURL == null &&
|
||||
styles.desaturate,
|
||||
)}
|
||||
style={{
|
||||
backgroundImage:
|
||||
style === "icon"
|
||||
? `url('${previewURL ?? defaultPreview}')`
|
||||
: previewURL
|
||||
? `linear-gradient( rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5) ), url('${previewURL}')`
|
||||
: "none",
|
||||
width,
|
||||
height,
|
||||
}}
|
||||
onClick={onClick}>
|
||||
{uploading ? (
|
||||
<div className={styles.uploading}>
|
||||
<Preloader type="ring" />
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.edit}>
|
||||
<Pencil size={30} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.modify}>
|
||||
<span onClick={removeOrUpload}>
|
||||
{uploading ? (
|
||||
<Text id="app.main.channel.uploading_file" />
|
||||
) : props.previewURL ? (
|
||||
<Text id="app.settings.actions.remove" />
|
||||
) : (
|
||||
<Text id="app.settings.actions.upload" />
|
||||
)}
|
||||
</span>
|
||||
<span className={styles.small}>
|
||||
<Text
|
||||
id="app.settings.actions.max_filesize"
|
||||
fields={{
|
||||
filesize: determineFileSize(maxFileSize),
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else if (props.style === "attachment") {
|
||||
const { attached, uploading, cancel, size } = props;
|
||||
return (
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
if (uploading) return cancel();
|
||||
if (attached) return remove();
|
||||
onClick();
|
||||
}}
|
||||
rotate={uploading || attached ? "45deg" : undefined}>
|
||||
<Plus size={size} />
|
||||
</IconButton>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -4,9 +4,8 @@ import { Text } from "preact-i18n";
|
||||
|
||||
import { ModalForm } from "@revoltchat/ui";
|
||||
|
||||
import { mapError } from "../../../context/revoltjs/util";
|
||||
|
||||
import { useClient } from "../../client/ClientController";
|
||||
import { mapError } from "../../client/jsx/error";
|
||||
import { ModalProps } from "../types";
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,8 +7,7 @@ import { ModalForm } from "@revoltchat/ui";
|
||||
|
||||
import { noopAsync } from "../../../lib/js";
|
||||
|
||||
import { takeError } from "../../../context/revoltjs/util";
|
||||
|
||||
import { takeError } from "../../client/jsx/error";
|
||||
import { modalController } from "../ModalController";
|
||||
import { ModalProps } from "../types";
|
||||
|
||||
|
||||
@@ -4,9 +4,8 @@ import { Text } from "preact-i18n";
|
||||
|
||||
import { ModalForm } from "@revoltchat/ui";
|
||||
|
||||
import { mapError } from "../../../context/revoltjs/util";
|
||||
|
||||
import { useClient } from "../../client/ClientController";
|
||||
import { mapError } from "../../client/jsx/error";
|
||||
import { ModalProps } from "../types";
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,10 +7,9 @@ import { Category, Error, Modal } from "@revoltchat/ui";
|
||||
|
||||
import { noopTrue } from "../../../lib/js";
|
||||
|
||||
import { takeError } from "../../../context/revoltjs/util";
|
||||
|
||||
import FormField from "../../../pages/login/FormField";
|
||||
import { useClient } from "../../client/ClientController";
|
||||
import { takeError } from "../../client/jsx/error";
|
||||
import { ModalProps } from "../types";
|
||||
|
||||
interface FormInputs {
|
||||
|
||||
@@ -19,8 +19,7 @@ import {
|
||||
|
||||
import { noop } from "../../../lib/js";
|
||||
|
||||
import { FileUploader } from "../../../context/revoltjs/FileUploads";
|
||||
|
||||
import { FileUploader } from "../../client/jsx/legacy/FileUploads";
|
||||
import { ModalProps } from "../types";
|
||||
|
||||
const Preview = styled(Centred)`
|
||||
|
||||
@@ -9,10 +9,10 @@ import { Category, Modal } from "@revoltchat/ui";
|
||||
import { noopTrue } from "../../../../lib/js";
|
||||
|
||||
import { I18nError } from "../../../../context/Locale";
|
||||
import { takeError } from "../../../../context/revoltjs/util";
|
||||
|
||||
import FormField from "../../../../pages/login/FormField";
|
||||
import { useClient } from "../../../client/ClientController";
|
||||
import { takeError } from "../../../client/jsx/error";
|
||||
import { modalController } from "../../ModalController";
|
||||
import { ModalProps } from "../../types";
|
||||
|
||||
|
||||
@@ -6,11 +6,10 @@ import { useState } from "preact/hooks";
|
||||
|
||||
import { Button, Preloader } from "@revoltchat/ui";
|
||||
|
||||
import { takeError } from "../../../../context/revoltjs/util";
|
||||
|
||||
import wideSVG from "/assets/wide.svg";
|
||||
|
||||
import FormField from "../../../../pages/login/FormField";
|
||||
import { takeError } from "../../../client/jsx/error";
|
||||
import { ModalProps } from "../../types";
|
||||
|
||||
interface FormInputs {
|
||||
|
||||
Reference in New Issue
Block a user