mirror of
https://github.com/stoatchat/for-legacy-web.git
synced 2026-03-07 01:15:28 +00:00
Run prettier on all files.
This commit is contained in:
@@ -1,38 +1,44 @@
|
||||
import { X } from "@styled-icons/boxicons-regular";
|
||||
|
||||
import styles from "./ChannelInfo.module.scss";
|
||||
|
||||
import Modal from "../../../components/ui/Modal";
|
||||
import { getChannelName } from "../../revoltjs/util";
|
||||
|
||||
import Markdown from "../../../components/markdown/Markdown";
|
||||
import { useChannel, useForceUpdate } from "../../revoltjs/hooks";
|
||||
import { getChannelName } from "../../revoltjs/util";
|
||||
|
||||
interface Props {
|
||||
channel_id: string;
|
||||
onClose: () => void;
|
||||
channel_id: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function ChannelInfo({ channel_id, onClose }: Props) {
|
||||
const ctx = useForceUpdate();
|
||||
const channel = useChannel(channel_id, ctx);
|
||||
if (!channel) return null;
|
||||
const ctx = useForceUpdate();
|
||||
const channel = useChannel(channel_id, ctx);
|
||||
if (!channel) return null;
|
||||
|
||||
if (channel.channel_type === "DirectMessage" || channel.channel_type === 'SavedMessages') {
|
||||
onClose();
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
channel.channel_type === "DirectMessage" ||
|
||||
channel.channel_type === "SavedMessages"
|
||||
) {
|
||||
onClose();
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal visible={true} onClose={onClose}>
|
||||
<div className={styles.info}>
|
||||
<div className={styles.header}>
|
||||
<h1>{ getChannelName(ctx.client, channel, true) }</h1>
|
||||
<div onClick={onClose}>
|
||||
<X size={36} />
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<Markdown content={channel.description} />
|
||||
</p>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
return (
|
||||
<Modal visible={true} onClose={onClose}>
|
||||
<div className={styles.info}>
|
||||
<div className={styles.header}>
|
||||
<h1>{getChannelName(ctx.client, channel, true)}</h1>
|
||||
<div onClick={onClose}>
|
||||
<X size={36} />
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<Markdown content={channel.description} />
|
||||
</p>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,43 +1,46 @@
|
||||
import styles from "./ImageViewer.module.scss";
|
||||
import Modal from "../../../components/ui/Modal";
|
||||
import { useContext, useEffect } from "preact/hooks";
|
||||
import { AppContext } from "../../revoltjs/RevoltClient";
|
||||
import { Attachment, EmbedImage } from "revolt.js/dist/api/objects";
|
||||
import EmbedMediaActions from "../../../components/common/messaging/embed/EmbedMediaActions";
|
||||
|
||||
import styles from "./ImageViewer.module.scss";
|
||||
import { useContext, useEffect } from "preact/hooks";
|
||||
|
||||
import AttachmentActions from "../../../components/common/messaging/attachments/AttachmentActions";
|
||||
import EmbedMediaActions from "../../../components/common/messaging/embed/EmbedMediaActions";
|
||||
import Modal from "../../../components/ui/Modal";
|
||||
|
||||
import { AppContext } from "../../revoltjs/RevoltClient";
|
||||
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
embed?: EmbedImage;
|
||||
attachment?: Attachment;
|
||||
onClose: () => void;
|
||||
embed?: EmbedImage;
|
||||
attachment?: Attachment;
|
||||
}
|
||||
|
||||
export function ImageViewer({ attachment, embed, onClose }: Props) {
|
||||
// ! FIXME: temp code
|
||||
// ! add proxy function to client
|
||||
function proxyImage(url: string) {
|
||||
return 'https://jan.revolt.chat/proxy?url=' + encodeURIComponent(url);
|
||||
}
|
||||
|
||||
if (attachment && attachment.metadata.type !== "Image") return null;
|
||||
const client = useContext(AppContext);
|
||||
// ! FIXME: temp code
|
||||
// ! add proxy function to client
|
||||
function proxyImage(url: string) {
|
||||
return "https://jan.revolt.chat/proxy?url=" + encodeURIComponent(url);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal visible={true} onClose={onClose} noBackground>
|
||||
<div className={styles.viewer}>
|
||||
{ attachment &&
|
||||
<>
|
||||
<img src={client.generateFileURL(attachment)} />
|
||||
<AttachmentActions attachment={attachment} />
|
||||
</>
|
||||
}
|
||||
{ embed &&
|
||||
<>
|
||||
<img src={proxyImage(embed.url)} />
|
||||
<EmbedMediaActions embed={embed} />
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
if (attachment && attachment.metadata.type !== "Image") return null;
|
||||
const client = useContext(AppContext);
|
||||
|
||||
return (
|
||||
<Modal visible={true} onClose={onClose} noBackground>
|
||||
<div className={styles.viewer}>
|
||||
{attachment && (
|
||||
<>
|
||||
<img src={client.generateFileURL(attachment)} />
|
||||
<AttachmentActions attachment={attachment} />
|
||||
</>
|
||||
)}
|
||||
{embed && (
|
||||
<>
|
||||
<img src={proxyImage(embed.url)} />
|
||||
<EmbedMediaActions embed={embed} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,127 +1,134 @@
|
||||
import { Text } from "preact-i18n";
|
||||
import { SubmitHandler, useForm } from "react-hook-form";
|
||||
import Modal from "../../../components/ui/Modal";
|
||||
import { takeError } from "../../revoltjs/util";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
import { useContext, useState } from "preact/hooks";
|
||||
import FormField from '../../../pages/login/FormField';
|
||||
|
||||
import Modal from "../../../components/ui/Modal";
|
||||
import Overline from "../../../components/ui/Overline";
|
||||
|
||||
import FormField from "../../../pages/login/FormField";
|
||||
import { AppContext } from "../../revoltjs/RevoltClient";
|
||||
import { takeError } from "../../revoltjs/util";
|
||||
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
field: "username" | "email" | "password";
|
||||
onClose: () => void;
|
||||
field: "username" | "email" | "password";
|
||||
}
|
||||
|
||||
interface FormInputs {
|
||||
password: string,
|
||||
new_email: string,
|
||||
new_username: string,
|
||||
new_password: string,
|
||||
password: string;
|
||||
new_email: string;
|
||||
new_username: string;
|
||||
new_password: string;
|
||||
|
||||
// TODO: figure out if this is correct or not
|
||||
// it wasn't in the types before this was typed but the element itself was there
|
||||
current_password?: string
|
||||
// TODO: figure out if this is correct or not
|
||||
// it wasn't in the types before this was typed but the element itself was there
|
||||
current_password?: string;
|
||||
}
|
||||
|
||||
export function ModifyAccountModal({ onClose, field }: Props) {
|
||||
const client = useContext(AppContext);
|
||||
const { handleSubmit, register, errors } = useForm<FormInputs>();
|
||||
const [error, setError] = useState<string | undefined>(undefined);
|
||||
const client = useContext(AppContext);
|
||||
const { handleSubmit, register, errors } = useForm<FormInputs>();
|
||||
const [error, setError] = useState<string | undefined>(undefined);
|
||||
|
||||
const onSubmit: SubmitHandler<FormInputs> = async ({
|
||||
password,
|
||||
new_username,
|
||||
new_email,
|
||||
new_password
|
||||
}) => {
|
||||
try {
|
||||
if (field === "email") {
|
||||
await client.req("POST", "/auth/change/email", {
|
||||
password,
|
||||
new_email
|
||||
});
|
||||
onClose();
|
||||
} else if (field === "password") {
|
||||
await client.req("POST", "/auth/change/password", {
|
||||
password,
|
||||
new_password
|
||||
});
|
||||
onClose();
|
||||
} else if (field === "username") {
|
||||
await client.req("PATCH", "/users/id/username", {
|
||||
username: new_username,
|
||||
password
|
||||
});
|
||||
onClose();
|
||||
}
|
||||
} catch (err) {
|
||||
setError(takeError(err));
|
||||
}
|
||||
}
|
||||
const onSubmit: SubmitHandler<FormInputs> = async ({
|
||||
password,
|
||||
new_username,
|
||||
new_email,
|
||||
new_password,
|
||||
}) => {
|
||||
try {
|
||||
if (field === "email") {
|
||||
await client.req("POST", "/auth/change/email", {
|
||||
password,
|
||||
new_email,
|
||||
});
|
||||
onClose();
|
||||
} else if (field === "password") {
|
||||
await client.req("POST", "/auth/change/password", {
|
||||
password,
|
||||
new_password,
|
||||
});
|
||||
onClose();
|
||||
} else if (field === "username") {
|
||||
await client.req("PATCH", "/users/id/username", {
|
||||
username: new_username,
|
||||
password,
|
||||
});
|
||||
onClose();
|
||||
}
|
||||
} catch (err) {
|
||||
setError(takeError(err));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={true}
|
||||
onClose={onClose}
|
||||
title={<Text id={`app.special.modals.account.change.${field}`} />}
|
||||
actions={[
|
||||
{
|
||||
confirmation: true,
|
||||
onClick: handleSubmit(onSubmit),
|
||||
text:
|
||||
field === "email" ? (
|
||||
<Text id="app.special.modals.actions.send_email" />
|
||||
) : (
|
||||
<Text id="app.special.modals.actions.update" />
|
||||
)
|
||||
},
|
||||
{
|
||||
onClick: onClose,
|
||||
text: <Text id="app.special.modals.actions.close" />
|
||||
}
|
||||
]}
|
||||
>
|
||||
{/* Preact / React typing incompatabilities */}
|
||||
<form onSubmit={handleSubmit(onSubmit) as JSX.GenericEventHandler<HTMLFormElement>}>
|
||||
{field === "email" && (
|
||||
<FormField
|
||||
type="email"
|
||||
name="new_email"
|
||||
register={register}
|
||||
showOverline
|
||||
error={errors.new_email?.message}
|
||||
/>
|
||||
)}
|
||||
{field === "password" && (
|
||||
<FormField
|
||||
type="password"
|
||||
name="new_password"
|
||||
register={register}
|
||||
showOverline
|
||||
error={errors.new_password?.message}
|
||||
/>
|
||||
)}
|
||||
{field === "username" && (
|
||||
<FormField
|
||||
type="username"
|
||||
name="new_username"
|
||||
register={register}
|
||||
showOverline
|
||||
error={errors.new_username?.message}
|
||||
/>
|
||||
)}
|
||||
<FormField
|
||||
type="current_password"
|
||||
register={register}
|
||||
showOverline
|
||||
error={errors.current_password?.message}
|
||||
/>
|
||||
{error && (
|
||||
<Overline type="error" error={error}>
|
||||
<Text id="app.special.modals.account.failed" />
|
||||
</Overline>
|
||||
)}
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
return (
|
||||
<Modal
|
||||
visible={true}
|
||||
onClose={onClose}
|
||||
title={<Text id={`app.special.modals.account.change.${field}`} />}
|
||||
actions={[
|
||||
{
|
||||
confirmation: true,
|
||||
onClick: handleSubmit(onSubmit),
|
||||
text:
|
||||
field === "email" ? (
|
||||
<Text id="app.special.modals.actions.send_email" />
|
||||
) : (
|
||||
<Text id="app.special.modals.actions.update" />
|
||||
),
|
||||
},
|
||||
{
|
||||
onClick: onClose,
|
||||
text: <Text id="app.special.modals.actions.close" />,
|
||||
},
|
||||
]}>
|
||||
{/* Preact / React typing incompatabilities */}
|
||||
<form
|
||||
onSubmit={
|
||||
handleSubmit(
|
||||
onSubmit,
|
||||
) as JSX.GenericEventHandler<HTMLFormElement>
|
||||
}>
|
||||
{field === "email" && (
|
||||
<FormField
|
||||
type="email"
|
||||
name="new_email"
|
||||
register={register}
|
||||
showOverline
|
||||
error={errors.new_email?.message}
|
||||
/>
|
||||
)}
|
||||
{field === "password" && (
|
||||
<FormField
|
||||
type="password"
|
||||
name="new_password"
|
||||
register={register}
|
||||
showOverline
|
||||
error={errors.new_password?.message}
|
||||
/>
|
||||
)}
|
||||
{field === "username" && (
|
||||
<FormField
|
||||
type="username"
|
||||
name="new_username"
|
||||
register={register}
|
||||
showOverline
|
||||
error={errors.new_username?.message}
|
||||
/>
|
||||
)}
|
||||
<FormField
|
||||
type="current_password"
|
||||
register={register}
|
||||
showOverline
|
||||
error={errors.current_password?.message}
|
||||
/>
|
||||
{error && (
|
||||
<Overline type="error" error={error}>
|
||||
<Text id="app.special.modals.account.failed" />
|
||||
</Overline>
|
||||
)}
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,27 +1,31 @@
|
||||
import { Text } from "preact-i18n";
|
||||
import styles from "./UserPicker.module.scss";
|
||||
import { useUsers } from "../../revoltjs/hooks";
|
||||
import { Text } from "preact-i18n";
|
||||
|
||||
import Modal from "../../../components/ui/Modal";
|
||||
|
||||
import { Friend } from "../../../pages/friends/Friend";
|
||||
import { useUsers } from "../../revoltjs/hooks";
|
||||
|
||||
interface Props {
|
||||
users: string[];
|
||||
onClose: () => void;
|
||||
users: string[];
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function PendingRequests({ users: ids, onClose }: Props) {
|
||||
const users = useUsers(ids);
|
||||
const users = useUsers(ids);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={true}
|
||||
title={<Text id="app.special.friends.pending" />}
|
||||
onClose={onClose}>
|
||||
<div className={styles.list}>
|
||||
{ users
|
||||
.filter(x => typeof x !== 'undefined')
|
||||
.map(x => <Friend user={x!} key={x!._id} />) }
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
return (
|
||||
<Modal
|
||||
visible={true}
|
||||
title={<Text id="app.special.friends.pending" />}
|
||||
onClose={onClose}>
|
||||
<div className={styles.list}>
|
||||
{users
|
||||
.filter((x) => typeof x !== "undefined")
|
||||
.map((x) => (
|
||||
<Friend user={x!} key={x!._id} />
|
||||
))}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,64 +1,68 @@
|
||||
import { User, Users } from "revolt.js/dist/api/objects";
|
||||
|
||||
import styles from "./UserPicker.module.scss";
|
||||
import { Text } from "preact-i18n";
|
||||
import { useState } from "preact/hooks";
|
||||
import styles from "./UserPicker.module.scss";
|
||||
import { useUsers } from "../../revoltjs/hooks";
|
||||
import Modal from "../../../components/ui/Modal";
|
||||
import { User, Users } from "revolt.js/dist/api/objects";
|
||||
|
||||
import UserCheckbox from "../../../components/common/user/UserCheckbox";
|
||||
import Modal from "../../../components/ui/Modal";
|
||||
|
||||
import { useUsers } from "../../revoltjs/hooks";
|
||||
|
||||
interface Props {
|
||||
omit?: string[];
|
||||
onClose: () => void;
|
||||
callback: (users: string[]) => Promise<void>;
|
||||
omit?: string[];
|
||||
onClose: () => void;
|
||||
callback: (users: string[]) => Promise<void>;
|
||||
}
|
||||
|
||||
export function UserPicker(props: Props) {
|
||||
const [selected, setSelected] = useState<string[]>([]);
|
||||
const omit = [...(props.omit || []), "00000000000000000000000000"];
|
||||
const [selected, setSelected] = useState<string[]>([]);
|
||||
const omit = [...(props.omit || []), "00000000000000000000000000"];
|
||||
|
||||
const users = useUsers();
|
||||
const users = useUsers();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={true}
|
||||
title={<Text id="app.special.popovers.user_picker.select" />}
|
||||
onClose={props.onClose}
|
||||
actions={[
|
||||
{
|
||||
text: <Text id="app.special.modals.actions.ok" />,
|
||||
onClick: () => props.callback(selected).then(props.onClose)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<div className={styles.list}>
|
||||
{(users.filter(
|
||||
x =>
|
||||
x &&
|
||||
x.relationship === Users.Relationship.Friend &&
|
||||
!omit.includes(x._id)
|
||||
) as User[])
|
||||
.map(x => {
|
||||
return {
|
||||
...x,
|
||||
selected: selected.includes(x._id)
|
||||
};
|
||||
})
|
||||
.map(x => (
|
||||
<UserCheckbox
|
||||
user={x}
|
||||
checked={x.selected}
|
||||
onChange={v => {
|
||||
if (v) {
|
||||
setSelected([...selected, x._id]);
|
||||
} else {
|
||||
setSelected(
|
||||
selected.filter(y => y !== x._id)
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
return (
|
||||
<Modal
|
||||
visible={true}
|
||||
title={<Text id="app.special.popovers.user_picker.select" />}
|
||||
onClose={props.onClose}
|
||||
actions={[
|
||||
{
|
||||
text: <Text id="app.special.modals.actions.ok" />,
|
||||
onClick: () => props.callback(selected).then(props.onClose),
|
||||
},
|
||||
]}>
|
||||
<div className={styles.list}>
|
||||
{(
|
||||
users.filter(
|
||||
(x) =>
|
||||
x &&
|
||||
x.relationship === Users.Relationship.Friend &&
|
||||
!omit.includes(x._id),
|
||||
) as User[]
|
||||
)
|
||||
.map((x) => {
|
||||
return {
|
||||
...x,
|
||||
selected: selected.includes(x._id),
|
||||
};
|
||||
})
|
||||
.map((x) => (
|
||||
<UserCheckbox
|
||||
user={x}
|
||||
checked={x.selected}
|
||||
onChange={(v) => {
|
||||
if (v) {
|
||||
setSelected([...selected, x._id]);
|
||||
} else {
|
||||
setSelected(
|
||||
selected.filter((y) => y !== x._id),
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,331 +1,362 @@
|
||||
import { decodeTime } from "ulid";
|
||||
import {
|
||||
Envelope,
|
||||
Edit,
|
||||
UserPlus,
|
||||
Shield,
|
||||
Money,
|
||||
} from "@styled-icons/boxicons-regular";
|
||||
import { Link, useHistory } from "react-router-dom";
|
||||
import { Localizer, Text } from "preact-i18n";
|
||||
import styles from "./UserProfile.module.scss";
|
||||
import Modal from "../../../components/ui/Modal";
|
||||
import { Route } from "revolt.js/dist/api/routes";
|
||||
import { Users } from "revolt.js/dist/api/objects";
|
||||
import { useIntermediate } from "../Intermediate";
|
||||
import Preloader from "../../../components/ui/Preloader";
|
||||
import Tooltip from '../../../components/common/Tooltip';
|
||||
import IconButton from "../../../components/ui/IconButton";
|
||||
import Markdown from '../../../components/markdown/Markdown';
|
||||
import { UserPermission } from "revolt.js/dist/api/permissions";
|
||||
import UserIcon from '../../../components/common/user/UserIcon';
|
||||
import ChannelIcon from '../../../components/common/ChannelIcon';
|
||||
import UserStatus from '../../../components/common/user/UserStatus';
|
||||
import { Envelope, Edit, UserPlus, Shield, Money } from "@styled-icons/boxicons-regular";
|
||||
import { Route } from "revolt.js/dist/api/routes";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
import styles from "./UserProfile.module.scss";
|
||||
import { Localizer, Text } from "preact-i18n";
|
||||
import { useContext, useEffect, useLayoutEffect, useState } from "preact/hooks";
|
||||
import { AppContext, ClientStatus, StatusContext } from "../../revoltjs/RevoltClient";
|
||||
import { useChannels, useForceUpdate, useUserPermission, useUsers } from "../../revoltjs/hooks";
|
||||
|
||||
import ChannelIcon from "../../../components/common/ChannelIcon";
|
||||
import Tooltip from "../../../components/common/Tooltip";
|
||||
import UserIcon from "../../../components/common/user/UserIcon";
|
||||
import UserStatus from "../../../components/common/user/UserStatus";
|
||||
import IconButton from "../../../components/ui/IconButton";
|
||||
import Modal from "../../../components/ui/Modal";
|
||||
import Preloader from "../../../components/ui/Preloader";
|
||||
|
||||
import Markdown from "../../../components/markdown/Markdown";
|
||||
import {
|
||||
AppContext,
|
||||
ClientStatus,
|
||||
StatusContext,
|
||||
} from "../../revoltjs/RevoltClient";
|
||||
import {
|
||||
useChannels,
|
||||
useForceUpdate,
|
||||
useUserPermission,
|
||||
useUsers,
|
||||
} from "../../revoltjs/hooks";
|
||||
import { useIntermediate } from "../Intermediate";
|
||||
|
||||
interface Props {
|
||||
user_id: string;
|
||||
dummy?: boolean;
|
||||
onClose: () => void;
|
||||
dummyProfile?: Users.Profile;
|
||||
user_id: string;
|
||||
dummy?: boolean;
|
||||
onClose: () => void;
|
||||
dummyProfile?: Users.Profile;
|
||||
}
|
||||
|
||||
enum Badges {
|
||||
Developer = 1,
|
||||
Translator = 2,
|
||||
Supporter = 4,
|
||||
ResponsibleDisclosure = 8,
|
||||
EarlyAdopter = 256
|
||||
Developer = 1,
|
||||
Translator = 2,
|
||||
Supporter = 4,
|
||||
ResponsibleDisclosure = 8,
|
||||
EarlyAdopter = 256,
|
||||
}
|
||||
|
||||
export function UserProfile({ user_id, onClose, dummy, dummyProfile }: Props) {
|
||||
const { openScreen, writeClipboard } = useIntermediate();
|
||||
const { openScreen, writeClipboard } = useIntermediate();
|
||||
|
||||
const [profile, setProfile] = useState<undefined | null | Users.Profile>(
|
||||
undefined
|
||||
);
|
||||
const [mutual, setMutual] = useState<
|
||||
undefined | null | Route<"GET", "/users/id/mutual">["response"]
|
||||
>(undefined);
|
||||
const [profile, setProfile] = useState<undefined | null | Users.Profile>(
|
||||
undefined,
|
||||
);
|
||||
const [mutual, setMutual] = useState<
|
||||
undefined | null | Route<"GET", "/users/id/mutual">["response"]
|
||||
>(undefined);
|
||||
|
||||
const history = useHistory();
|
||||
const client = useContext(AppContext);
|
||||
const status = useContext(StatusContext);
|
||||
const [tab, setTab] = useState("profile");
|
||||
const history = useHistory();
|
||||
const client = useContext(AppContext);
|
||||
const status = useContext(StatusContext);
|
||||
const [tab, setTab] = useState("profile");
|
||||
|
||||
const ctx = useForceUpdate();
|
||||
const all_users = useUsers(undefined, ctx);
|
||||
const channels = useChannels(undefined, ctx);
|
||||
|
||||
const user = all_users.find(x => x!._id === user_id);
|
||||
const users = mutual?.users ? all_users.filter(x => mutual.users.includes(x!._id)) : undefined;
|
||||
|
||||
if (!user) {
|
||||
useEffect(onClose, []);
|
||||
return null;
|
||||
}
|
||||
const ctx = useForceUpdate();
|
||||
const all_users = useUsers(undefined, ctx);
|
||||
const channels = useChannels(undefined, ctx);
|
||||
|
||||
const permissions = useUserPermission(user!._id, ctx);
|
||||
const user = all_users.find((x) => x!._id === user_id);
|
||||
const users = mutual?.users
|
||||
? all_users.filter((x) => mutual.users.includes(x!._id))
|
||||
: undefined;
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!user_id) return;
|
||||
if (typeof profile !== 'undefined') setProfile(undefined);
|
||||
if (typeof mutual !== 'undefined') setMutual(undefined);
|
||||
}, [user_id]);
|
||||
if (!user) {
|
||||
useEffect(onClose, []);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (dummy) {
|
||||
useLayoutEffect(() => {
|
||||
setProfile(dummyProfile);
|
||||
}, [dummyProfile]);
|
||||
}
|
||||
const permissions = useUserPermission(user!._id, ctx);
|
||||
|
||||
useEffect(() => {
|
||||
if (dummy) return;
|
||||
if (
|
||||
status === ClientStatus.ONLINE &&
|
||||
typeof mutual === "undefined"
|
||||
) {
|
||||
setMutual(null);
|
||||
client.users
|
||||
.fetchMutual(user_id)
|
||||
.then(data => setMutual(data));
|
||||
}
|
||||
}, [mutual, status]);
|
||||
useLayoutEffect(() => {
|
||||
if (!user_id) return;
|
||||
if (typeof profile !== "undefined") setProfile(undefined);
|
||||
if (typeof mutual !== "undefined") setMutual(undefined);
|
||||
}, [user_id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (dummy) return;
|
||||
if (
|
||||
status === ClientStatus.ONLINE &&
|
||||
typeof profile === "undefined"
|
||||
) {
|
||||
setProfile(null);
|
||||
if (dummy) {
|
||||
useLayoutEffect(() => {
|
||||
setProfile(dummyProfile);
|
||||
}, [dummyProfile]);
|
||||
}
|
||||
|
||||
if (permissions & UserPermission.ViewProfile) {
|
||||
client.users
|
||||
.fetchProfile(user_id)
|
||||
.then(data => setProfile(data))
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
}, [profile, status]);
|
||||
useEffect(() => {
|
||||
if (dummy) return;
|
||||
if (status === ClientStatus.ONLINE && typeof mutual === "undefined") {
|
||||
setMutual(null);
|
||||
client.users.fetchMutual(user_id).then((data) => setMutual(data));
|
||||
}
|
||||
}, [mutual, status]);
|
||||
|
||||
const mutualGroups = channels.filter(
|
||||
channel =>
|
||||
channel?.channel_type === "Group" &&
|
||||
channel.recipients.includes(user_id)
|
||||
);
|
||||
useEffect(() => {
|
||||
if (dummy) return;
|
||||
if (status === ClientStatus.ONLINE && typeof profile === "undefined") {
|
||||
setProfile(null);
|
||||
|
||||
const backgroundURL = profile && client.users.getBackgroundURL(profile, { width: 1000 }, true);
|
||||
const badges = (user.badges ?? 0) | (decodeTime(user._id) < 1623751765790 ? Badges.EarlyAdopter : 0);
|
||||
if (permissions & UserPermission.ViewProfile) {
|
||||
client.users
|
||||
.fetchProfile(user_id)
|
||||
.then((data) => setProfile(data))
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
}, [profile, status]);
|
||||
|
||||
return (
|
||||
<Modal visible
|
||||
border={dummy}
|
||||
padding={false}
|
||||
onClose={onClose}
|
||||
dontModal={dummy}>
|
||||
<div
|
||||
className={styles.header}
|
||||
data-force={
|
||||
profile?.background
|
||||
? "light"
|
||||
: undefined
|
||||
}
|
||||
style={{
|
||||
backgroundImage: backgroundURL && `linear-gradient( rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7) ), url('${backgroundURL}')`
|
||||
}}
|
||||
>
|
||||
<div className={styles.profile}>
|
||||
<UserIcon size={80} target={user} status />
|
||||
<div className={styles.details}>
|
||||
<Localizer>
|
||||
<span
|
||||
className={styles.username}
|
||||
onClick={() => writeClipboard(user.username)}>
|
||||
@{user.username}
|
||||
</span>
|
||||
</Localizer>
|
||||
{user.status?.text && (
|
||||
<span className={styles.status}>
|
||||
<UserStatus user={user} />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{user.relationship === Users.Relationship.Friend && (
|
||||
<Localizer>
|
||||
<Tooltip
|
||||
content={
|
||||
<Text id="app.context_menu.message_user" />
|
||||
}
|
||||
>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
onClose();
|
||||
history.push(`/open/${user_id}`);
|
||||
}}>
|
||||
<Envelope size={30} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Localizer>
|
||||
)}
|
||||
{user.relationship === Users.Relationship.User && (
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
onClose();
|
||||
if (dummy) return;
|
||||
history.push(`/settings/profile`);
|
||||
}}>
|
||||
<Edit size={28} />
|
||||
</IconButton>
|
||||
)}
|
||||
{(user.relationship === Users.Relationship.Incoming ||
|
||||
user.relationship === Users.Relationship.None) && (
|
||||
<IconButton onClick={() => client.users.addFriend(user.username)}>
|
||||
<UserPlus size={28} />
|
||||
</IconButton>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.tabs}>
|
||||
<div
|
||||
data-active={tab === "profile"}
|
||||
onClick={() => setTab("profile")}
|
||||
>
|
||||
<Text id="app.special.popovers.user_profile.profile" />
|
||||
</div>
|
||||
{ user.relationship !== Users.Relationship.User &&
|
||||
<>
|
||||
<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")}
|
||||
>
|
||||
<Text id="app.special.popovers.user_profile.mutual_groups" />
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
{tab === "profile" &&
|
||||
<div>
|
||||
{ !(profile?.content || (badges > 0)) &&
|
||||
<div className={styles.empty}><Text id="app.special.popovers.user_profile.empty" /></div> }
|
||||
{ (badges > 0) && <div className={styles.category}><Text id="app.special.popovers.user_profile.sub.badges" /></div> }
|
||||
{ (badges > 0) && (
|
||||
<div className={styles.badges}>
|
||||
<Localizer>
|
||||
{badges & Badges.Developer ? (
|
||||
<Tooltip
|
||||
content={
|
||||
<Text id="app.navigation.tabs.dev" />
|
||||
}
|
||||
>
|
||||
<img src="/assets/badges/developer.svg" />
|
||||
</Tooltip>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{badges & Badges.Translator ? (
|
||||
<Tooltip
|
||||
content={
|
||||
<Text id="app.special.popovers.user_profile.badges.translator" />
|
||||
}
|
||||
>
|
||||
<img src="/assets/badges/translator.svg" />
|
||||
</Tooltip>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{badges & Badges.EarlyAdopter ? (
|
||||
<Tooltip
|
||||
content={
|
||||
<Text id="app.special.popovers.user_profile.badges.early_adopter" />
|
||||
}
|
||||
>
|
||||
<img src="/assets/badges/early_adopter.svg" />
|
||||
</Tooltip>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{badges & Badges.Supporter ? (
|
||||
<Tooltip
|
||||
content={
|
||||
<Text id="app.special.popovers.user_profile.badges.supporter" />
|
||||
}
|
||||
>
|
||||
<Money size={32} color="#efab44" />
|
||||
</Tooltip>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{badges & Badges.ResponsibleDisclosure ? (
|
||||
<Tooltip
|
||||
content={
|
||||
<Text id="app.special.popovers.user_profile.badges.responsible_disclosure" />
|
||||
}
|
||||
>
|
||||
<Shield size={32} color="gray" />
|
||||
</Tooltip>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Localizer>
|
||||
</div>
|
||||
)}
|
||||
{ profile?.content && <div className={styles.category}><Text id="app.special.popovers.user_profile.sub.information" /></div> }
|
||||
<Markdown content={profile?.content} />
|
||||
{/*<div className={styles.category}><Text id="app.special.popovers.user_profile.sub.connections" /></div>*/}
|
||||
</div>}
|
||||
{tab === "friends" &&
|
||||
(users ? (
|
||||
<div className={styles.entries}>
|
||||
{users.length === 0 ? (
|
||||
<div className={styles.empty}>
|
||||
<Text id="app.special.popovers.user_profile.no_users" />
|
||||
</div>
|
||||
) : (
|
||||
users.map(
|
||||
x =>
|
||||
x && (
|
||||
<div onClick={() => openScreen({ id: 'profile', user_id: x._id })}
|
||||
className={styles.entry}
|
||||
key={x._id}>
|
||||
<UserIcon size={32} target={x} />
|
||||
<span>{x.username}</span>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Preloader type="ring" />
|
||||
))}
|
||||
{tab === "groups" && (
|
||||
<div className={styles.entries}>
|
||||
{mutualGroups.length === 0 ? (
|
||||
<div className={styles.empty}>
|
||||
<Text id="app.special.popovers.user_profile.no_groups" />
|
||||
</div>
|
||||
) : (
|
||||
mutualGroups.map(
|
||||
x =>
|
||||
x?.channel_type === "Group" && (
|
||||
<Link to={`/channel/${x._id}`}>
|
||||
<div
|
||||
className={styles.entry}
|
||||
key={x._id}
|
||||
>
|
||||
<ChannelIcon target={x} size={32} />
|
||||
<span>{x.name}</span>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
const mutualGroups = channels.filter(
|
||||
(channel) =>
|
||||
channel?.channel_type === "Group" &&
|
||||
channel.recipients.includes(user_id),
|
||||
);
|
||||
|
||||
const backgroundURL =
|
||||
profile &&
|
||||
client.users.getBackgroundURL(profile, { width: 1000 }, true);
|
||||
const badges =
|
||||
(user.badges ?? 0) |
|
||||
(decodeTime(user._id) < 1623751765790 ? Badges.EarlyAdopter : 0);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible
|
||||
border={dummy}
|
||||
padding={false}
|
||||
onClose={onClose}
|
||||
dontModal={dummy}>
|
||||
<div
|
||||
className={styles.header}
|
||||
data-force={profile?.background ? "light" : undefined}
|
||||
style={{
|
||||
backgroundImage:
|
||||
backgroundURL &&
|
||||
`linear-gradient( rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7) ), url('${backgroundURL}')`,
|
||||
}}>
|
||||
<div className={styles.profile}>
|
||||
<UserIcon size={80} target={user} status />
|
||||
<div className={styles.details}>
|
||||
<Localizer>
|
||||
<span
|
||||
className={styles.username}
|
||||
onClick={() => writeClipboard(user.username)}>
|
||||
@{user.username}
|
||||
</span>
|
||||
</Localizer>
|
||||
{user.status?.text && (
|
||||
<span className={styles.status}>
|
||||
<UserStatus user={user} />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{user.relationship === Users.Relationship.Friend && (
|
||||
<Localizer>
|
||||
<Tooltip
|
||||
content={
|
||||
<Text id="app.context_menu.message_user" />
|
||||
}>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
onClose();
|
||||
history.push(`/open/${user_id}`);
|
||||
}}>
|
||||
<Envelope size={30} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Localizer>
|
||||
)}
|
||||
{user.relationship === Users.Relationship.User && (
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
onClose();
|
||||
if (dummy) return;
|
||||
history.push(`/settings/profile`);
|
||||
}}>
|
||||
<Edit size={28} />
|
||||
</IconButton>
|
||||
)}
|
||||
{(user.relationship === Users.Relationship.Incoming ||
|
||||
user.relationship === Users.Relationship.None) && (
|
||||
<IconButton
|
||||
onClick={() =>
|
||||
client.users.addFriend(user.username)
|
||||
}>
|
||||
<UserPlus size={28} />
|
||||
</IconButton>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.tabs}>
|
||||
<div
|
||||
data-active={tab === "profile"}
|
||||
onClick={() => setTab("profile")}>
|
||||
<Text id="app.special.popovers.user_profile.profile" />
|
||||
</div>
|
||||
{user.relationship !== Users.Relationship.User && (
|
||||
<>
|
||||
<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")}>
|
||||
<Text id="app.special.popovers.user_profile.mutual_groups" />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
{tab === "profile" && (
|
||||
<div>
|
||||
{!(profile?.content || badges > 0) && (
|
||||
<div className={styles.empty}>
|
||||
<Text id="app.special.popovers.user_profile.empty" />
|
||||
</div>
|
||||
)}
|
||||
{badges > 0 && (
|
||||
<div className={styles.category}>
|
||||
<Text id="app.special.popovers.user_profile.sub.badges" />
|
||||
</div>
|
||||
)}
|
||||
{badges > 0 && (
|
||||
<div className={styles.badges}>
|
||||
<Localizer>
|
||||
{badges & Badges.Developer ? (
|
||||
<Tooltip
|
||||
content={
|
||||
<Text id="app.navigation.tabs.dev" />
|
||||
}>
|
||||
<img src="/assets/badges/developer.svg" />
|
||||
</Tooltip>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{badges & Badges.Translator ? (
|
||||
<Tooltip
|
||||
content={
|
||||
<Text id="app.special.popovers.user_profile.badges.translator" />
|
||||
}>
|
||||
<img src="/assets/badges/translator.svg" />
|
||||
</Tooltip>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{badges & Badges.EarlyAdopter ? (
|
||||
<Tooltip
|
||||
content={
|
||||
<Text id="app.special.popovers.user_profile.badges.early_adopter" />
|
||||
}>
|
||||
<img src="/assets/badges/early_adopter.svg" />
|
||||
</Tooltip>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{badges & Badges.Supporter ? (
|
||||
<Tooltip
|
||||
content={
|
||||
<Text id="app.special.popovers.user_profile.badges.supporter" />
|
||||
}>
|
||||
<Money size={32} color="#efab44" />
|
||||
</Tooltip>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{badges & Badges.ResponsibleDisclosure ? (
|
||||
<Tooltip
|
||||
content={
|
||||
<Text id="app.special.popovers.user_profile.badges.responsible_disclosure" />
|
||||
}>
|
||||
<Shield size={32} color="gray" />
|
||||
</Tooltip>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Localizer>
|
||||
</div>
|
||||
)}
|
||||
{profile?.content && (
|
||||
<div className={styles.category}>
|
||||
<Text id="app.special.popovers.user_profile.sub.information" />
|
||||
</div>
|
||||
)}
|
||||
<Markdown content={profile?.content} />
|
||||
{/*<div className={styles.category}><Text id="app.special.popovers.user_profile.sub.connections" /></div>*/}
|
||||
</div>
|
||||
)}
|
||||
{tab === "friends" &&
|
||||
(users ? (
|
||||
<div className={styles.entries}>
|
||||
{users.length === 0 ? (
|
||||
<div className={styles.empty}>
|
||||
<Text id="app.special.popovers.user_profile.no_users" />
|
||||
</div>
|
||||
) : (
|
||||
users.map(
|
||||
(x) =>
|
||||
x && (
|
||||
<div
|
||||
onClick={() =>
|
||||
openScreen({
|
||||
id: "profile",
|
||||
user_id: x._id,
|
||||
})
|
||||
}
|
||||
className={styles.entry}
|
||||
key={x._id}>
|
||||
<UserIcon
|
||||
size={32}
|
||||
target={x}
|
||||
/>
|
||||
<span>{x.username}</span>
|
||||
</div>
|
||||
),
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Preloader type="ring" />
|
||||
))}
|
||||
{tab === "groups" && (
|
||||
<div className={styles.entries}>
|
||||
{mutualGroups.length === 0 ? (
|
||||
<div className={styles.empty}>
|
||||
<Text id="app.special.popovers.user_profile.no_groups" />
|
||||
</div>
|
||||
) : (
|
||||
mutualGroups.map(
|
||||
(x) =>
|
||||
x?.channel_type === "Group" && (
|
||||
<Link to={`/channel/${x._id}`}>
|
||||
<div
|
||||
className={styles.entry}
|
||||
key={x._id}>
|
||||
<ChannelIcon
|
||||
target={x}
|
||||
size={32}
|
||||
/>
|
||||
<span>{x.name}</span>
|
||||
</div>
|
||||
</Link>
|
||||
),
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user