mirror of
https://github.com/spacebarchat/client.git
synced 2024-11-24 19:32:34 +01:00
implement changing avatar
This commit is contained in:
parent
686431e900
commit
329a14ccf5
@ -17,11 +17,11 @@ import useLogger from "./hooks/useLogger";
|
|||||||
import AppPage from "./pages/AppPage";
|
import AppPage from "./pages/AppPage";
|
||||||
import LogoutPage from "./pages/LogoutPage";
|
import LogoutPage from "./pages/LogoutPage";
|
||||||
import ChannelPage from "./pages/subpages/ChannelPage";
|
import ChannelPage from "./pages/subpages/ChannelPage";
|
||||||
import { useAppStore } from "./stores/AppStore";
|
|
||||||
import { Globals } from "./utils/Globals";
|
import { Globals } from "./utils/Globals";
|
||||||
// @ts-expect-error no types
|
// @ts-expect-error no types
|
||||||
import FPSStats from "react-fps-stats";
|
import FPSStats from "react-fps-stats";
|
||||||
import { bannerController } from "./controllers/banners";
|
import { bannerController } from "./controllers/banners";
|
||||||
|
import { useAppStore } from "./hooks/useAppStore";
|
||||||
import { isTauri } from "./utils/Utils";
|
import { isTauri } from "./utils/Utils";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { PresenceUpdateStatus } from "@spacebarchat/spacebar-api-types/v9";
|
import { PresenceUpdateStatus } from "@spacebarchat/spacebar-api-types/v9";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import React from "react";
|
import React, { useRef } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
import { useAppStore } from "../hooks/useAppStore";
|
||||||
import AccountStore from "../stores/AccountStore";
|
import AccountStore from "../stores/AccountStore";
|
||||||
import { useAppStore } from "../stores/AppStore";
|
|
||||||
import Presence from "../stores/objects/Presence";
|
import Presence from "../stores/objects/Presence";
|
||||||
import User from "../stores/objects/User";
|
import User from "../stores/objects/User";
|
||||||
import Container from "./Container";
|
import Container from "./Container";
|
||||||
@ -33,7 +33,7 @@ interface Props {
|
|||||||
function Avatar(props: Props) {
|
function Avatar(props: Props) {
|
||||||
const app = useAppStore();
|
const app = useAppStore();
|
||||||
|
|
||||||
const ref = React.useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const user = props.user ?? app.account;
|
const user = props.user ?? app.account;
|
||||||
if (!user) return null;
|
if (!user) return null;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { useAppStore } from "../stores/AppStore";
|
import { useAppStore } from "../hooks/useAppStore";
|
||||||
import Icon, { IconProps } from "./Icon";
|
import Icon, { IconProps } from "./Icon";
|
||||||
import { SectionHeader } from "./SectionHeader";
|
import { SectionHeader } from "./SectionHeader";
|
||||||
import Floating from "./floating/Floating";
|
import Floating from "./floating/Floating";
|
||||||
|
@ -2,7 +2,7 @@ import { ChannelType } from "@spacebarchat/spacebar-api-types/v9";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { AutoSizer, List, ListRowProps } from "react-virtualized";
|
import { AutoSizer, List, ListRowProps } from "react-virtualized";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import ChannelListItem from "./ChannelListItem";
|
import ChannelListItem from "./ChannelListItem";
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
|
@ -4,13 +4,13 @@ import { useNavigate } from "react-router-dom";
|
|||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { ContextMenuContext } from "../../contexts/ContextMenuContext";
|
import { ContextMenuContext } from "../../contexts/ContextMenuContext";
|
||||||
import { modalController } from "../../controllers/modals";
|
import { modalController } from "../../controllers/modals";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
|
||||||
import Channel from "../../stores/objects/Channel";
|
import Channel from "../../stores/objects/Channel";
|
||||||
import { Permissions } from "../../utils/Permissions";
|
import { Permissions } from "../../utils/Permissions";
|
||||||
import Icon from "../Icon";
|
import Icon from "../Icon";
|
||||||
import SidebarPill from "../SidebarPill";
|
import SidebarPill from "../SidebarPill";
|
||||||
import Floating from "../floating/Floating";
|
import Floating from "../floating/Floating";
|
||||||
import FloatingTrigger from "../floating/FloatingTrigger";
|
import FloatingTrigger from "../floating/FloatingTrigger";
|
||||||
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
|
|
||||||
const ListItem = styled.div<{ isCategory?: boolean }>`
|
const ListItem = styled.div<{ isCategory?: boolean }>`
|
||||||
padding: ${(props) => (props.isCategory ? "16px 8px 0 0" : "1px 8px 0 0")};
|
padding: ${(props) => (props.isCategory ? "16px 8px 0 0" : "1px 8px 0 0")};
|
||||||
|
@ -4,8 +4,8 @@ import React, { useContext } from "react";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { ContextMenuContext } from "../contexts/ContextMenuContext";
|
import { ContextMenuContext } from "../contexts/ContextMenuContext";
|
||||||
|
import { useAppStore } from "../hooks/useAppStore";
|
||||||
import useLogger from "../hooks/useLogger";
|
import useLogger from "../hooks/useLogger";
|
||||||
import { useAppStore } from "../stores/AppStore";
|
|
||||||
import Guild from "../stores/objects/Guild";
|
import Guild from "../stores/objects/Guild";
|
||||||
import { Permissions } from "../utils/Permissions";
|
import { Permissions } from "../utils/Permissions";
|
||||||
import REST from "../utils/REST";
|
import REST from "../utils/REST";
|
||||||
@ -30,9 +30,7 @@ const Wrapper = styled(Container)<{ active?: boolean; hasImage?: boolean }>`
|
|||||||
border-radius: ${(props) => (props.active ? "30%" : "50%")};
|
border-radius: ${(props) => (props.active ? "30%" : "50%")};
|
||||||
background-color: ${(props) =>
|
background-color: ${(props) =>
|
||||||
props.hasImage ? "transparent" : props.active ? "var(--primary)" : "var(--background-secondary)"};
|
props.hasImage ? "transparent" : props.active ? "var(--primary)" : "var(--background-secondary)"};
|
||||||
transition:
|
transition: border-radius 0.2s ease, background-color 0.2s ease;
|
||||||
border-radius 0.2s ease,
|
|
||||||
background-color 0.2s ease;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-radius: 30%;
|
border-radius: 30%;
|
||||||
|
@ -4,7 +4,7 @@ import { useNavigate } from "react-router-dom";
|
|||||||
import { AutoSizer, List, ListRowProps } from "react-virtualized";
|
import { AutoSizer, List, ListRowProps } from "react-virtualized";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { modalController } from "../controllers/modals";
|
import { modalController } from "../controllers/modals";
|
||||||
import { useAppStore } from "../stores/AppStore";
|
import { useAppStore } from "../hooks/useAppStore";
|
||||||
import GuildItem, { GuildSidebarListItem } from "./GuildItem";
|
import GuildItem, { GuildSidebarListItem } from "./GuildItem";
|
||||||
import SidebarAction from "./SidebarAction";
|
import SidebarAction from "./SidebarAction";
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { useAppStore } from "../hooks/useAppStore";
|
||||||
import LoadingPage from "../pages/LoadingPage";
|
import LoadingPage from "../pages/LoadingPage";
|
||||||
import { useAppStore } from "../stores/AppStore";
|
|
||||||
import { isTauri } from "../utils/Utils";
|
import { isTauri } from "../utils/Utils";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
@ -2,7 +2,7 @@ import { autorun } from "mobx";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import GuildMemberListStore from "../../stores/GuildMemberListStore";
|
import GuildMemberListStore from "../../stores/GuildMemberListStore";
|
||||||
import ListSection from "../ListSection";
|
import ListSection from "../ListSection";
|
||||||
import MemberListItem from "./MemberListItem";
|
import MemberListItem from "./MemberListItem";
|
||||||
|
@ -3,7 +3,7 @@ import { observer } from "mobx-react-lite";
|
|||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { ContextMenuContext } from "../../contexts/ContextMenuContext";
|
import { ContextMenuContext } from "../../contexts/ContextMenuContext";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import GuildMember from "../../stores/objects/GuildMember";
|
import GuildMember from "../../stores/objects/GuildMember";
|
||||||
import Avatar from "../Avatar";
|
import Avatar from "../Avatar";
|
||||||
import Floating from "../floating/Floating";
|
import Floating from "../floating/Floating";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { modalController } from "../controllers/modals";
|
import { modalController } from "../controllers/modals";
|
||||||
import { useAppStore } from "../stores/AppStore";
|
import { useAppStore } from "../hooks/useAppStore";
|
||||||
import Avatar from "./Avatar";
|
import Avatar from "./Avatar";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import IconButton from "./IconButton";
|
import IconButton from "./IconButton";
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
import { ChannelType } from "@spacebarchat/spacebar-api-types/v9";
|
import { ChannelType } from "@spacebarchat/spacebar-api-types/v9";
|
||||||
import { modalController } from "../../controllers/modals";
|
import { modalController } from "../../controllers/modals";
|
||||||
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import useLogger from "../../hooks/useLogger";
|
import useLogger from "../../hooks/useLogger";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
|
||||||
import Guild from "../../stores/objects/Guild";
|
import Guild from "../../stores/objects/Guild";
|
||||||
import { ContextMenu, ContextMenuButton, ContextMenuDivider } from "./ContextMenu";
|
import { ContextMenu, ContextMenuButton, ContextMenuDivider } from "./ContextMenu";
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { modalController } from "../../controllers/modals";
|
import { modalController } from "../../controllers/modals";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import Message from "../../stores/objects/Message";
|
import Message from "../../stores/objects/Message";
|
||||||
import { ContextMenu, ContextMenuButton, ContextMenuDivider } from "./ContextMenu";
|
import { ContextMenu, ContextMenuButton, ContextMenuDivider } from "./ContextMenu";
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// loosely based on https://github.com/revoltchat/frontend/blob/master/components/app/menus/UserContextMenu.tsx
|
// loosely based on https://github.com/revoltchat/frontend/blob/master/components/app/menus/UserContextMenu.tsx
|
||||||
|
|
||||||
import { modalController } from "../../controllers/modals";
|
import { modalController } from "../../controllers/modals";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import GuildMember from "../../stores/objects/GuildMember";
|
import GuildMember from "../../stores/objects/GuildMember";
|
||||||
import User from "../../stores/objects/User";
|
import User from "../../stores/objects/User";
|
||||||
import { ContextMenu, ContextMenuButton, ContextMenuDivider } from "./ContextMenu";
|
import { ContextMenu, ContextMenuButton, ContextMenuDivider } from "./ContextMenu";
|
||||||
|
@ -3,7 +3,7 @@ import useLogger from "../../hooks/useLogger";
|
|||||||
|
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { modalController } from "../../controllers/modals";
|
import { modalController } from "../../controllers/modals";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import { Permissions } from "../../utils/Permissions";
|
import { Permissions } from "../../utils/Permissions";
|
||||||
import { ContextMenu, ContextMenuButton, ContextMenuDivider } from "../contextMenus/ContextMenu";
|
import { ContextMenu, ContextMenuButton, ContextMenuDivider } from "../contextMenus/ContextMenu";
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import { HorizontalDivider } from "../Divider";
|
|||||||
import { CDNRoutes, ImageFormat } from "@spacebarchat/spacebar-api-types/v9";
|
import { CDNRoutes, ImageFormat } from "@spacebarchat/spacebar-api-types/v9";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import SpacebarLogoBlue from "../../assets/images/logo/Spacebar_Icon.svg?react";
|
import SpacebarLogoBlue from "../../assets/images/logo/Spacebar_Icon.svg?react";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import REST from "../../utils/REST";
|
import REST from "../../utils/REST";
|
||||||
import Floating from "./Floating";
|
import Floating from "./Floating";
|
||||||
import FloatingTrigger from "./FloatingTrigger";
|
import FloatingTrigger from "./FloatingTrigger";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Navigate } from "react-router-dom";
|
import { Navigate } from "react-router-dom";
|
||||||
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import { LoadingSuspense } from "../../pages/LoadingPage";
|
import { LoadingSuspense } from "../../pages/LoadingPage";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
component: React.FC;
|
component: React.FC;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Navigate } from "react-router-dom";
|
import { Navigate } from "react-router-dom";
|
||||||
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import { LoadingSuspense } from "../../pages/LoadingPage";
|
import { LoadingSuspense } from "../../pages/LoadingPage";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
component: React.FC;
|
component: React.FC;
|
||||||
|
@ -2,7 +2,7 @@ import React, { memo } from "react";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { ContextMenuContext } from "../../contexts/ContextMenuContext";
|
import { ContextMenuContext } from "../../contexts/ContextMenuContext";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import Channel from "../../stores/objects/Channel";
|
import Channel from "../../stores/objects/Channel";
|
||||||
import Role from "../../stores/objects/Role";
|
import Role from "../../stores/objects/Role";
|
||||||
import User from "../../stores/objects/User";
|
import User from "../../stores/objects/User";
|
||||||
|
@ -2,8 +2,8 @@ import { runInAction } from "mobx";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import useLogger from "../../hooks/useLogger";
|
import useLogger from "../../hooks/useLogger";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
|
||||||
import Channel from "../../stores/objects/Channel";
|
import Channel from "../../stores/objects/Channel";
|
||||||
import Guild from "../../stores/objects/Guild";
|
import Guild from "../../stores/objects/Guild";
|
||||||
import MemberList from "../MemberList/MemberList";
|
import MemberList from "../MemberList/MemberList";
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import * as Icons from "@mdi/js";
|
import * as Icons from "@mdi/js";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import useLogger from "../../hooks/useLogger";
|
import useLogger from "../../hooks/useLogger";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
|
||||||
import Channel from "../../stores/objects/Channel";
|
import Channel from "../../stores/objects/Channel";
|
||||||
import Icon from "../Icon";
|
import Icon from "../Icon";
|
||||||
import { SectionHeader } from "../SectionHeader";
|
import { SectionHeader } from "../SectionHeader";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { memo, useContext } from "react";
|
import { memo, useContext } from "react";
|
||||||
import { ContextMenuContext } from "../../contexts/ContextMenuContext";
|
import { ContextMenuContext } from "../../contexts/ContextMenuContext";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import { MessageLike } from "../../stores/objects/Message";
|
import { MessageLike } from "../../stores/objects/Message";
|
||||||
import { QueuedMessageStatus } from "../../stores/objects/QueuedMessage";
|
import { QueuedMessageStatus } from "../../stores/objects/QueuedMessage";
|
||||||
import Avatar from "../Avatar";
|
import Avatar from "../Avatar";
|
||||||
|
@ -2,8 +2,8 @@ import { observer } from "mobx-react-lite";
|
|||||||
import React, { useContext } from "react";
|
import React, { useContext } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { ContextMenuContext } from "../../contexts/ContextMenuContext";
|
import { ContextMenuContext } from "../../contexts/ContextMenuContext";
|
||||||
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import useLogger from "../../hooks/useLogger";
|
import useLogger from "../../hooks/useLogger";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
|
||||||
import Guild from "../../stores/objects/Guild";
|
import Guild from "../../stores/objects/Guild";
|
||||||
import GuildMember from "../../stores/objects/GuildMember";
|
import GuildMember from "../../stores/objects/GuildMember";
|
||||||
import { MessageLike } from "../../stores/objects/Message";
|
import { MessageLike } from "../../stores/objects/Message";
|
||||||
@ -36,7 +36,7 @@ function MessageAuthor({ message, guild }: Props) {
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!eventData) return;
|
if (!eventData) return;
|
||||||
contextMenu.onContextMenu(eventData, { type: "user", user: message.author, member });
|
contextMenu?.onContextMenu(eventData, { type: "user", user: message.author, member });
|
||||||
}, [eventData, member]);
|
}, [eventData, member]);
|
||||||
|
|
||||||
const onContextMenu = async (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
const onContextMenu = async (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||||
@ -68,7 +68,7 @@ function MessageAuthor({ message, guild }: Props) {
|
|||||||
style={{
|
style={{
|
||||||
color,
|
color,
|
||||||
}}
|
}}
|
||||||
ref={contextMenu.setReferenceElement}
|
ref={contextMenu?.setReferenceElement}
|
||||||
onContextMenu={onContextMenu}
|
onContextMenu={onContextMenu}
|
||||||
>
|
>
|
||||||
{message.author.username}
|
{message.author.username}
|
||||||
|
@ -6,8 +6,8 @@ import React from "react";
|
|||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { modalController } from "../../controllers/modals";
|
import { modalController } from "../../controllers/modals";
|
||||||
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import useLogger from "../../hooks/useLogger";
|
import useLogger from "../../hooks/useLogger";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
|
||||||
import Guild from "../../stores/objects/Guild";
|
import Guild from "../../stores/objects/Guild";
|
||||||
import Snowflake from "../../utils/Snowflake";
|
import Snowflake from "../../utils/Snowflake";
|
||||||
import { MAX_ATTACHMENTS } from "../../utils/constants";
|
import { MAX_ATTACHMENTS } from "../../utils/constants";
|
||||||
@ -197,7 +197,7 @@ function MessageInput({ channel }: Props) {
|
|||||||
channel.type === ChannelType.DM
|
channel.type === ChannelType.DM
|
||||||
? channel.recipients?.[0].username
|
? channel.recipients?.[0].username
|
||||||
: "#" + channel.name
|
: "#" + channel.name
|
||||||
}`
|
}`
|
||||||
: "You do not have permission to send messages in this channel."
|
: "You do not have permission to send messages in this channel."
|
||||||
}
|
}
|
||||||
disabled={!channel.hasPermission("SEND_MESSAGES")}
|
disabled={!channel.hasPermission("SEND_MESSAGES")}
|
||||||
|
@ -4,8 +4,8 @@ import InfiniteScroll from "react-infinite-scroll-component";
|
|||||||
import PulseLoader from "react-spinners/PulseLoader";
|
import PulseLoader from "react-spinners/PulseLoader";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import useResizeObserver from "use-resize-observer";
|
import useResizeObserver from "use-resize-observer";
|
||||||
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import useLogger from "../../hooks/useLogger";
|
import useLogger from "../../hooks/useLogger";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
|
||||||
import { MessageGroup as MessageGroupType } from "../../stores/MessageStore";
|
import { MessageGroup as MessageGroupType } from "../../stores/MessageStore";
|
||||||
import Channel from "../../stores/objects/Channel";
|
import Channel from "../../stores/objects/Channel";
|
||||||
import Guild from "../../stores/objects/Guild";
|
import Guild from "../../stores/objects/Guild";
|
||||||
@ -30,6 +30,7 @@ const EndMessageContainer = styled.div`
|
|||||||
interface Props {
|
interface Props {
|
||||||
guild: Guild;
|
guild: Guild;
|
||||||
channel: Channel;
|
channel: Channel;
|
||||||
|
before?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { useAppStore } from "../../../stores/AppStore";
|
import { useAppStore } from "../../../hooks/useAppStore";
|
||||||
import QueuedMessage from "../../../stores/objects/QueuedMessage";
|
import QueuedMessage from "../../../stores/objects/QueuedMessage";
|
||||||
import { bytesToSize } from "../../../utils/Utils";
|
import { bytesToSize } from "../../../utils/Utils";
|
||||||
import Icon from "../../Icon";
|
import Icon from "../../Icon";
|
||||||
|
@ -4,7 +4,7 @@ import { useForm } from "react-hook-form";
|
|||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
import { ModalProps, modalController } from "../../controllers/modals";
|
import { ModalProps, modalController } from "../../controllers/modals";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import { Modal } from "./ModalComponents";
|
import { Modal } from "./ModalComponents";
|
||||||
|
|
||||||
const DescriptionText = styled.p`
|
const DescriptionText = styled.p`
|
||||||
@ -54,7 +54,7 @@ export function BanMemberModal({ target, type, ...props }: ModalProps<"ban_membe
|
|||||||
data.reason
|
data.reason
|
||||||
? {
|
? {
|
||||||
"X-Audit-Log-Reason": data.reason,
|
"X-Audit-Log-Reason": data.reason,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
@ -11,7 +11,7 @@ import { useNavigate } from "react-router-dom";
|
|||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
import { ModalProps, modalController } from "../../controllers/modals";
|
import { ModalProps, modalController } from "../../controllers/modals";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import { messageFromFieldError } from "../../utils/messageFromFieldError";
|
import { messageFromFieldError } from "../../utils/messageFromFieldError";
|
||||||
import { Input, InputErrorText } from "../AuthComponents";
|
import { Input, InputErrorText } from "../AuthComponents";
|
||||||
import { TextDivider } from "../Divider";
|
import { TextDivider } from "../Divider";
|
||||||
|
@ -4,8 +4,8 @@ import React from "react";
|
|||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { ModalProps } from "../../controllers/modals/types";
|
import { ModalProps } from "../../controllers/modals/types";
|
||||||
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import useLogger from "../../hooks/useLogger";
|
import useLogger from "../../hooks/useLogger";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
|
||||||
import { messageFromFieldError } from "../../utils/messageFromFieldError";
|
import { messageFromFieldError } from "../../utils/messageFromFieldError";
|
||||||
import { Input, InputErrorText, InputLabel, LabelWrapper } from "../AuthComponents";
|
import { Input, InputErrorText, InputLabel, LabelWrapper } from "../AuthComponents";
|
||||||
import Button from "../Button";
|
import Button from "../Button";
|
||||||
|
@ -4,8 +4,8 @@ import { useForm } from "react-hook-form";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { ModalProps, modalController } from "../../controllers/modals";
|
import { ModalProps, modalController } from "../../controllers/modals";
|
||||||
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import useLogger from "../../hooks/useLogger";
|
import useLogger from "../../hooks/useLogger";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
|
||||||
import { messageFromFieldError } from "../../utils/messageFromFieldError";
|
import { messageFromFieldError } from "../../utils/messageFromFieldError";
|
||||||
import { Input, InputErrorText, InputLabel, InputWrapper, LabelWrapper } from "../AuthComponents";
|
import { Input, InputErrorText, InputLabel, InputWrapper, LabelWrapper } from "../AuthComponents";
|
||||||
import { TextDivider } from "../Divider";
|
import { TextDivider } from "../Divider";
|
||||||
|
@ -3,8 +3,8 @@ import { useForm } from "react-hook-form";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { ModalProps, modalController } from "../../controllers/modals";
|
import { ModalProps, modalController } from "../../controllers/modals";
|
||||||
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import useLogger from "../../hooks/useLogger";
|
import useLogger from "../../hooks/useLogger";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
|
||||||
import { messageFromFieldError } from "../../utils/messageFromFieldError";
|
import { messageFromFieldError } from "../../utils/messageFromFieldError";
|
||||||
import { Input, InputErrorText, InputLabel, LabelWrapper } from "../AuthComponents";
|
import { Input, InputErrorText, InputLabel, LabelWrapper } from "../AuthComponents";
|
||||||
import { TextDivider } from "../Divider";
|
import { TextDivider } from "../Divider";
|
||||||
|
@ -4,7 +4,7 @@ import { useForm } from "react-hook-form";
|
|||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
import { ModalProps, modalController } from "../../controllers/modals";
|
import { ModalProps, modalController } from "../../controllers/modals";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import { Modal } from "./ModalComponents";
|
import { Modal } from "./ModalComponents";
|
||||||
|
|
||||||
const DescriptionText = styled.p`
|
const DescriptionText = styled.p`
|
||||||
@ -54,7 +54,7 @@ export function KickMemberModal({ target, ...props }: ModalProps<"kick_member">)
|
|||||||
data.reason
|
data.reason
|
||||||
? {
|
? {
|
||||||
"X-Audit-Log-Reason": data.reason,
|
"X-Audit-Log-Reason": data.reason,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
@ -2,8 +2,8 @@ import { Routes } from "@spacebarchat/spacebar-api-types/v9";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { ModalProps, modalController } from "../../controllers/modals";
|
import { ModalProps, modalController } from "../../controllers/modals";
|
||||||
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import useLogger from "../../hooks/useLogger";
|
import useLogger from "../../hooks/useLogger";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
|
||||||
import { Modal } from "./ModalComponents";
|
import { Modal } from "./ModalComponents";
|
||||||
|
|
||||||
export function LeaveServerModal({ target, ...props }: ModalProps<"leave_server">) {
|
export function LeaveServerModal({ target, ...props }: ModalProps<"leave_server">) {
|
||||||
|
@ -2,7 +2,7 @@ import { observer } from "mobx-react-lite";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import styled, { css } from "styled-components";
|
import styled, { css } from "styled-components";
|
||||||
import { ModalProps, modalController } from "../../controllers/modals";
|
import { ModalProps, modalController } from "../../controllers/modals";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import { isTauri } from "../../utils/Utils";
|
import { isTauri } from "../../utils/Utils";
|
||||||
import { APP_VERSION, GIT_BRANCH, GIT_REVISION, REPO_URL } from "../../utils/revison";
|
import { APP_VERSION, GIT_BRANCH, GIT_REVISION, REPO_URL } from "../../utils/revison";
|
||||||
import Icon from "../Icon";
|
import Icon from "../Icon";
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
|
import { RESTPatchAPICurrentUserJSONBody, Routes } from "@spacebarchat/spacebar-api-types/v9";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import styled, { css } from "styled-components";
|
import styled, { css } from "styled-components";
|
||||||
import { useAppStore } from "../../../stores/AppStore";
|
import { useAppStore } from "../../../hooks/useAppStore";
|
||||||
|
import Avatar from "../../Avatar";
|
||||||
|
import Button from "../../Button";
|
||||||
import SectionTitle from "../../SectionTitle";
|
import SectionTitle from "../../SectionTitle";
|
||||||
|
|
||||||
const Content = styled.div`
|
const Content = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
min-width: 30vw;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const UserInfoContainer = styled.div`
|
const UserInfoContainer = styled.div`
|
||||||
@ -77,25 +82,152 @@ const FieldValueToggle = styled.button`
|
|||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const IconContainer = styled.div`
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const IconInput = styled.input`
|
||||||
|
display: none;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const FileInput = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const UnsavedChangesBar = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
background-color: var(--background-tertiary);
|
||||||
|
padding: 10px 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-top: 24px;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const UnsavedChangedActions = styled.div`
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Text = styled.p`
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
`;
|
||||||
|
|
||||||
function AccountSettingsPage() {
|
function AccountSettingsPage() {
|
||||||
const app = useAppStore();
|
const app = useAppStore();
|
||||||
const [shouldRedactEmail, setShouldRedactEmail] = useState(true);
|
const [shouldRedactEmail, setShouldRedactEmail] = useState(true);
|
||||||
|
const [selectedFile, setSelectedFile] = useState<File>();
|
||||||
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
const [hasUnsavedChangd, setHasUnsavedChanged] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const redactEmail = (email: string) => {
|
const redactEmail = (email: string) => {
|
||||||
const [username, domain] = email.split("@");
|
const [username, domain] = email.split("@");
|
||||||
return `${"*".repeat(username.length)}@${domain}`;
|
return `${"*".repeat(username.length)}@${domain}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const refactPhoneNumber = (phoneNumber: string) => {
|
const redactPhoneNumber = (phoneNumber: string) => {
|
||||||
const lastFour = phoneNumber.slice(-4);
|
const lastFour = phoneNumber.slice(-4);
|
||||||
return "*".repeat(phoneNumber.length - 4) + lastFour;
|
return "*".repeat(phoneNumber.length - 4) + lastFour;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onAvatarChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (!event.target.files) return;
|
||||||
|
setSelectedFile(event.target.files[0]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const discardChanges = () => {
|
||||||
|
setSelectedFile(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
const save = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
if (!selectedFile) return;
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = async () => {
|
||||||
|
const payload: RESTPatchAPICurrentUserJSONBody = {
|
||||||
|
// @ts-expect-error broken types or whatever
|
||||||
|
avatar: reader.result,
|
||||||
|
};
|
||||||
|
app.rest
|
||||||
|
.patch<RESTPatchAPICurrentUserJSONBody, RESTPatchAPICurrentUserJSONBody>(Routes.user(), payload)
|
||||||
|
.then((r) => {
|
||||||
|
// runInAction(() => {
|
||||||
|
// if (r.username) app.account!.username = r.username;
|
||||||
|
// if (r.avatar) app.account!.avatar = r.avatar;
|
||||||
|
// });
|
||||||
|
setSelectedFile(undefined);
|
||||||
|
setLoading(false);
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(selectedFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// handle unsaved changes state
|
||||||
|
if (selectedFile) {
|
||||||
|
setHasUnsavedChanged(true);
|
||||||
|
} else {
|
||||||
|
// Reset state if there is nothing changed
|
||||||
|
setHasUnsavedChanged(false);
|
||||||
|
}
|
||||||
|
}, [selectedFile]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SectionTitle>Account</SectionTitle>
|
<SectionTitle>Account</SectionTitle>
|
||||||
<Content>
|
<Content>
|
||||||
<UserInfoContainer>
|
<UserInfoContainer>
|
||||||
|
<Field spacerBottom>
|
||||||
|
<IconContainer>
|
||||||
|
{selectedFile ? (
|
||||||
|
<img
|
||||||
|
src={URL.createObjectURL(selectedFile)}
|
||||||
|
alt="Avatar"
|
||||||
|
width="80px"
|
||||||
|
height="80px"
|
||||||
|
style={{
|
||||||
|
borderRadius: "50%",
|
||||||
|
pointerEvents: "none",
|
||||||
|
objectFit: "cover",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Avatar user={app.account!} size={80} />
|
||||||
|
)}
|
||||||
|
<IconInput
|
||||||
|
ref={fileInputRef}
|
||||||
|
type="file"
|
||||||
|
name="avatar"
|
||||||
|
accept="image/*"
|
||||||
|
onChange={onAvatarChange}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
<FileInput
|
||||||
|
role="button"
|
||||||
|
onClick={() => fileInputRef.current?.click()}
|
||||||
|
aria-disabled={loading}
|
||||||
|
/>
|
||||||
|
</IconContainer>
|
||||||
|
</Field>
|
||||||
|
|
||||||
<Field spacerBottom>
|
<Field spacerBottom>
|
||||||
<Row>
|
<Row>
|
||||||
<FieldTitle>Username</FieldTitle>
|
<FieldTitle>Username</FieldTitle>
|
||||||
@ -138,6 +270,20 @@ function AccountSettingsPage() {
|
|||||||
</Row>
|
</Row>
|
||||||
</Field>
|
</Field>
|
||||||
</UserInfoContainer>
|
</UserInfoContainer>
|
||||||
|
|
||||||
|
{hasUnsavedChangd && (
|
||||||
|
<UnsavedChangesBar>
|
||||||
|
<Text>You have unsaved changes.</Text>
|
||||||
|
<UnsavedChangedActions>
|
||||||
|
<Button palette="link" onClick={discardChanges} disabled={loading}>
|
||||||
|
Discard
|
||||||
|
</Button>
|
||||||
|
<Button palette="primary" disabled={loading} onClick={save}>
|
||||||
|
{loading ? "Saving..." : "Save"}
|
||||||
|
</Button>
|
||||||
|
</UnsavedChangedActions>
|
||||||
|
</UnsavedChangesBar>
|
||||||
|
)}
|
||||||
</Content>
|
</Content>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { useAppStore } from "../../../stores/AppStore";
|
import { useAppStore } from "../../../hooks/useAppStore";
|
||||||
import { EXPERIMENT_LIST, Experiment as ExperimentType } from "../../../stores/ExperimentsStore";
|
import { EXPERIMENT_LIST, Experiment as ExperimentType } from "../../../stores/ExperimentsStore";
|
||||||
import SectionTitle from "../../SectionTitle";
|
import SectionTitle from "../../SectionTitle";
|
||||||
|
|
||||||
|
41
src/contexts/ContextMenuContext.ts
Normal file
41
src/contexts/ContextMenuContext.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import { useFloating } from "@floating-ui/react";
|
||||||
|
import React from "react";
|
||||||
|
import Channel from "../stores/objects/Channel";
|
||||||
|
import Guild from "../stores/objects/Guild";
|
||||||
|
import GuildMember from "../stores/objects/GuildMember";
|
||||||
|
import { MessageLike } from "../stores/objects/Message";
|
||||||
|
import User from "../stores/objects/User";
|
||||||
|
|
||||||
|
export type ContextMenuProps =
|
||||||
|
| {
|
||||||
|
type: "user";
|
||||||
|
user: User;
|
||||||
|
member?: GuildMember;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "message";
|
||||||
|
message: MessageLike;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "channel";
|
||||||
|
channel: Channel;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "channelMention";
|
||||||
|
channel: Channel;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "guild";
|
||||||
|
guild: Guild;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ContextMenuContextType = {
|
||||||
|
setReferenceElement: ReturnType<typeof useFloating>["refs"]["setReference"];
|
||||||
|
onContextMenu: (e: React.MouseEvent, props: ContextMenuProps) => void;
|
||||||
|
close: () => void;
|
||||||
|
open: (props: ContextMenuProps) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error not specifying a default value here
|
||||||
|
export const ContextMenuContext = React.createContext<ContextMenuContextType>();
|
@ -1,45 +1,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
import { FloatingPortal } from "@floating-ui/react";
|
||||||
import { FloatingPortal, useFloating } from "@floating-ui/react";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import useContextMenu, { ContextMenuComponents } from "../hooks/useContextMenu";
|
import useContextMenu, { ContextMenuComponents } from "../hooks/useContextMenu";
|
||||||
import Channel from "../stores/objects/Channel";
|
import { ContextMenuContext, ContextMenuProps } from "./ContextMenuContext";
|
||||||
import Guild from "../stores/objects/Guild";
|
|
||||||
import GuildMember from "../stores/objects/GuildMember";
|
|
||||||
import { MessageLike } from "../stores/objects/Message";
|
|
||||||
import User from "../stores/objects/User";
|
|
||||||
|
|
||||||
export type ContextMenuProps =
|
|
||||||
| {
|
|
||||||
type: "user";
|
|
||||||
user: User;
|
|
||||||
member?: GuildMember;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: "message";
|
|
||||||
message: MessageLike;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: "channel";
|
|
||||||
channel: Channel;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: "channelMention";
|
|
||||||
channel: Channel;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: "guild";
|
|
||||||
guild: Guild;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ContextMenuContextType = {
|
|
||||||
setReferenceElement: ReturnType<typeof useFloating>["refs"]["setReference"];
|
|
||||||
onContextMenu: (e: React.MouseEvent, props: ContextMenuProps) => void;
|
|
||||||
close: () => void;
|
|
||||||
open: (props: ContextMenuProps) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
// @ts-expect-error not specifying a default value here
|
|
||||||
export const ContextMenuContext = React.createContext<ContextMenuContextType>();
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export const ContextMenuContextProvider: React.FC<any> = ({ children }) => {
|
export const ContextMenuContextProvider: React.FC<any> = ({ children }) => {
|
||||||
@ -53,7 +15,7 @@ export const ContextMenuContextProvider: React.FC<any> = ({ children }) => {
|
|||||||
? ContextMenuComponents[contextMenu.props.type]
|
? ContextMenuComponents[contextMenu.props.type]
|
||||||
: () => {
|
: () => {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContextMenuContext.Provider
|
<ContextMenuContext.Provider
|
@ -1,6 +1,6 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { createGlobalStyle } from "styled-components";
|
import { createGlobalStyle } from "styled-components";
|
||||||
import { useAppStore } from "../stores/AppStore";
|
import { useAppStore } from "../hooks/useAppStore";
|
||||||
import { rgbToHsl } from "../utils/Utils";
|
import { rgbToHsl } from "../utils/Utils";
|
||||||
|
|
||||||
const font: ThemeFont["font"] = {
|
const font: ThemeFont["font"] = {
|
||||||
|
7
src/hooks/useAppStore.ts
Normal file
7
src/hooks/useAppStore.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import AppStore from "../stores/AppStore";
|
||||||
|
|
||||||
|
export const appStore = new AppStore();
|
||||||
|
|
||||||
|
export function useAppStore() {
|
||||||
|
return appStore;
|
||||||
|
}
|
@ -19,7 +19,7 @@ import ReactDOM from "react-dom/client";
|
|||||||
import { BrowserRouter } from "react-router-dom";
|
import { BrowserRouter } from "react-router-dom";
|
||||||
import { ErrorBoundaryContext } from "react-use-error-boundary";
|
import { ErrorBoundaryContext } from "react-use-error-boundary";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
import { ContextMenuContextProvider } from "./contexts/ContextMenuContext";
|
import { ContextMenuContextProvider } from "./contexts/ContextMenuContextProvider";
|
||||||
import Theme from "./contexts/Theme";
|
import Theme from "./contexts/Theme";
|
||||||
import ModalRenderer from "./controllers/modals/ModalRenderer";
|
import ModalRenderer from "./controllers/modals/ModalRenderer";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
|
@ -5,7 +5,7 @@ import styled from "styled-components";
|
|||||||
import SpacebarLogoBlue from "../assets/images/logo/Logo-Blue.svg?react";
|
import SpacebarLogoBlue from "../assets/images/logo/Logo-Blue.svg?react";
|
||||||
import Button from "../components/Button";
|
import Button from "../components/Button";
|
||||||
import Container from "../components/Container";
|
import Container from "../components/Container";
|
||||||
import { useAppStore } from "../stores/AppStore";
|
import { useAppStore } from "../hooks/useAppStore";
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -23,8 +23,9 @@ import {
|
|||||||
} from "../components/AuthComponents";
|
} from "../components/AuthComponents";
|
||||||
import { TextDivider } from "../components/Divider";
|
import { TextDivider } from "../components/Divider";
|
||||||
import HCaptcha, { HeaderContainer } from "../components/HCaptcha";
|
import HCaptcha, { HeaderContainer } from "../components/HCaptcha";
|
||||||
|
import { useAppStore } from "../hooks/useAppStore";
|
||||||
import useLogger from "../hooks/useLogger";
|
import useLogger from "../hooks/useLogger";
|
||||||
import { AUTH_NO_BRANDING, useAppStore } from "../stores/AppStore";
|
import { AUTH_NO_BRANDING } from "../stores/AppStore";
|
||||||
import { Globals } from "../utils/Globals";
|
import { Globals } from "../utils/Globals";
|
||||||
import REST from "../utils/REST";
|
import REST from "../utils/REST";
|
||||||
import { RouteSettings } from "../utils/constants";
|
import { RouteSettings } from "../utils/constants";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { useAppStore } from "../stores/AppStore";
|
import { useAppStore } from "../hooks/useAppStore";
|
||||||
|
|
||||||
function LogoutPage() {
|
function LogoutPage() {
|
||||||
const app = useAppStore();
|
const app = useAppStore();
|
||||||
|
@ -24,8 +24,9 @@ import {
|
|||||||
import DOBInput from "../components/DOBInput";
|
import DOBInput from "../components/DOBInput";
|
||||||
import { TextDivider } from "../components/Divider";
|
import { TextDivider } from "../components/Divider";
|
||||||
import HCaptcha from "../components/HCaptcha";
|
import HCaptcha from "../components/HCaptcha";
|
||||||
|
import { useAppStore } from "../hooks/useAppStore";
|
||||||
import useLogger from "../hooks/useLogger";
|
import useLogger from "../hooks/useLogger";
|
||||||
import { AUTH_NO_BRANDING, useAppStore } from "../stores/AppStore";
|
import { AUTH_NO_BRANDING } from "../stores/AppStore";
|
||||||
import { IAPILoginResponseSuccess, IAPIRegisterRequest, IAPIRegisterResponseError } from "../utils/interfaces/api";
|
import { IAPILoginResponseSuccess, IAPIRegisterRequest, IAPIRegisterResponseError } from "../utils/interfaces/api";
|
||||||
import { messageFromFieldError } from "../utils/messageFromFieldError";
|
import { messageFromFieldError } from "../utils/messageFromFieldError";
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import GuildSidebar from "../../components/GuildSidebar";
|
|||||||
import SwipeableLayout from "../../components/SwipeableLayout";
|
import SwipeableLayout from "../../components/SwipeableLayout";
|
||||||
import Chat from "../../components/messaging/Chat";
|
import Chat from "../../components/messaging/Chat";
|
||||||
import BannerRenderer from "../../controllers/banners/BannerRenderer";
|
import BannerRenderer from "../../controllers/banners/BannerRenderer";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
|
|
||||||
const Container = styled(ContainerComponent)`
|
const Container = styled(ContainerComponent)`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -20,8 +20,8 @@ import {
|
|||||||
Wrapper,
|
Wrapper,
|
||||||
} from "../../components/AuthComponents";
|
} from "../../components/AuthComponents";
|
||||||
import { TextDivider } from "../../components/Divider";
|
import { TextDivider } from "../../components/Divider";
|
||||||
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import useLogger from "../../hooks/useLogger";
|
import useLogger from "../../hooks/useLogger";
|
||||||
import { useAppStore } from "../../stores/AppStore";
|
|
||||||
import {
|
import {
|
||||||
IAPIError,
|
IAPIError,
|
||||||
IAPILoginResponseMFARequired,
|
IAPILoginResponseMFARequired,
|
||||||
|
@ -189,9 +189,3 @@ export default class AppStore {
|
|||||||
this.loadUpdaterEnabled();
|
this.loadUpdaterEnabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const appStore = new AppStore();
|
|
||||||
|
|
||||||
export function useAppStore() {
|
|
||||||
return appStore;
|
|
||||||
}
|
|
||||||
|
@ -728,5 +728,9 @@ export default class GatewayConnectionStore {
|
|||||||
|
|
||||||
private onUserUpdate = (data: GatewayUserUpdateDispatchData) => {
|
private onUserUpdate = (data: GatewayUserUpdateDispatchData) => {
|
||||||
this.app.users.update(data);
|
this.app.users.update(data);
|
||||||
|
|
||||||
|
if (data.id === this.app.account!.id) {
|
||||||
|
this.app.setUser(data);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -200,6 +200,48 @@ export default class REST {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async patch<T, U>(
|
||||||
|
path: string,
|
||||||
|
body?: T,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
queryParams: Record<string, any> = {},
|
||||||
|
headers: Record<string, string> = {},
|
||||||
|
): Promise<U> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const url = REST.makeAPIUrl(path, queryParams);
|
||||||
|
this.logger.debug(`PATCH ${url}; payload:`, body);
|
||||||
|
return fetch(url, {
|
||||||
|
method: "PATCH",
|
||||||
|
headers: {
|
||||||
|
...headers,
|
||||||
|
...this.headers,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: body ? JSON.stringify(body) : undefined,
|
||||||
|
mode: "cors",
|
||||||
|
})
|
||||||
|
.then(async (res) => {
|
||||||
|
// handle json if content type is json
|
||||||
|
if (res.headers.get("content-type")?.includes("application/json")) {
|
||||||
|
const data = await res.json();
|
||||||
|
if (res.ok) return resolve(data);
|
||||||
|
else return reject(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if theres content, handle text
|
||||||
|
if (res.headers.get("content-length") !== "0") {
|
||||||
|
const data = await res.text();
|
||||||
|
if (res.ok) return resolve(data as U);
|
||||||
|
else return reject(data as U);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.ok) return resolve(res.status as U);
|
||||||
|
else return reject(res.statusText);
|
||||||
|
})
|
||||||
|
.catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public async postFormData<U>(
|
public async postFormData<U>(
|
||||||
path: string,
|
path: string,
|
||||||
body: FormData,
|
body: FormData,
|
||||||
|
Loading…
Reference in New Issue
Block a user