feat(modal): implement new server identity modal

closes #172
This commit is contained in:
Paul Makles 2022-06-18 15:54:17 +01:00
parent f685352963
commit 03e177f865
11 changed files with 171 additions and 145 deletions

View file

@ -73,7 +73,7 @@
"@hcaptcha/react-hcaptcha": "^0.3.6",
"@insertish/vite-plugin-babel-macros": "^1.0.5",
"@preact/preset-vite": "^2.0.0",
"@revoltchat/ui": "1.0.43",
"@revoltchat/ui": "1.0.45",
"@rollup/plugin-replace": "^2.4.2",
"@styled-icons/boxicons-logos": "^10.38.0",
"@styled-icons/boxicons-regular": "^10.38.0",

View file

@ -3,7 +3,13 @@ import { useHistory } from "react-router-dom";
import { API, Channel, Message, Server, User } from "revolt.js";
import { createContext } from "preact";
import { useContext, useEffect, useMemo, useState } from "preact/hooks";
import {
StateUpdater,
useContext,
useEffect,
useMemo,
useState,
} from "preact/hooks";
import type { Action } from "@revoltchat/ui/esm/components/design/atoms/display/Modal";
@ -128,12 +134,11 @@ interface Props {
children: Children;
}
export let __thisIsAHack;
export let __thisIsAHack: StateUpdater<Screen>;
export default function Intermediate(props: Props) {
const [screen, openScreen] = useState<Screen>({ id: "none" });
__thisIsAHack = openScreen;
const settings = useApplicationState().settings;
const history = useHistory();
const value = {

View file

@ -1,14 +1,11 @@
import { useContext } from "preact/hooks";
import { internalEmit } from "../../lib/eventEmitter";
import { IntermediateContext, useIntermediate } from "./Intermediate";
import { SpecialInputModal } from "./modals/Input";
import { SpecialPromptModal } from "./modals/Prompt";
import { ChannelInfo } from "./popovers/ChannelInfo";
import { CreateBotModal } from "./popovers/CreateBot";
import { ImageViewer } from "./popovers/ImageViewer";
import { ServerIdentityModal } from "./popovers/ServerIdentityModal";
import { UserPicker } from "./popovers/UserPicker";
import { UserProfile } from "./popovers/UserProfile";
@ -42,9 +39,6 @@ export default function Popovers() {
case "special_input":
// @ts-expect-error someone figure this out :)
return <SpecialInputModal onClose={onClose} {...screen} />;
case "server_identity":
// @ts-expect-error someone figure this out :)
return <ServerIdentityModal onClose={onClose} {...screen} />;
}
return null;

View file

@ -1,17 +0,0 @@
.identityMain {
display: flex;
flex-direction: row;
gap: 10px;
> div {
display: flex;
flex-direction: column;
gap: 10px;
}
.buttons {
display: flex;
flex-direction: row;
gap: 10px;
}
}

View file

@ -1,106 +0,0 @@
import { observer } from "mobx-react-lite";
import { Server } from "revolt.js";
import styles from "./ServerIdentityModal.module.scss";
import { Text } from "preact-i18n";
import { useEffect, useState } from "preact/hooks";
import { Button, Category, InputBox, Modal } from "@revoltchat/ui";
import { noop } from "../../../lib/js";
import { FileUploader } from "../../revoltjs/FileUploads";
import { useClient } from "../../revoltjs/RevoltClient";
interface Props {
server: Server;
onClose: () => void;
}
export const ServerIdentityModal = observer(({ server, onClose }: Props) => {
const client = useClient();
const member = client.members.getKey({
server: server._id,
user: client.user!._id,
});
if (!member) return null;
const [nickname, setNickname] = useState("");
const [currentNickname, setCurrentNickname] = useState("");
useEffect(() => {
setNickname(member.nickname ?? "");
setCurrentNickname(member.nickname ?? "");
}, [member.nickname]);
return (
<Modal
title={
<Text
id={"app.special.popovers.server_identity.title"}
fields={{ server: server.name }}
/>
}
onClose={onClose}>
<div className={styles.identityMain}>
<div>
<Category>
<Text id="app.special.popovers.server_identity.avatar" />
</Category>
<FileUploader
width={80}
height={80}
style="icon"
fileType="avatars"
behaviour="upload"
maxFileSize={4_000_000}
onUpload={(avatar) =>
member.edit({ avatar }).then(noop)
}
remove={() =>
member.edit({ remove: ["Avatar"] }).then(noop)
}
defaultPreview={client.user?.generateAvatarURL(
{
max_side: 256,
},
false,
)}
previewURL={client.generateFileURL(
member.avatar ?? undefined,
{ max_side: 256 },
true,
)}
desaturateDefault
/>
</div>
<div>
<Category>
<Text id="app.special.popovers.server_identity.nickname" />
</Category>
<InputBox
value={nickname}
placeholder={client.user!.username}
onChange={(e) => setNickname(e.currentTarget.value)}
/>
<div className={styles.buttons}>
<Button
disabled={nickname === currentNickname}
onClick={() => member.edit({ nickname })}>
<Text id="app.special.modals.actions.save" />
</Button>
{currentNickname !== "" && (
<Button
palette="plain"
onClick={() =>
member.edit({ remove: ["Nickname"] })
}>
<Text id="app.special.modals.actions.remove" />
</Button>
)}
</div>
</div>
</div>
</Modal>
);
});

View file

@ -0,0 +1,138 @@
import { X } from "@styled-icons/boxicons-regular";
import { Save } from "@styled-icons/boxicons-solid";
import { observer } from "mobx-react-lite";
import styled from "styled-components";
import { Text } from "preact-i18n";
import { useMemo, useState } from "preact/hooks";
import {
Button,
Category,
Centred,
Column,
InputBox,
Modal,
Row,
Message,
} from "@revoltchat/ui";
import { noop } from "../../../lib/js";
import { FileUploader } from "../../revoltjs/FileUploads";
import { ModalProps } from "../types";
const Preview = styled(Centred)`
flex-grow: 1;
border-radius: var(--border-radius);
background: var(--secondary-background);
> div {
padding: 0;
}
`;
export default observer(
({ member, ...props }: ModalProps<"server_identity">) => {
const [nickname, setNickname] = useState(member.nickname ?? "");
const message: any = useMemo(() => {
return {
author: member.user!,
member: {
...member,
nickname,
},
};
}, []);
return (
<Modal
{...props}
title={
<Text
id={"app.special.popovers.server_identity.title"}
fields={{ server: member.server!.name }}
/>
}>
<Column gap="18px">
<Column>
<Category compact>
<Text id="app.special.popovers.server_identity.nickname" />
</Category>
<Row centred>
<InputBox
value={nickname}
placeholder={member.user!.username}
onChange={(e) =>
setNickname(e.currentTarget.value)
}
/>
<Button
compact="icon"
palette="secondary"
disabled={
nickname === member.nickname || !nickname
}
onClick={() => member.edit({ nickname })}>
<Save size={24} />
</Button>
<Button
compact="icon"
palette="secondary"
disabled={!member.nickname}
onClick={() =>
member
.edit({ remove: ["Nickname"] })
.then(() => setNickname(""))
}>
<X size={24} />
</Button>
</Row>
</Column>
<Row gap="18px">
<Column>
<Category compact>
<Text id="app.special.popovers.server_identity.avatar" />
</Category>
<FileUploader
width={80}
height={80}
style="icon"
fileType="avatars"
behaviour="upload"
maxFileSize={4_000_000}
onUpload={(avatar) =>
member.edit({ avatar }).then(noop)
}
remove={() =>
member
.edit({ remove: ["Avatar"] })
.then(noop)
}
defaultPreview={member.user?.generateAvatarURL(
{
max_side: 256,
},
false,
)}
previewURL={member.client.generateFileURL(
member.avatar ?? undefined,
{ max_side: 256 },
true,
)}
desaturateDefault
/>
</Column>
<Column grow>
<Category compact>Preview</Category>
<Preview>
<Message message={message} head />
</Preview>
</Column>
</Row>
</Column>
</Modal>
);
},
);

View file

@ -25,6 +25,7 @@ import MFARecovery from "./components/MFARecovery";
import ModifyAccount from "./components/ModifyAccount";
import OutOfDate from "./components/OutOfDate";
import PendingFriendRequests from "./components/PendingFriendRequests";
import ServerIdentity from "./components/ServerIdentity";
import ShowToken from "./components/ShowToken";
import SignOutSessions from "./components/SignOutSessions";
import SignedOut from "./components/SignedOut";
@ -211,6 +212,7 @@ export const modalController = new ModalControllerExtended({
modify_account: ModifyAccount,
out_of_date: OutOfDate,
pending_friend_requests: PendingFriendRequests,
server_identity: ServerIdentity,
show_token: ShowToken,
signed_out: SignedOut,
sign_out_sessions: SignOutSessions,

View file

@ -1,4 +1,4 @@
import { API, Client, User } from "revolt.js";
import { API, Client, User, Member } from "revolt.js";
export type Modal = {
key?: string;
@ -65,6 +65,10 @@ export type Modal = {
client: Client;
field: "username" | "email" | "password";
}
| {
type: "server_identity";
member: Member;
}
| {
type: "signed_out";
}

View file

@ -11,6 +11,8 @@ import { IconButton, Preloader } from "@revoltchat/ui";
import { determineFileSize } from "../../lib/fileSize";
import { useApplicationState } from "../../mobx/State";
import { useIntermediate } from "../intermediate/Intermediate";
import { modalController } from "../modals";
import { AppContext } from "./RevoltClient";
@ -113,7 +115,7 @@ export function grabFiles(
export function FileUploader(props: Props) {
const { fileType, maxFileSize, remove } = props;
const { openScreen } = useIntermediate();
const client = useContext(AppContext);
const client = useApplicationState().client!;
const [uploading, setUploading] = useState(false);

View file

@ -10,6 +10,7 @@ import {
API,
Permission,
UserPermission,
Member,
} from "revolt.js";
import {
@ -101,7 +102,7 @@ type Action =
| { action: "close_dm"; target: Channel }
| { action: "leave_server"; target: Server }
| { action: "delete_server"; target: Server }
| { action: "edit_identity"; target: Server }
| { action: "edit_identity"; target: Member }
| {
action: "open_notification_options";
channel?: Channel;
@ -399,9 +400,9 @@ export default function ContextMenus() {
break;
case "edit_identity":
openScreen({
id: "server_identity",
server: data.target,
modalController.push({
type: "server_identity",
member: data.target,
});
break;
@ -952,7 +953,10 @@ export default function ContextMenus() {
serverPermissions & Permission.ChangeAvatar
)
generateAction(
{ action: "edit_identity", target: server },
{
action: "edit_identity",
target: server.member!,
},
"edit_identity",
);

View file

@ -2231,9 +2231,9 @@ __metadata:
languageName: node
linkType: hard
"@revoltchat/ui@npm:1.0.43":
version: 1.0.43
resolution: "@revoltchat/ui@npm:1.0.43"
"@revoltchat/ui@npm:1.0.45":
version: 1.0.45
resolution: "@revoltchat/ui@npm:1.0.45"
dependencies:
"@styled-icons/boxicons-logos": ^10.38.0
"@styled-icons/boxicons-regular": ^10.38.0
@ -2246,7 +2246,7 @@ __metadata:
react-device-detect: "*"
react-virtuoso: "*"
revolt.js: "*"
checksum: d6a6d0cb4a2f08fea45a4d61e5599894012fbb591472ef95d34ee8ddc9e66cfdc7626e94360b7c104e59d3c64a7d0bd674d6a42f5c3cefc723574db8c1aee64e
checksum: 3a4eef546b1ad941dfd47381705b5121b06e8f6781699813fd3ac9a4d559470e2a720af6123cbea7ad4d5d24a7805ebd9c1f384f7a09d2eb73ff83daf69e6a1c
languageName: node
linkType: hard
@ -3539,7 +3539,7 @@ __metadata:
"@hcaptcha/react-hcaptcha": ^0.3.6
"@insertish/vite-plugin-babel-macros": ^1.0.5
"@preact/preset-vite": ^2.0.0
"@revoltchat/ui": 1.0.43
"@revoltchat/ui": 1.0.45
"@rollup/plugin-replace": ^2.4.2
"@styled-icons/boxicons-logos": ^10.38.0
"@styled-icons/boxicons-regular": ^10.38.0