Guild list in Recoil!
I'm liking it already. Quite a bit nicer than passing state all over the place.
This commit is contained in:
parent
4f2d272d00
commit
e0814f1096
@ -5,7 +5,7 @@ const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import React, { FC, ReactNode, RefObject, useCallback, useEffect } from "react";
|
||||
import { useActionWhenEscapeOrClickedOrContextOutsideEffect } from '../require/react-helper';
|
||||
import { overlayState } from '../atoms';
|
||||
import { overlayState } from '../require/atoms';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
interface OverlayProps {
|
||||
|
@ -2,7 +2,7 @@ import React, { FC, ReactNode, RefObject, useCallback, useMemo } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { Member } from '../../data-types';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import { overlayState } from '../atoms';
|
||||
import { overlayState } from '../require/atoms';
|
||||
import PersonalizeOverlay from '../overlays/overlay-personalize';
|
||||
import { SubscriptionResult } from '../require/guild-subscriptions';
|
||||
import ContextMenu from './components/context-menu';
|
||||
|
@ -2,7 +2,7 @@ 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 { overlayState } from '../atoms';
|
||||
import { overlayState } from '../require/atoms';
|
||||
import ChannelOverlay from '../overlays/overlay-channel';
|
||||
import GuildSettingsOverlay from '../overlays/overlay-guild-settings';
|
||||
import BaseElements from '../require/base-elements';
|
||||
|
@ -10,7 +10,7 @@ import ChannelOverlay from '../../overlays/overlay-channel';
|
||||
import BaseElements from '../../require/base-elements';
|
||||
import { useContextHover } from '../../require/react-helper';
|
||||
import BasicHover, { BasicHoverSide } from '../../contexts/context-hover-basic';
|
||||
import { overlayState } from '../../atoms';
|
||||
import { overlayState } from '../../require/atoms';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
export interface ChannelElementProps {
|
||||
|
@ -1,41 +1,42 @@
|
||||
import React, { FC, useCallback, useMemo, useRef } from 'react';
|
||||
import CombinedGuild from '../../../guild-combined';
|
||||
import GuildsManager from '../../../guilds-manager';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { GuildMetadata } from '../../../data-types';
|
||||
import ContextMenu from '../../contexts/components/context-menu';
|
||||
import BasicHover, { BasicHoverSide } from '../../contexts/context-hover-basic';
|
||||
import { guildsManagerState, GuildWithErrorableValue, selectedGuildIdState, selectedGuildState } from '../../require/atoms';
|
||||
import BaseElements from '../../require/base-elements';
|
||||
import { IAlignment } from '../../require/elements-util';
|
||||
import { useGuildMetadataSubscription, useSelfMemberSubscription, useSoftImageSrcResourceSubscription } from '../../require/guild-subscriptions';
|
||||
import { useSelfMemberSubscription, useSoftImageSrcResourceSubscription } from '../../require/guild-subscriptions';
|
||||
import { useContextClickContextMenu, useContextHover } from '../../require/react-helper';
|
||||
|
||||
export interface GuildListElementProps {
|
||||
guildsManager: GuildsManager;
|
||||
guild: CombinedGuild;
|
||||
activeGuild: CombinedGuild | null;
|
||||
setSelfActiveGuild: () => void;
|
||||
guildWithMeta: GuildWithErrorableValue<GuildMetadata>,
|
||||
}
|
||||
|
||||
const GuildListElement: FC<GuildListElementProps> = (props: GuildListElementProps) => {
|
||||
const { guildsManager, guild, activeGuild, setSelfActiveGuild } = props;
|
||||
const { guildWithMeta: { guild, value: guildMeta, valueError: _guildMetaError } } = props;
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// TODO: state higher up
|
||||
const guildsManager = useRecoilValue(guildsManagerState);
|
||||
const selectedGuild = useRecoilValue(selectedGuildState);
|
||||
const setSelectedGuildId = useSetRecoilState(selectedGuildIdState);
|
||||
|
||||
// TODO: more state higher up
|
||||
// TODO: handle metadata error
|
||||
const [ guildMetaResult, guildMetaError ] = useGuildMetadataSubscription(guild);
|
||||
const [ selfMemberResult ] = useSelfMemberSubscription(guild);
|
||||
const [ iconSrc ] = useSoftImageSrcResourceSubscription(guild, guildMetaResult?.value.iconResourceId ?? null, guildMetaResult?.guild ?? null);
|
||||
const [ iconSrc ] = useSoftImageSrcResourceSubscription(guild, guildMeta?.iconResourceId ?? null, guild);
|
||||
|
||||
const [ contextHover, mouseEnterCallable, mouseLeaveCallable ] = useContextHover(() => {
|
||||
if (!guildMetaResult) return null;
|
||||
if (!guildMeta) return null;
|
||||
if (!selfMemberResult || !selfMemberResult.value) return null;
|
||||
const nameStyle = selfMemberResult.value.roleColor ? { color: selfMemberResult.value.roleColor } : {};
|
||||
return (
|
||||
<BasicHover relativeToRef={rootRef} side={BasicHoverSide.RIGHT} realignDeps={[ guildMetaResult, selfMemberResult ]}>
|
||||
<BasicHover relativeToRef={rootRef} side={BasicHoverSide.RIGHT} realignDeps={[ guildMeta, selfMemberResult ]}>
|
||||
<div className="guild-hover">
|
||||
<div className="tab">{BaseElements.TAB_LEFT}</div>
|
||||
<div className="info">
|
||||
<div className="guild-name">{guildMetaResult.value.name}</div>
|
||||
<div className="guild-name">{guildMeta.name}</div>
|
||||
<div className={'connection ' + selfMemberResult.value.status}>
|
||||
<div className="status-circle" />
|
||||
<div className="display-name" style={nameStyle}>{selfMemberResult.value.displayName}</div>
|
||||
@ -44,9 +45,10 @@ const GuildListElement: FC<GuildListElementProps> = (props: GuildListElementProp
|
||||
</div>
|
||||
</BasicHover>
|
||||
)
|
||||
}, [ guildMetaResult, selfMemberResult ]);
|
||||
}, [ guildMeta, selfMemberResult ]);
|
||||
|
||||
const leaveGuildCallable = useCallback(async () => {
|
||||
if (!guildsManager) return;
|
||||
guild.disconnect();
|
||||
await guildsManager.removeGuild(guild);
|
||||
}, [ guildsManager, guild ])
|
||||
@ -63,9 +65,14 @@ const GuildListElement: FC<GuildListElementProps> = (props: GuildListElementProp
|
||||
)
|
||||
}, [ leaveGuildCallable ]);
|
||||
|
||||
|
||||
const setSelfActiveGuild = useCallback(() => {
|
||||
setSelectedGuildId(guild.id);
|
||||
}, [ guild ]);
|
||||
|
||||
const className = useMemo(() => {
|
||||
return activeGuild && guild.id === activeGuild.id ? 'guild active' : 'guild';
|
||||
}, [ guild, activeGuild ]);
|
||||
return selectedGuild && guild.id === selectedGuild.id ? 'guild active' : 'guild';
|
||||
}, [ guild, selectedGuild ]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
@ -3,7 +3,7 @@ import React, { FC, ReactNode, useCallback, useMemo } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { Member, Message } from '../../../data-types';
|
||||
import CombinedGuild from '../../../guild-combined';
|
||||
import { overlayState } from '../../atoms';
|
||||
import { overlayState } from '../../require/atoms';
|
||||
import ImageContextMenu from '../../contexts/context-menu-image';
|
||||
import ImageOverlay from '../../overlays/overlay-image';
|
||||
import ElementsUtil, { IAlignment } from '../../require/elements-util';
|
||||
|
@ -1,27 +1,20 @@
|
||||
import React, { Dispatch, FC, SetStateAction, useMemo } from 'react';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import GuildsManager from '../../guilds-manager';
|
||||
import React, { FC, useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { GuildMetadata } from '../../data-types';
|
||||
import { guildsState, GuildWithErrorableValue } from '../require/atoms';
|
||||
import GuildListElement from './components/guild-list-element';
|
||||
|
||||
export interface GuildListProps {
|
||||
guildsManager: GuildsManager;
|
||||
guilds: CombinedGuild[];
|
||||
activeGuild: CombinedGuild | null;
|
||||
setActiveGuild: Dispatch<SetStateAction<CombinedGuild | null>>;
|
||||
}
|
||||
|
||||
const GuildList: FC<GuildListProps> = (props: GuildListProps) => {
|
||||
const { guildsManager, guilds, activeGuild, setActiveGuild } = props;
|
||||
const GuildList: FC = () => {
|
||||
const guildsWithMeta = useRecoilValue(guildsState);
|
||||
|
||||
const guildElements = useMemo(() => {
|
||||
return guilds.map((guild: CombinedGuild) => (
|
||||
if (!guildsWithMeta) return null;
|
||||
return guildsWithMeta.map((guildWithMeta: GuildWithErrorableValue<GuildMetadata>) => (
|
||||
<GuildListElement
|
||||
key={guild.id}
|
||||
guildsManager={guildsManager} guild={guild}
|
||||
activeGuild={activeGuild} setSelfActiveGuild={() => { setActiveGuild(guild); } }
|
||||
guildWithMeta={guildWithMeta} key={guildWithMeta.guild.id}
|
||||
/>
|
||||
));
|
||||
}, [ guildsManager, guilds, activeGuild ]);
|
||||
}, [ guildsWithMeta ]);
|
||||
|
||||
return (
|
||||
<div className="guild-list">
|
||||
|
@ -3,8 +3,7 @@ const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||
import Logger from '../../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import React, { Dispatch, FC, ReactNode, SetStateAction, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import GuildsManager from '../../guilds-manager';
|
||||
import React, { FC, ReactNode, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import moment from 'moment';
|
||||
import TextInput from '../components/input-text';
|
||||
import ImageEditInput from '../components/input-image-edit';
|
||||
@ -17,8 +16,8 @@ import { useAsyncSubmitButton, useOneTimeAsyncAction } from '../require/react-he
|
||||
import * as fs from 'fs/promises';
|
||||
import Button from '../components/button';
|
||||
import Overlay from '../components/overlay';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { overlayState } from '../atoms';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { guildsManagerState, overlayState, selectedGuildIdState } from '../require/atoms';
|
||||
|
||||
export interface IAddGuildData {
|
||||
name: string,
|
||||
@ -52,13 +51,14 @@ function getExampleAvatarPath(): string {
|
||||
}
|
||||
|
||||
export interface AddGuildOverlayProps {
|
||||
guildsManager: GuildsManager;
|
||||
addGuildData: IAddGuildData;
|
||||
setActiveGuild: Dispatch<SetStateAction<CombinedGuild | null>>;
|
||||
}
|
||||
|
||||
const AddGuildOverlay: FC<AddGuildOverlayProps> = (props: AddGuildOverlayProps) => {
|
||||
const { guildsManager, addGuildData, setActiveGuild } = props;
|
||||
const { addGuildData } = props;
|
||||
|
||||
const guildsManager = useRecoilValue(guildsManagerState);
|
||||
const setSelectedGuildId = useSetRecoilState(selectedGuildIdState);
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
const setOverlay = useSetRecoilState<ReactNode>(overlayState);
|
||||
@ -97,6 +97,7 @@ const AddGuildOverlay: FC<AddGuildOverlayProps> = (props: AddGuildOverlayProps)
|
||||
|
||||
const [ submitFunc, submitButtonText, submitButtonShaking, _, submitFailMessage ] = useAsyncSubmitButton(
|
||||
async () => {
|
||||
if (!guildsManager) return { result: null, errorMessage: null };
|
||||
if (validationErrorMessage || !avatarBuff) return { result: null, errorMessage: 'Validation failed' };
|
||||
if (expired) return { result: null, errorMessage: 'Token expired' };
|
||||
|
||||
@ -108,7 +109,7 @@ const AddGuildOverlay: FC<AddGuildOverlayProps> = (props: AddGuildOverlayProps)
|
||||
return { result: null, errorMessage: (e as Error).message ?? 'Error adding new guild' };
|
||||
}
|
||||
|
||||
setActiveGuild(newGuild);
|
||||
setSelectedGuildId(newGuild.id);
|
||||
|
||||
setOverlay(null);
|
||||
return { result: newGuild, errorMessage: null };
|
||||
|
@ -14,7 +14,7 @@ import { useAsyncSubmitButton } from '../require/react-helper';
|
||||
import Button from '../components/button';
|
||||
import Overlay from '../components/overlay';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { overlayState } from '../atoms';
|
||||
import { overlayState } from '../require/atoms';
|
||||
|
||||
export interface ChannelOverlayProps {
|
||||
guild: CombinedGuild;
|
||||
|
@ -15,7 +15,7 @@ import Button from '../components/button';
|
||||
import Overlay from '../components/overlay';
|
||||
import { SubscriptionResult, useResourceSubscription } from '../require/guild-subscriptions';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { overlayState } from '../atoms';
|
||||
import { overlayState } from '../require/atoms';
|
||||
|
||||
export interface PersonalizeOverlayProps {
|
||||
guild: CombinedGuild;
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { atom, selector } from 'recoil';
|
||||
import { Channel, GuildMetadata, Member, Message } from '../data-types';
|
||||
import CombinedGuild from '../guild-combined';
|
||||
import { atom, DefaultValue, selector } from 'recoil';
|
||||
import { Channel, GuildMetadata, Member, Message } from '../../data-types';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import GuildsManager from '../../guilds-manager';
|
||||
|
||||
export interface GuildWithValue<T> {
|
||||
guild: CombinedGuild;
|
||||
@ -25,9 +26,18 @@ export const overlayState = atom<ReactNode>({
|
||||
default: null
|
||||
});
|
||||
|
||||
export const guildsManagerState = atom<GuildsManager | null>({
|
||||
key: 'guildsManagerState',
|
||||
default: null,
|
||||
// Allow mutability so that we can have changes to internal guild stuff
|
||||
// We will still listen to new/update/delete/conflict events to maintain the UI's state
|
||||
dangerouslyAllowMutability: true
|
||||
})
|
||||
|
||||
export const guildsState = atom<GuildWithErrorableValue<GuildMetadata>[] | null>({
|
||||
key: 'guildsState',
|
||||
default: null
|
||||
default: null,
|
||||
dangerouslyAllowMutability: true
|
||||
});
|
||||
|
||||
export const selectedGuildIdState = atom<number | null>({
|
||||
@ -45,7 +55,8 @@ export const selectedGuildWithMetaState = selector<GuildWithErrorableValue<Guild
|
||||
if (guildId === null) return null;
|
||||
|
||||
return guildsWithMeta.find(guildWithMeta => guildWithMeta.guild.id === guildId) ?? null;
|
||||
}
|
||||
},
|
||||
dangerouslyAllowMutability: true
|
||||
});
|
||||
|
||||
export const selectedGuildState = selector<CombinedGuild | null>({
|
||||
@ -55,7 +66,8 @@ export const selectedGuildState = selector<CombinedGuild | null>({
|
||||
if (guildWithMeta === null) return null;
|
||||
|
||||
return guildWithMeta.guild;
|
||||
}
|
||||
},
|
||||
dangerouslyAllowMutability: true
|
||||
});
|
||||
|
||||
export const selectedGuildMembersState = atom<GuildWithErrorableValue<Member[]> | null>({
|
142
src/client/webapp/elements/require/setup-guild-recoil.ts
Normal file
142
src/client/webapp/elements/require/setup-guild-recoil.ts
Normal file
@ -0,0 +1,142 @@
|
||||
import * as electronRemote from '@electron/remote';
|
||||
const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||
import Logger from '../../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from "recoil";
|
||||
import { GuildMetadata } from "../../data-types";
|
||||
import GuildsManager from "../../guilds-manager";
|
||||
import { guildsManagerState, guildsState, GuildWithErrorableValue, selectedGuildIdState, selectedGuildState } from "./atoms";
|
||||
import { useIsMountedRef } from "./react-helper";
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import { AutoVerifierChangesType } from '../../auto-verifier';
|
||||
|
||||
|
||||
/** Manages the guildsManager to ensure that guildsState is up to date with a list of guilds
|
||||
* and that the guilds attempt to fetch their corresponding guildMetas
|
||||
* This class it a bit more complicated since it has to fetch/listen to guild metadata changes on many guilds at once */
|
||||
function useManagedGuildsManager(guildsManager: GuildsManager) {
|
||||
const isMounted = useIsMountedRef();
|
||||
const metadataInProgress = useRef<Set<number>>(new Set<number>());
|
||||
const [ guildsWithMeta, setGuildsWithMeta ] = useRecoilState(guildsState);
|
||||
|
||||
// Update the guild list
|
||||
// If new guilds are added, add them with null metadata
|
||||
const onChange = useCallback(() => {
|
||||
const guilds = guildsManager.guilds;
|
||||
setGuildsWithMeta((prevGuildsWithMeta) => {
|
||||
if (!prevGuildsWithMeta) {
|
||||
return guilds.map(guild => ({ guild, value: null, valueError: null }));
|
||||
} else {
|
||||
const newGuilds = guilds.filter(guild => !prevGuildsWithMeta.find(guildWithMeta => guildWithMeta.guild.id === guild.id));
|
||||
const newGuildsWithMeta = prevGuildsWithMeta
|
||||
.filter(guildWithMeta => guilds.find(guild => guildWithMeta.guild.id === guild.id)) // Remove old guilds
|
||||
.concat(newGuilds.map(guild => ({ guild, value: null, valueError: null }))); // Add new guilds
|
||||
return newGuildsWithMeta;
|
||||
}
|
||||
});
|
||||
}, [ guildsManager, setGuildsWithMeta ]);
|
||||
|
||||
// Listen for changes to the guild manager
|
||||
useEffect(() => {
|
||||
// Set initial guilds
|
||||
onChange();
|
||||
|
||||
// Listen for updates
|
||||
guildsManager.on('update-guilds', onChange);
|
||||
return () => {
|
||||
guildsManager.off('update-guilds', onChange);
|
||||
}
|
||||
}, [ guildsManager, onChange ]);
|
||||
|
||||
|
||||
const setGuildWithMeta = useCallback((guildId: number, newGuildWithMeta: GuildWithErrorableValue<GuildMetadata>) => {
|
||||
setGuildsWithMeta(prevGuildsWithMeta => {
|
||||
if (!prevGuildsWithMeta) return null;
|
||||
return prevGuildsWithMeta.map(guildWithMeta => guildWithMeta.guild.id === guildId ? newGuildWithMeta : guildWithMeta);
|
||||
});
|
||||
}, [ setGuildsWithMeta ]);
|
||||
|
||||
useEffect(() => {
|
||||
if (guildsWithMeta === null) return;
|
||||
|
||||
// Load guild metadatas
|
||||
for (const { guild, value, valueError } of guildsWithMeta) {
|
||||
// Only fetch if the value has not been fetched yet and we are not currently fetching it.
|
||||
if (!metadataInProgress.current.has(guild.id) && value === null && valueError === null) {
|
||||
(async () => {
|
||||
metadataInProgress.current.add(guild.id);
|
||||
try {
|
||||
const guildMeta = await guild.fetchMetadata();
|
||||
if (!isMounted.current) return;
|
||||
setGuildWithMeta(guild.id, { guild, value: guildMeta, valueError: null });
|
||||
} catch (e: unknown) {
|
||||
LOG.error(`error fetching metadata for g#${guild.id}`, e);
|
||||
if (!isMounted.current) return;
|
||||
setGuildWithMeta(guild.id, { guild, value: null, valueError: e });
|
||||
}
|
||||
metadataInProgress.current.delete(guild.id);
|
||||
})();
|
||||
}
|
||||
}
|
||||
}, [ guildsWithMeta, setGuildWithMeta ]);
|
||||
|
||||
// Listen for changes to the guild metadata
|
||||
useEffect(() => {
|
||||
if (guildsWithMeta === null) return;
|
||||
|
||||
// Listen for changes to metadata
|
||||
function handleUpdateOrConflict(guild: CombinedGuild, guildMeta: GuildMetadata) {
|
||||
setGuildWithMeta(guild.id, { guild, value: guildMeta, valueError: null });
|
||||
}
|
||||
const callbacks = new Map<CombinedGuild, {
|
||||
updateCallback: (guildMeta: GuildMetadata) => void,
|
||||
conflictCallback: (changesType: AutoVerifierChangesType, oldGuildMeta: GuildMetadata, guildMeta: GuildMetadata) => void
|
||||
}>();
|
||||
|
||||
for (const { guild } of guildsWithMeta) {
|
||||
const updateCallback = (guildMeta: GuildMetadata) => handleUpdateOrConflict(guild, guildMeta);
|
||||
const conflictCallback = (_changesType: AutoVerifierChangesType, _oldGuildMeta: GuildMetadata, guildMeta: GuildMetadata) => handleUpdateOrConflict(guild, guildMeta);
|
||||
guild.on('update-metadata', updateCallback);
|
||||
guild.on('conflict-metadata', conflictCallback);
|
||||
callbacks.set(guild, { updateCallback, conflictCallback });
|
||||
}
|
||||
|
||||
return () => {
|
||||
for (const { guild } of guildsWithMeta) {
|
||||
const { updateCallback, conflictCallback } = callbacks.get(guild) as {
|
||||
updateCallback: (guildMeta: GuildMetadata) => void,
|
||||
conflictCallback: (changesType: AutoVerifierChangesType, oldGuildMeta: GuildMetadata, guildMeta: GuildMetadata) => void
|
||||
};
|
||||
guild.off('update-metadata', updateCallback);
|
||||
guild.off('conflict-metadata', conflictCallback);
|
||||
}
|
||||
}
|
||||
}, [ guildsWithMeta ]);
|
||||
|
||||
//useEffect(() => {
|
||||
// LOG.debug(`guildsWithMeta: `, { guildsWithMeta });
|
||||
//}, [ guildsWithMeta ]);
|
||||
}
|
||||
|
||||
// Entrypoint for recoil setup with guilds
|
||||
export function useGuildsManagerWithRecoil(guildsManager: GuildsManager): void {
|
||||
const setGuildsManager = useSetRecoilState(guildsManagerState);
|
||||
const guilds = useRecoilValue(guildsState);
|
||||
const [ selectedGuildId, setSelectedGuildId ] = useRecoilState(selectedGuildIdState);
|
||||
|
||||
// Set the guilds manager atom
|
||||
useEffect(() => { setGuildsManager(guildsManager); }, [ guildsManager, setGuildsManager ]);
|
||||
|
||||
// Manage the guilds within the manager (adds them to the guilds atom and automatically loads and updates their metadata)
|
||||
useManagedGuildsManager(guildsManager);
|
||||
|
||||
// Make sure that the first guild is set to the active guild once we get some guilds
|
||||
useEffect(() => {
|
||||
if (guilds && guilds.length > 0 && selectedGuildId === null) {
|
||||
setSelectedGuildId((guilds[0] as GuildWithErrorableValue<GuildMetadata>).guild.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React, { FC, ReactNode } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import GuildsManager from '../guilds-manager';
|
||||
import { overlayState } from './atoms';
|
||||
import { overlayState } from './require/atoms';
|
||||
import { useGuildsManagerWithRecoil } from './require/setup-guild-recoil';
|
||||
import GuildsManagerElement from './sections/guilds-manager';
|
||||
import TitleBar from './sections/title-bar';
|
||||
|
||||
@ -12,12 +13,14 @@ export interface RootElementProps {
|
||||
const RootElement: FC<RootElementProps> = (props: RootElementProps) => {
|
||||
const { guildsManager } = props;
|
||||
|
||||
const [ overlay, setOverlay ] = useRecoilState<ReactNode>(overlayState);
|
||||
const overlay = useRecoilValue<ReactNode>(overlayState);
|
||||
|
||||
useGuildsManagerWithRecoil(guildsManager);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TitleBar />
|
||||
<GuildsManagerElement guildsManager={guildsManager} />
|
||||
<GuildsManagerElement />
|
||||
<div className="react-overlays">{overlay}</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -3,9 +3,7 @@ const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||
import Logger from '../../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import React, { Dispatch, FC, ReactNode, SetStateAction, useRef } from 'react';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import GuildsManager from '../../guilds-manager';
|
||||
import React, { FC, ReactNode, useRef } from 'react';
|
||||
import GuildList from '../lists/guild-list';
|
||||
import { useAsyncVoidCallback, useContextHover } from '../require/react-helper';
|
||||
import fs from 'fs/promises';
|
||||
@ -13,19 +11,10 @@ import AddGuildOverlay from '../overlays/overlay-add-guild';
|
||||
import ErrorMessageOverlay from '../overlays/overlay-error-message';
|
||||
import BasicHover, { BasicHoverSide } from '../contexts/context-hover-basic';
|
||||
import BaseElements from '../require/base-elements';
|
||||
import { overlayState } from '../atoms';
|
||||
import { overlayState } from '../require/atoms';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
export interface GuildListContainerProps {
|
||||
guildsManager: GuildsManager;
|
||||
guilds: CombinedGuild[];
|
||||
activeGuild: CombinedGuild | null;
|
||||
setActiveGuild: Dispatch<SetStateAction<CombinedGuild | null>>;
|
||||
}
|
||||
|
||||
const GuildListContainer: FC<GuildListContainerProps> = (props: GuildListContainerProps) => {
|
||||
const { guildsManager, guilds, activeGuild, setActiveGuild } = props;
|
||||
|
||||
const GuildListContainer: FC = () => {
|
||||
const addGuildRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const setOverlay = useSetRecoilState<ReactNode>(overlayState);
|
||||
@ -71,16 +60,13 @@ const GuildListContainer: FC<GuildListContainerProps> = (props: GuildListContain
|
||||
LOG.debug('bad guild data:', { addGuildData, fileText });
|
||||
setOverlay(<ErrorMessageOverlay title="Unable to parse guild file" message="Bad guild data" />);
|
||||
} else {
|
||||
setOverlay(<AddGuildOverlay guildsManager={guildsManager} addGuildData={addGuildData} setActiveGuild={setActiveGuild} />);
|
||||
setOverlay(<AddGuildOverlay addGuildData={addGuildData} />);
|
||||
}
|
||||
}, [ guildsManager ]);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="guild-list-container">
|
||||
<GuildList
|
||||
guildsManager={guildsManager} guilds={guilds}
|
||||
activeGuild={activeGuild} setActiveGuild={setActiveGuild}
|
||||
/>
|
||||
<GuildList />
|
||||
<div
|
||||
ref={addGuildRef}
|
||||
className="add-guild" onClick={onAddGuildClickCallback}
|
||||
|
@ -1,36 +1,16 @@
|
||||
import React, { Dispatch, FC, ReactNode, SetStateAction, useEffect, useState } from 'react';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import GuildsManager from '../../guilds-manager';
|
||||
import { useGuildListSubscription } from '../require/guilds-manager-subscriptions';
|
||||
import React, { FC } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { selectedGuildState } from '../require/atoms';
|
||||
import GuildElement from './guild';
|
||||
import GuildListContainer from './guild-list-container';
|
||||
|
||||
export interface GuildsManagerElementProps {
|
||||
guildsManager: GuildsManager;
|
||||
}
|
||||
|
||||
const GuildsManagerElement: FC<GuildsManagerElementProps> = (props: GuildsManagerElementProps) => {
|
||||
const { guildsManager } = props;
|
||||
|
||||
const [ guilds ] = useGuildListSubscription(guildsManager);
|
||||
const [ activeGuild, setActiveGuild ] = useState<CombinedGuild | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeGuild === null) {
|
||||
// initial active channel is the first one in the list
|
||||
if (guilds && guilds.length > 0) {
|
||||
setActiveGuild(guilds[0] as CombinedGuild);
|
||||
}
|
||||
}
|
||||
}, [ guilds, activeGuild ]);
|
||||
const GuildsManagerElement: FC = () => {
|
||||
const selectedGuild = useRecoilValue(selectedGuildState);
|
||||
|
||||
return (
|
||||
<div className="guilds-manager">
|
||||
<GuildListContainer
|
||||
guildsManager={guildsManager} guilds={guilds}
|
||||
activeGuild={activeGuild} setActiveGuild={setActiveGuild}
|
||||
/>
|
||||
{activeGuild && <GuildElement guild={activeGuild} />}
|
||||
<GuildListContainer />
|
||||
{selectedGuild && <GuildElement guild={selectedGuild} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user