Fix elements not observing permissions.

This commit is contained in:
Paul 2021-07-31 12:09:18 +01:00
parent 463aa79356
commit 29cc221a34
6 changed files with 301 additions and 282 deletions

View file

@ -97,7 +97,7 @@
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-scroll": "^1.8.2", "react-scroll": "^1.8.2",
"redux": "^4.1.0", "redux": "^4.1.0",
"revolt.js": "5.0.0-alpha.12", "revolt.js": "5.0.0-alpha.14",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"sass": "^1.35.1", "sass": "^1.35.1",
"shade-blend-color": "^1.0.0", "shade-blend-color": "^1.0.0",

View file

@ -1,5 +1,6 @@
import { Send, ShieldX } from "@styled-icons/boxicons-solid"; import { Send, ShieldX } from "@styled-icons/boxicons-solid";
import Axios, { CancelTokenSource } from "axios"; import Axios, { CancelTokenSource } from "axios";
import { observer } from "mobx-react-lite";
import { ChannelPermission } from "revolt.js/dist/api/permissions"; import { ChannelPermission } from "revolt.js/dist/api/permissions";
import { Channel } from "revolt.js/dist/maps/Channels"; import { Channel } from "revolt.js/dist/maps/Channels";
import styled from "styled-components"; import styled from "styled-components";
@ -108,7 +109,7 @@ const Action = styled.div`
// ! FIXME: add to app config and load from app config // ! FIXME: add to app config and load from app config
export const CAN_UPLOAD_AT_ONCE = 4; export const CAN_UPLOAD_AT_ONCE = 4;
export default function MessageBox({ channel }: Props) { export default observer(({ channel }: Props) => {
const [draft, setDraft] = useState(getState().drafts[channel._id] ?? ""); const [draft, setDraft] = useState(getState().drafts[channel._id] ?? "");
const [uploadState, setUploadState] = useState<UploadState>({ const [uploadState, setUploadState] = useState<UploadState>({
@ -506,4 +507,4 @@ export default function MessageBox({ channel }: Props) {
</Base> </Base>
</> </>
); );
} });

View file

@ -1,5 +1,6 @@
import { Money } from "@styled-icons/boxicons-regular"; import { Money } from "@styled-icons/boxicons-regular";
import { Envelope, Edit, UserPlus, Shield } from "@styled-icons/boxicons-solid"; import { Envelope, Edit, UserPlus, Shield } from "@styled-icons/boxicons-solid";
import { observer } from "mobx-react-lite";
import { Link, useHistory } from "react-router-dom"; import { Link, useHistory } from "react-router-dom";
import { Profile, RelationshipStatus } from "revolt-api/types/Users"; import { Profile, RelationshipStatus } from "revolt-api/types/Users";
import { UserPermission } from "revolt.js/dist/api/permissions"; import { UserPermission } from "revolt.js/dist/api/permissions";
@ -41,297 +42,314 @@ enum Badges {
EarlyAdopter = 256, EarlyAdopter = 256,
} }
export function UserProfile({ user_id, onClose, dummy, dummyProfile }: Props) { export const UserProfile = observer(
const { openScreen, writeClipboard } = useIntermediate(); ({ user_id, onClose, dummy, dummyProfile }: Props) => {
const { openScreen, writeClipboard } = useIntermediate();
const [profile, setProfile] = useState<undefined | null | Profile>( const [profile, setProfile] = useState<undefined | null | Profile>(
undefined, undefined,
); );
const [mutual, setMutual] = useState< const [mutual, setMutual] = useState<
undefined | null | Route<"GET", "/users/id/mutual">["response"] undefined | null | Route<"GET", "/users/id/mutual">["response"]
>(undefined); >(undefined);
const history = useHistory(); const history = useHistory();
const client = useClient(); const client = useClient();
const status = useContext(StatusContext); const status = useContext(StatusContext);
const [tab, setTab] = useState("profile"); const [tab, setTab] = useState("profile");
const user = client.users.get(user_id); const user = client.users.get(user_id);
if (!user) { if (!user) {
useEffect(onClose, []); useEffect(onClose, []);
return null; return null;
} }
const users = mutual?.users.map((id) => client.users.get(id)); const users = mutual?.users.map((id) => client.users.get(id));
const mutualGroups = [...client.channels.values()].filter( const mutualGroups = [...client.channels.values()].filter(
(channel) => (channel) =>
channel?.channel_type === "Group" && channel?.channel_type === "Group" &&
channel.recipient_ids!.includes(user_id), channel.recipient_ids!.includes(user_id),
); );
useLayoutEffect(() => {
if (!user_id) return;
if (typeof profile !== "undefined") setProfile(undefined);
if (typeof mutual !== "undefined") setMutual(undefined);
}, [user_id]);
if (dummy) {
useLayoutEffect(() => { useLayoutEffect(() => {
setProfile(dummyProfile); if (!user_id) return;
}, [dummyProfile]); if (typeof profile !== "undefined") setProfile(undefined);
} if (typeof mutual !== "undefined") setMutual(undefined);
}, [user_id]);
useEffect(() => { if (dummy) {
if (dummy) return; useLayoutEffect(() => {
if (status === ClientStatus.ONLINE && typeof mutual === "undefined") { setProfile(dummyProfile);
setMutual(null); }, [dummyProfile]);
user.fetchMutual().then(setMutual);
} }
}, [mutual, status]);
useEffect(() => { useEffect(() => {
if (dummy) return; if (dummy) return;
if (status === ClientStatus.ONLINE && typeof profile === "undefined") { if (
setProfile(null); status === ClientStatus.ONLINE &&
typeof mutual === "undefined"
if (user.permission & UserPermission.ViewProfile) { ) {
user.fetchProfile() setMutual(null);
.then(setProfile) user.fetchMutual().then(setMutual);
.catch(() => {});
} }
} }, [mutual, status]);
}, [profile, status]);
const backgroundURL = useEffect(() => {
profile && if (dummy) return;
client.generateFileURL(profile.background, { width: 1000 }, true); if (
const badges = user.badges ?? 0; status === ClientStatus.ONLINE &&
typeof profile === "undefined"
) {
setProfile(null);
return ( if (user.permission & UserPermission.ViewProfile) {
<Modal user.fetchProfile()
visible .then(setProfile)
border={dummy} .catch(() => {});
padding={false} }
onClose={onClose} }
dontModal={dummy}> }, [profile, status]);
<div
className={styles.header} const backgroundURL =
data-force={profile?.background ? "light" : undefined} profile &&
style={{ client.generateFileURL(profile.background, { width: 1000 }, true);
backgroundImage: const badges = user.badges ?? 0;
backgroundURL &&
`linear-gradient( rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7) ), url('${backgroundURL}')`, return (
}}> <Modal
<div className={styles.profile}> visible
<UserIcon size={80} target={user} status animate /> border={dummy}
<div className={styles.details}> padding={false}
<Localizer> onClose={onClose}
<span dontModal={dummy}>
className={styles.username} <div
onClick={() => writeClipboard(user.username)}> className={styles.header}
@{user.username} data-force={profile?.background ? "light" : undefined}
</span> style={{
</Localizer> backgroundImage:
{user.status?.text && ( backgroundURL &&
<span className={styles.status}> `linear-gradient( rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7) ), url('${backgroundURL}')`,
<UserStatus user={user} tooltip /> }}>
</span> <div className={styles.profile}>
<UserIcon size={80} target={user} status animate />
<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} tooltip />
</span>
)}
</div>
{user.relationship === RelationshipStatus.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 === RelationshipStatus.User && (
<IconButton
onClick={() => {
onClose();
if (dummy) return;
history.push(`/settings/profile`);
}}>
<Edit size={28} />
</IconButton>
)}
{(user.relationship === RelationshipStatus.Incoming ||
user.relationship === RelationshipStatus.None) && (
<IconButton onClick={() => user.addFriend()}>
<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 !== RelationshipStatus.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>
{user.relationship === RelationshipStatus.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 === RelationshipStatus.User && (
<IconButton
onClick={() => {
onClose();
if (dummy) return;
history.push(`/settings/profile`);
}}>
<Edit size={28} />
</IconButton>
)}
{(user.relationship === RelationshipStatus.Incoming ||
user.relationship === RelationshipStatus.None) && (
<IconButton onClick={() => user.addFriend()}>
<UserPlus size={28} />
</IconButton>
)}
</div> </div>
<div className={styles.tabs}> <div className={styles.content}>
<div {tab === "profile" && (
data-active={tab === "profile"} <div>
onClick={() => setTab("profile")}> {!(profile?.content || badges > 0) && (
<Text id="app.special.popovers.user_profile.profile" />
</div>
{user.relationship !== RelationshipStatus.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}> <div className={styles.empty}>
<Text id="app.special.popovers.user_profile.no_users" /> <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> </div>
) : ( ) : (
users.map( mutualGroups.map(
(x) => (x) =>
x && ( x?.channel_type === "Group" && (
<div <Link to={`/channel/${x._id}`}>
onClick={() => <div
openScreen({ className={styles.entry}
id: "profile", key={x._id}>
user_id: x._id, <ChannelIcon
}) target={x}
} size={32}
className={styles.entry} />
key={x._id}> <span>{x.name}</span>
<UserIcon </div>
size={32} </Link>
target={x}
/>
<span>{x.username}</span>
</div>
), ),
) )
)} )}
</div> </div>
) : ( )}
<Preloader type="ring" /> </div>
))} </Modal>
{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>
);
}

View file

@ -30,7 +30,7 @@ export default function ChannelSettings() {
switch (channel?.channel_type) { switch (channel?.channel_type) {
case "TextChannel": case "TextChannel":
case "VoiceChannel": case "VoiceChannel":
base_url = `/server/${channel.server}/channel/${cid}/settings`; base_url = `/server/${channel.server_id}/channel/${cid}/settings`;
break; break;
default: default:
base_url = `/channel/${cid}/settings`; base_url = `/channel/${cid}/settings`;

View file

@ -27,8 +27,6 @@ export const Members = observer(({ server }: Props) => {
{ members: Member[]; users: User[] } | undefined { members: Member[]; users: User[] } | undefined
>(undefined); >(undefined);
const client = useClient();
useEffect(() => { useEffect(() => {
server.fetchMembers().then(setData); server.fetchMembers().then(setData);
}, []); }, []);
@ -50,10 +48,12 @@ export const Members = observer(({ server }: Props) => {
{data && {data &&
data.members.length > 0 && data.members.length > 0 &&
data.members data.members
.map((member, index) => { .map((member) => {
return { return {
member, member,
user: data.users[index], user: data.users.find(
(x) => x._id === member._id.user,
),
}; };
}) })
.map(({ member, user }) => ( .map(({ member, user }) => (

View file

@ -3570,10 +3570,10 @@ revolt-api@0.5.1-alpha.10-patch.0:
resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.1-alpha.10-patch.0.tgz#97d31bec7dfa4573567097443acb059c4feaac20" resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.1-alpha.10-patch.0.tgz#97d31bec7dfa4573567097443acb059c4feaac20"
integrity sha512-UyM890HkGlYNQOxpHuEpUsJHLt8Ujnjg9/zPEDGpbvS4iy0jmHX23Hh8tOCfb/ewxbNrtT3G1HpSWKOneW/vYg== integrity sha512-UyM890HkGlYNQOxpHuEpUsJHLt8Ujnjg9/zPEDGpbvS4iy0jmHX23Hh8tOCfb/ewxbNrtT3G1HpSWKOneW/vYg==
revolt.js@5.0.0-alpha.12: revolt.js@5.0.0-alpha.14:
version "5.0.0-alpha.12" version "5.0.0-alpha.14"
resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-5.0.0-alpha.12.tgz#06777f74f6a79161b18e02938a8d60d465395066" resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-5.0.0-alpha.14.tgz#13b1d350a89467eb2ad6905a290ee1fada4150c1"
integrity sha512-QqgawsSjrFTKhMA5JBKii6MgOJ4VO2u1GBmjTiKzPR2krnoWHdPSbD7VvD2scMGMPXIaIxU3zA++tyN8mfzIFg== integrity sha512-kZBIx9PX8Y8Esu51Y6OgeFwlpajtaRv/ap3YKlWEELlAcDAEDoSZj+iL4ilkxIxvh4RDJMlVlAforwSvXvy9DQ==
dependencies: dependencies:
axios "^0.19.2" axios "^0.19.2"
eventemitter3 "^4.0.7" eventemitter3 "^4.0.7"