Use Recoil State for Guild Metadata

This commit is contained in:
Michael Peters 2022-01-27 21:46:40 -06:00
parent e0814f1096
commit 245637bed6
10 changed files with 83 additions and 93 deletions

View File

@ -1,42 +1,44 @@
import moment from 'moment';
import React, { FC } from 'react';
import { GuildMetadata, Member, Token } from '../../data-types';
import CombinedGuild from '../../guild-combined';
import { useRecoilValue } from 'recoil';
import { Member, Token } from '../../data-types';
import Util from '../../util';
import { IAddGuildData } from '../overlays/overlay-add-guild';
import { selectedGuildWithMetaState } from '../require/atoms';
import BaseElements from '../require/base-elements';
import { SubscriptionResult, useSoftImageSrcResourceSubscription } from '../require/guild-subscriptions';
import { useSoftImageSrcResourceSubscription } from '../require/guild-subscriptions';
import { useAsyncVoidCallback, useDownloadButton, useOneTimeAsyncAction } from '../require/react-helper';
import Button, { ButtonColorType } from './button';
export interface TokenRowProps {
url: string;
guild: CombinedGuild;
guildMetaResult: SubscriptionResult<GuildMetadata>;
token: Token;
}
const TokenRow: FC<TokenRowProps> = (props: TokenRowProps) => {
const { guild, guildMetaResult, token } = props;
const { token } = props;
const guildWithMeta = useRecoilValue(selectedGuildWithMetaState);
if (guildWithMeta === null || guildWithMeta.value === null) return null;
const [ guildSocketConfigs ] = useOneTimeAsyncAction(
async () => await guild.fetchSocketConfigs(),
async () => await guildWithMeta.guild.fetchSocketConfigs(),
null,
[ guild ]
[ guildWithMeta.guild ]
);
const [ iconSrc ] = useSoftImageSrcResourceSubscription(guild, guildMetaResult.value.iconResourceId, guildMetaResult.guild);
const [ iconSrc ] = useSoftImageSrcResourceSubscription(guildWithMeta.guild, guildWithMeta.value.iconResourceId, guildWithMeta.guild);
const [ revoke ] = useAsyncVoidCallback(async () => {
await guild.requestDoRevokeToken(token.token);
}, [ guild, token ]);
await guildWithMeta.guild.requestDoRevokeToken(token.token);
}, [ guildWithMeta.guild, token ]);
const [ downloadFunc, downloadText, downloadShaking ] = useDownloadButton(
guildMetaResult.value.name + '.cordis',
guildWithMeta.value.name + '.cordis',
async () => {
if (guildSocketConfigs === null) return null;
if (guildWithMeta === null || guildWithMeta.value === null) return null;
const guildSocketConfig = Util.randomChoice(guildSocketConfigs);
const addGuildData: IAddGuildData = {
name: guildMetaResult.value.name,
name: guildWithMeta.value.name,
url: guildSocketConfig.url,
cert: guildSocketConfig.cert,
token: token.token,
@ -46,12 +48,12 @@ const TokenRow: FC<TokenRowProps> = (props: TokenRowProps) => {
const json = JSON.stringify(addGuildData);
return Buffer.from(json);
},
[ guildSocketConfigs, guildMetaResult, token, iconSrc ]
[ guildSocketConfigs, guildWithMeta, token, iconSrc ]
);
const userText = (token.member instanceof Member ? 'Used by ' + token.member.displayName : token.member?.id) ?? 'Unused Token';
return (
<div key={guild.id + token.token} className="token-row">
<div key={guildWithMeta.guild.id + token.token} className="token-row">
<div className="user-token">
<div className="user">{userText}</div>
<div className="token">{token.token}</div>

View File

@ -1,36 +1,32 @@
import React, { FC, ReactNode, RefObject, useCallback, useMemo } from 'react';
import { useSetRecoilState } from 'recoil';
import { GuildMetadata, Member } from '../../data-types';
import CombinedGuild from '../../guild-combined';
import { Member } from '../../data-types';
import { overlayState } from '../require/atoms';
import ChannelOverlay from '../overlays/overlay-channel';
import GuildSettingsOverlay from '../overlays/overlay-guild-settings';
import BaseElements from '../require/base-elements';
import { SubscriptionResult } from '../require/guild-subscriptions';
import ContextMenu from './components/context-menu';
export interface GuildTitleContextMenuProps {
close: () => void;
relativeToRef: RefObject<HTMLElement>;
guild: CombinedGuild;
guildMetaResult: SubscriptionResult<GuildMetadata>;
selfMember: Member;
}
const GuildTitleContextMenu: FC<GuildTitleContextMenuProps> = (props: GuildTitleContextMenuProps) => {
const { close, relativeToRef, guild, guildMetaResult, selfMember } = props;
const { close, relativeToRef, selfMember } = props;
const setOverlay = useSetRecoilState<ReactNode>(overlayState);
const openGuildSettings = useCallback(() => {
close();
setOverlay(<GuildSettingsOverlay guild={guild} guildMetaResult={guildMetaResult} />);
}, [ guild, guildMetaResult, close ]);
setOverlay(<GuildSettingsOverlay />);
}, [ close ]);
const openCreateChannel = useCallback(() => {
close();
setOverlay(<ChannelOverlay guild={guild} />);
}, [ guild, close ]);
setOverlay(<ChannelOverlay />);
}, [ close ]);
const guildSettingsElement = useMemo(() => {
if (!selfMember.privileges.includes('modify_profile')) return null;
@ -59,7 +55,7 @@ const GuildTitleContextMenu: FC<GuildTitleContextMenuProps> = (props: GuildTitle
}, []);
return (
<ContextMenu alignment={alignment} close={close} relativeToRef={relativeToRef} realignDeps={[ guild, guildMetaResult, selfMember ]}>
<ContextMenu alignment={alignment} close={close} relativeToRef={relativeToRef} realignDeps={[ selfMember ]}>
<div className="guild-title-context-menu">
{guildSettingsElement}
{createChannelElement}

View File

@ -4,34 +4,31 @@ import Logger from '../../../../logger/logger';
const LOG = Logger.create(__filename, electronConsole);
import React, { FC, useEffect, useMemo, useState } from 'react';
import CombinedGuild from '../../guild-combined';
import Display from '../components/display';
import InvitePreview from '../components/invite-preview';
import { GuildMetadata, Token } from '../../data-types';
import { Token } from '../../data-types';
import { useAsyncSubmitButton } from '../require/react-helper';
import { Duration } from 'moment';
import moment from 'moment';
import DropdownInput from '../components/input-dropdown';
import Button from '../components/button';
import { useTokensSubscription, useSoftImageSrcResourceSubscription, SubscriptionResult } from '../require/guild-subscriptions';
import { useTokensSubscription, useSoftImageSrcResourceSubscription } from '../require/guild-subscriptions';
import TokenRow from '../components/token-row';
import { useRecoilValue } from 'recoil';
import { selectedGuildWithMetaState } from '../require/atoms';
export interface GuildInvitesDisplayProps {
guild: CombinedGuild;
guildMetaResult: SubscriptionResult<GuildMetadata>;
}
const GuildInvitesDisplay: FC<GuildInvitesDisplayProps> = (props: GuildInvitesDisplayProps) => {
const { guild, guildMetaResult } = props;
const GuildInvitesDisplay: FC = () => {
const guildWithMeta = useRecoilValue(selectedGuildWithMetaState);
if (guildWithMeta === null || guildWithMeta.value === null) return null;
const url = 'https://localhost:3030'; // TODO: this will likely be a dropdown list at some point
const [ _fetchRetryCallable, tokensResult, tokensError ] = useTokensSubscription(guild);
const [ _fetchRetryCallable, tokensResult, tokensError ] = useTokensSubscription(guildWithMeta.guild);
const [ expiresFromNow, setExpiresFromNow ] = useState<Duration | null>(moment.duration(1, 'day'));
const [ expiresFromNowText, setExpiresFromNowText ] = useState<string>('1 day');
const [ iconSrc ] = useSoftImageSrcResourceSubscription(guild, guildMetaResult.value.iconResourceId ?? null, guildMetaResult.guild);
const [ iconSrc ] = useSoftImageSrcResourceSubscription(guildWithMeta.guild, guildWithMeta.value.iconResourceId ?? null, guildWithMeta.guild);
useEffect(() => {
if (expiresFromNowText === 'never') {
@ -46,14 +43,14 @@ const GuildInvitesDisplay: FC<GuildInvitesDisplayProps> = (props: GuildInvitesDi
const [ createTokenFunc, tokenButtonText, tokenButtonShaking, _, createTokenFailMessage ] = useAsyncSubmitButton(
async () => {
try {
const createdToken = await guild.requestDoCreateToken(expiresFromNowText === 'never' ? null : expiresFromNowText)
const createdToken = await guildWithMeta.guild.requestDoCreateToken(expiresFromNowText === 'never' ? null : expiresFromNowText)
return { result: createdToken, errorMessage: null };
} catch (e: unknown) {
LOG.error('error creating token', e);
return { result: null, errorMessage: 'Error creating token' };
}
},
[ guild, expiresFromNowText ],
[ guildWithMeta, expiresFromNowText ],
{ start: 'Create Token', done: 'Create Another Token' }
);
@ -67,11 +64,11 @@ const GuildInvitesDisplay: FC<GuildInvitesDisplayProps> = (props: GuildInvitesDi
// TODO: Try Again
return <div className="tokens-failed">Unable to load tokens</div>;
}
if (!guildMetaResult) {
if (!guildWithMeta?.value) {
return <div className="no-guild-meta">No Guild Metadata</div>;
}
return tokensResult?.value?.map((token: Token) => <TokenRow key={guild.id + token.token} url={url} guild={guild} token={token} guildMetaResult={guildMetaResult} />);
}, [ url, guild, tokensResult, tokensError ]);
return tokensResult?.value?.map((token: Token) => <TokenRow key={guildWithMeta.guild.id + token.token} url={url} token={token} />);
}, [ url, guildWithMeta, tokensResult, tokensError ]);
return (
<Display
@ -91,7 +88,7 @@ const GuildInvitesDisplay: FC<GuildInvitesDisplayProps> = (props: GuildInvitesDi
<div><Button shaking={tokenButtonShaking} onClick={createTokenFunc}>{tokenButtonText}</Button></div>
</div>
<InvitePreview
name={guildMetaResult.value.name ?? ''} iconSrc={iconSrc}
name={guildWithMeta.value.name} iconSrc={iconSrc}
url={url} expiresFromNow={expiresFromNow}
/>
</div>

View File

@ -6,22 +6,20 @@ const LOG = Logger.create(__filename, electronConsole);
import React, { Dispatch, FC, SetStateAction, useCallback, useEffect, useMemo, useState } from 'react';
import Globals from '../../globals';
import CombinedGuild from '../../guild-combined';
import Display from '../components/display';
import TextInput from '../components/input-text';
import ImageEditInput from '../components/input-image-edit';
import { SubscriptionResult, useResourceSubscription } from '../require/guild-subscriptions';
import { GuildMetadata } from '../../data-types';
import { useResourceSubscription } from '../require/guild-subscriptions';
import { useRecoilValue } from 'recoil';
import { selectedGuildWithMetaState } from '../require/atoms';
export interface GuildOverviewDisplayProps {
guild: CombinedGuild;
guildMetaResult: SubscriptionResult<GuildMetadata>;
}
const GuildOverviewDisplay: FC<GuildOverviewDisplayProps> = (props: GuildOverviewDisplayProps) => {
const { guild, guildMetaResult } = props;
const GuildOverviewDisplay: FC = () => {
const guildWithMeta = useRecoilValue(selectedGuildWithMetaState);
if (guildWithMeta === null || guildWithMeta.value === null) return null;
// TODO: Use the one from guild.tsx (for both of these?)
const [ iconResourceResult, iconResourceError ] = useResourceSubscription(guild, guildMetaResult.value.iconResourceId, guildMetaResult.guild);
const [ iconResourceResult, iconResourceError ] = useResourceSubscription(guildWithMeta.guild, guildWithMeta.value.iconResourceId, guildWithMeta.guild);
const [ savedName, setSavedName ] = useState<string | null>(null);
const [ savedIconBuff, setSavedIconBuff ] = useState<Buffer | null>(null);
@ -39,11 +37,10 @@ const GuildOverviewDisplay: FC<GuildOverviewDisplayProps> = (props: GuildOvervie
const [ iconInputMessage, setIconInputMessage ] = useState<string | null>(null);
useEffect(() => {
if (guildMetaResult) {
if (name === savedName) setName(guildMetaResult.value.name);
setSavedName(guildMetaResult.value.name);
}
}, [ guildMetaResult ]);
if (guildWithMeta === null || guildWithMeta.value === null) return;
if (name === savedName) setName(guildWithMeta.value.name);
setSavedName(guildWithMeta.value.name);
}, [ guildWithMeta ]);
useEffect(() => {
if (iconResourceResult && iconResourceResult.value) {
@ -84,7 +81,7 @@ const GuildOverviewDisplay: FC<GuildOverviewDisplayProps> = (props: GuildOvervie
if (name !== savedName) {
// Save name
try {
await guild.requestSetGuildName(name);
await guildWithMeta.guild.requestSetGuildName(name);
setSavedName(name);
} catch (e: unknown) {
LOG.error('error setting guild name', e);
@ -98,7 +95,7 @@ const GuildOverviewDisplay: FC<GuildOverviewDisplayProps> = (props: GuildOvervie
// Save icon
try {
LOG.debug('saving icon');
await guild.requestSetGuildIcon(iconBuff);
await guildWithMeta.guild.requestSetGuildIcon(iconBuff);
setSavedIconBuff(iconBuff);
} catch (e: unknown) {
LOG.error('error setting guild icon', e);

View File

@ -49,7 +49,7 @@ const ChannelElement: FC<ChannelElementProps> = (props: ChannelElementProps) =>
}, [ modifyRef, channel ]);
const launchModify = useCallback(() => {
setOverlay(<ChannelOverlay guild={guild} channel={channel} />);
setOverlay(<ChannelOverlay channel={channel} />);
}, [ guild, channel ]);
return (

View File

@ -4,7 +4,6 @@ import Logger from '../../../../logger/logger';
const LOG = Logger.create(__filename, electronConsole);
import React, { FC, ReactNode, useEffect, useMemo, useRef, useState } from 'react';
import CombinedGuild from '../../guild-combined';
import BaseElements from '../require/base-elements';
import TextInput from '../components/input-text';
import SubmitOverlayLower from '../components/submit-overlay-lower';
@ -13,15 +12,17 @@ import { Channel } from '../../data-types';
import { useAsyncSubmitButton } from '../require/react-helper';
import Button from '../components/button';
import Overlay from '../components/overlay';
import { useSetRecoilState } from 'recoil';
import { overlayState } from '../require/atoms';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { overlayState, selectedGuildState } from '../require/atoms';
export interface ChannelOverlayProps {
guild: CombinedGuild;
channel?: Channel;
channel?: Channel; // If this is undefined, this is an add-channel overlay
}
const ChannelOverlay: FC<ChannelOverlayProps> = (props: ChannelOverlayProps) => {
const { guild, channel } = props;
const { channel } = props;
const guild = useRecoilValue(selectedGuildState);
if (guild === null) return null;
const rootRef = useRef<HTMLDivElement>(null);
const nameInputRef = useRef<HTMLInputElement>(null);

View File

@ -1,18 +1,15 @@
import React, { FC, useEffect, useRef, useState } from "react";
import CombinedGuild from "../../guild-combined";
import ChoicesControl from "../components/control-choices";
import GuildInvitesDisplay from "../displays/display-guild-invites";
import GuildOverviewDisplay from "../displays/display-guild-overview";
import { GuildMetadata } from '../../data-types';
import Overlay from '../components/overlay';
import { SubscriptionResult } from '../require/guild-subscriptions';
import { useRecoilValue } from "recoil";
import { selectedGuildWithMetaState } from "../require/atoms";
export interface GuildSettingsOverlayProps {
guild: CombinedGuild;
guildMetaResult: SubscriptionResult<GuildMetadata>;
}
const GuildSettingsOverlay: FC<GuildSettingsOverlayProps> = (props: GuildSettingsOverlayProps) => {
const { guild, guildMetaResult } = props;
const GuildSettingsOverlay: FC = () => {
const guildWithMeta = useRecoilValue(selectedGuildWithMetaState);
if (guildWithMeta === null || guildWithMeta.value === null) return null;
const rootRef = useRef<HTMLDivElement>(null);
@ -20,15 +17,15 @@ const GuildSettingsOverlay: FC<GuildSettingsOverlayProps> = (props: GuildSetting
const [ display, setDisplay ] = useState<JSX.Element>();
useEffect(() => {
if (selectedId === 'overview') setDisplay(<GuildOverviewDisplay guild={guild} guildMetaResult={guildMetaResult} />);
if (selectedId === 'overview') setDisplay(<GuildOverviewDisplay />);
//if (selectedId === 'roles' ) setDisplay(<GuildOverviewDisplay guild={guild} guildMeta={guildMeta} />);
if (selectedId === 'invites' ) setDisplay(<GuildInvitesDisplay guild={guild} guildMetaResult={guildMetaResult} />);
if (selectedId === 'invites' ) setDisplay(<GuildInvitesDisplay />);
}, [ selectedId ]);
return (
<Overlay childRootRef={rootRef}>
<div ref={rootRef} className="content display-swapper guild-settings">
<ChoicesControl title={guildMetaResult.value.name} selectedId={selectedId} setSelectedId={setSelectedId} choices={[
<ChoicesControl title={guildWithMeta.value.name ?? 'unknown guild'} selectedId={selectedId} setSelectedId={setSelectedId} choices={[
{ id: 'overview', display: 'Overview' },
{ id: 'roles', display: 'Roles' },
{ id: 'invites', display: 'Invites' },

View File

@ -703,13 +703,14 @@ function useMultipleGuildSubscriptionScrolling<
}
/**
* @deprecated
* @param guild The guild to load from
* @returns [
* guildMetaResult: The guild's metadata
* fetchError: Any error from fetching
* ]
*/
export function useGuildMetadataSubscription(guild: CombinedGuild) {
function useGuildMetadataSubscription(guild: CombinedGuild) {
const fetchMetadataFunc = useCallback(async () => {
//LOG.silly('fetching metadata for subscription');
return await guild.fetchMetadata();

View File

@ -1,18 +1,19 @@
import React, { FC, useMemo, useRef } from 'react';
import { GuildMetadata, Member } from '../../data-types';
import CombinedGuild from '../../guild-combined';
import { useRecoilValue } from 'recoil';
import { Member } from '../../data-types';
import GuildTitleContextMenu from '../contexts/context-menu-guild-title';
import { selectedGuildWithMetaState } from '../require/atoms';
import { isNonNullAndHasValue, SubscriptionResult } from '../require/guild-subscriptions';
import { useContextMenu } from '../require/react-helper';
export interface GuildTitleProps {
guild: CombinedGuild;
guildMetaResult: SubscriptionResult<GuildMetadata | null> | null;
selfMemberResult: SubscriptionResult<Member | null> | null;
}
const GuildTitle: FC<GuildTitleProps> = (props: GuildTitleProps) => {
const { guild, guildMetaResult, selfMemberResult } = props;
const { selfMemberResult } = props;
const guildWithMeta = useRecoilValue(selectedGuildWithMetaState)
const rootRef = useRef<HTMLDivElement>(null);
@ -25,16 +26,15 @@ const GuildTitle: FC<GuildTitleProps> = (props: GuildTitleProps) => {
}, [ selfMemberResult ]);
const [ contextMenu, toggleContextMenu ] = useContextMenu((close: () => void) => {
if (!isNonNullAndHasValue(guildMetaResult)) return null;
if (!isNonNullAndHasValue(selfMemberResult)) return null;
return (
<GuildTitleContextMenu
relativeToRef={rootRef} close={close}
guild={guild} guildMetaResult={guildMetaResult} selfMember={selfMemberResult.value}
selfMember={selfMemberResult.value}
/>
);
}, [ guild, guildMetaResult, selfMemberResult, rootRef ]);
}, [ selfMemberResult, rootRef ]);
const nameStyle = useMemo(() => {
if (hasContextMenu) {
@ -47,7 +47,7 @@ const GuildTitle: FC<GuildTitleProps> = (props: GuildTitleProps) => {
return (
<div className="guild-title" ref={rootRef}>
<div className="guild-name-container" style={nameStyle} onClick={hasContextMenu ? toggleContextMenu : undefined}>
<span className="guild-name">{guildMetaResult?.value?.name ?? null}</span>
<span className="guild-name">{guildWithMeta?.value?.name ?? null}</span>
</div>
{contextMenu}
</div>

View File

@ -9,7 +9,7 @@ import CombinedGuild from '../../guild-combined';
import ChannelList from '../lists/channel-list';
import MemberList from '../lists/member-list';
import MessageList from '../lists/message-list';
import { useSelfMemberSubscription, useGuildMetadataSubscription, useMembersSubscription, useChannelsSubscription } from '../require/guild-subscriptions';
import { useSelfMemberSubscription, useMembersSubscription, useChannelsSubscription } from '../require/guild-subscriptions';
import ChannelTitle from './channel-title';
import ConnectionInfo from './connection-info';
import GuildTitle from './guild-title';
@ -28,7 +28,6 @@ const GuildElement: FC<GuildElementProps> = (props: GuildElementProps) => {
// TODO: React jump messages to bottom when the current user sent a message
const [ selfMemberResult ] = useSelfMemberSubscription(guild);
const [ guildMetaResult, guildMetaFetchError ] = useGuildMetadataSubscription(guild);
const [ membersRetry, membersResult, membersFetchError ] = useMembersSubscription(guild);
const [ channelsRetry, channelsResult, channelsFetchError ] = useChannelsSubscription(guild);
@ -83,7 +82,7 @@ const GuildElement: FC<GuildElementProps> = (props: GuildElementProps) => {
return (
<div className="guild-react">
<div className="guild-sidebar">
<GuildTitle guild={guild} selfMemberResult={selfMemberResult} guildMetaResult={guildMetaResult} />
<GuildTitle selfMemberResult={selfMemberResult} />
<ChannelList
guild={guild} selfMember={selfMemberResult?.value ?? null}
channels={channelsResult?.value ?? null} channelsFetchError={channelsFetchError}