recoilize resources
This commit is contained in:
parent
0c1c900724
commit
6da480c36e
@ -5,9 +5,8 @@ import { Member, Token } from '../../data-types';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import Util from '../../util';
|
||||
import { IAddGuildData } from '../overlays/overlay-add-guild';
|
||||
import { guildMetaState, isLoaded } from '../require/atoms-2';
|
||||
import { guildMetaState, guildResourceSoftImgSrcState, isLoaded, useRecoilValueSoftImgSrc } from '../require/atoms-2';
|
||||
import BaseElements from '../require/base-elements';
|
||||
import { useSoftImageSrcResourceSubscription } from '../require/guild-subscriptions';
|
||||
import { useAsyncVoidCallback, useDownloadButton, useOneTimeAsyncAction } from '../require/react-helper';
|
||||
import Button, { ButtonColorType } from './button';
|
||||
|
||||
@ -29,7 +28,7 @@ const TokenRow: FC<TokenRowProps> = (props: TokenRowProps) => {
|
||||
null,
|
||||
[ guild ]
|
||||
);
|
||||
const [ iconSrc ] = useSoftImageSrcResourceSubscription(guild, isLoaded(guildMeta) ? guildMeta.value.iconResourceId : null, guild);
|
||||
const iconSrc = useRecoilValueSoftImgSrc(guildResourceSoftImgSrcState({ guildId: guild.id, resourceId: isLoaded(guildMeta) ? guildMeta.value.iconResourceId : null }));
|
||||
|
||||
const [ revoke ] = useAsyncVoidCallback(async () => {
|
||||
await guild.requestDoRevokeToken(token.token);
|
||||
|
@ -12,11 +12,11 @@ import { Duration } from 'moment';
|
||||
import moment from 'moment';
|
||||
import DropdownInput from '../components/input-dropdown';
|
||||
import Button from '../components/button';
|
||||
import { useTokensSubscription, useSoftImageSrcResourceSubscription } from '../require/guild-subscriptions';
|
||||
import { useTokensSubscription } from '../require/guild-subscriptions';
|
||||
import TokenRow from '../components/token-row';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { guildMetaState, isLoaded } from '../require/atoms-2';
|
||||
import { guildMetaState, guildResourceSoftImgSrcState, isLoaded, useRecoilValueSoftImgSrc } from '../require/atoms-2';
|
||||
|
||||
export interface GuildInvitesDisplayProps {
|
||||
guild: CombinedGuild;
|
||||
@ -34,7 +34,7 @@ const GuildInvitesDisplay: FC<GuildInvitesDisplayProps> = (props: GuildInvitesDi
|
||||
const [ expiresFromNow, setExpiresFromNow ] = useState<Duration | null>(moment.duration(1, 'day'));
|
||||
const [ expiresFromNowText, setExpiresFromNowText ] = useState<string>('1 day');
|
||||
|
||||
const [ iconSrc ] = useSoftImageSrcResourceSubscription(guild, isLoaded(guildMeta) ? guildMeta.value.iconResourceId : null, guild);
|
||||
const iconSrc = useRecoilValueSoftImgSrc(guildResourceSoftImgSrcState({ guildId: guild.id, resourceId: isLoaded(guildMeta) ? guildMeta.value.iconResourceId : null }));
|
||||
|
||||
useEffect(() => {
|
||||
if (expiresFromNowText === 'never') {
|
||||
|
@ -3,10 +3,9 @@ import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import CombinedGuild from '../../../guild-combined';
|
||||
import ContextMenu from '../../contexts/components/context-menu';
|
||||
import BasicHover, { BasicHoverSide } from '../../contexts/context-hover-basic';
|
||||
import { currGuildIdState, guildMetaState, guildSelfMemberState, guildsManagerState, isLoaded } from '../../require/atoms-2';
|
||||
import { currGuildIdState, guildMetaState, guildResourceSoftImgSrcState, guildSelfMemberState, guildsManagerState, isLoaded, useRecoilValueSoftImgSrc } from '../../require/atoms-2';
|
||||
import BaseElements from '../../require/base-elements';
|
||||
import { IAlignment } from '../../require/elements-util';
|
||||
import { useSoftImageSrcResourceSubscription } from '../../require/guild-subscriptions';
|
||||
import { useContextClickContextMenu, useContextHover } from '../../require/react-helper';
|
||||
|
||||
export interface GuildListElementProps {
|
||||
@ -23,9 +22,7 @@ const GuildListElement: FC<GuildListElementProps> = (props: GuildListElementProp
|
||||
const guildMeta = useRecoilValue(guildMetaState(guild.id));
|
||||
const selfMember = useRecoilValue(guildSelfMemberState(guild.id));
|
||||
|
||||
// TODO: more state higher up
|
||||
// TODO: handle metadata error
|
||||
const [ iconSrc ] = useSoftImageSrcResourceSubscription(guild, isLoaded(guildMeta) ? guildMeta.value.iconResourceId : null, guild);
|
||||
const iconSrc = useRecoilValueSoftImgSrc(guildResourceSoftImgSrcState({ guildId: guild.id, resourceId: isLoaded(guildMeta) ? guildMeta.value.iconResourceId : null }));
|
||||
|
||||
const [ contextHover, mouseEnterCallable, mouseLeaveCallable ] = useContextHover(() => {
|
||||
if (!isLoaded(guildMeta)) return null; // TODO: Loading message here?
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { FC, useMemo } from 'react';
|
||||
import { Member } from '../../../data-types';
|
||||
import CombinedGuild from '../../../guild-combined';
|
||||
import { useSoftImageSrcResourceSubscription } from '../../require/guild-subscriptions';
|
||||
import { guildResourceSoftImgSrcState, useRecoilValueSoftImgSrc } from '../../require/atoms-2';
|
||||
|
||||
export interface DummyMember {
|
||||
id: 'dummy';
|
||||
@ -20,8 +20,7 @@ export interface MemberProps {
|
||||
const MemberElement: FC<MemberProps> = (props: MemberProps) => {
|
||||
const { guild, member } = props;
|
||||
|
||||
// TODO: This is a terrible line of code. Make sure to fix it whe we do resources in recoil
|
||||
const [ avatarSrc ] = useSoftImageSrcResourceSubscription(guild, member.avatarResourceId, guild);
|
||||
const avatarSrc = useRecoilValueSoftImgSrc(guildResourceSoftImgSrcState({ guildId: guild.id, resourceId: member.avatarResourceId }));
|
||||
|
||||
const nameStyle = useMemo(() => member.roleColor ? { color: member.roleColor } : {}, [ member.roleColor ]);
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
import moment from 'moment';
|
||||
import React, { FC, ReactNode, useCallback, useMemo } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { Member, Message } from '../../../data-types';
|
||||
import CombinedGuild from '../../../guild-combined';
|
||||
import ImageContextMenu from '../../contexts/context-menu-image';
|
||||
import ImageOverlay from '../../overlays/overlay-image';
|
||||
import { overlayState } from '../../require/atoms-2';
|
||||
import { guildResourceSoftImgSrcState, guildResourceState, isLoaded, overlayState, useRecoilValueSoftImgSrc } from '../../require/atoms-2';
|
||||
import ElementsUtil, { IAlignment } from '../../require/elements-util';
|
||||
import { useSoftImageSrcResourceSubscription } from '../../require/guild-subscriptions';
|
||||
import { useContextClickContextMenu, useDownloadButton, useOneTimeAsyncAction } from '../../require/react-helper';
|
||||
|
||||
interface ResourceElementProps {
|
||||
@ -48,7 +47,7 @@ interface PreviewImageElementProps {
|
||||
resourcePreviewId: string;
|
||||
resourceId: string;
|
||||
resourceName: string;
|
||||
resourceIdGuild: CombinedGuild;
|
||||
resourceIdGuild: CombinedGuild; // TODO: Remove in favor of just one guild
|
||||
}
|
||||
|
||||
const PreviewImageElement: FC<PreviewImageElementProps> = (props: PreviewImageElementProps) => {
|
||||
@ -57,20 +56,21 @@ const PreviewImageElement: FC<PreviewImageElementProps> = (props: PreviewImageEl
|
||||
const setOverlay = useSetRecoilState<ReactNode>(overlayState);
|
||||
|
||||
// TODO: Handle resourceError
|
||||
const [ previewImgSrc, previewResourceResult, previewResourceError ] = useSoftImageSrcResourceSubscription(guild, resourcePreviewId, resourceIdGuild);
|
||||
const previewResource = useRecoilValue(guildResourceState({ guildId: resourceIdGuild.id, resourceId: resourcePreviewId }));
|
||||
const previewImgSrc = useRecoilValueSoftImgSrc(guildResourceSoftImgSrcState({ guildId: resourceIdGuild.id, resourceId: resourcePreviewId }));
|
||||
|
||||
const [ contextMenu, onContextMenu ] = useContextClickContextMenu((alignment: IAlignment, relativeToPos: { x: number, y: number }, close: () => void) => {
|
||||
if (!previewResourceResult || !previewResourceResult.value) return null;
|
||||
const [ contextMenu, onContextMenu ] = useContextClickContextMenu((_alignment: IAlignment, relativeToPos: { x: number, y: number }, close: () => void) => {
|
||||
if (!isLoaded(previewResource)) return null;
|
||||
return (
|
||||
<ImageContextMenu
|
||||
relativeToPos={relativeToPos} close={close}
|
||||
resourceName={resourceName} resourceBuff={previewResourceResult.value.data} isPreview={true}
|
||||
resourceName={resourceName} resourceBuff={previewResource.value.data} isPreview={true}
|
||||
/>
|
||||
);
|
||||
}, [ previewResourceResult, resourceName ]);
|
||||
}, [ previewResource, resourceName ]);
|
||||
|
||||
const openImageOverlay = useCallback(() => {
|
||||
setOverlay(<ImageOverlay guild={guild} resourceId={resourceId} resourceName={resourceName} resourceIdGuild={resourceIdGuild} />);
|
||||
setOverlay(<ImageOverlay guild={resourceIdGuild} resourceId={resourceId} resourceName={resourceName} />);
|
||||
}, [ guild, resourceId, resourceName ]);
|
||||
|
||||
return (
|
||||
|
@ -10,36 +10,40 @@ import DownloadButton from '../components/button-download';
|
||||
import { useContextClickContextMenu } from '../require/react-helper';
|
||||
import ImageContextMenu from '../contexts/context-menu-image';
|
||||
import Overlay from '../components/overlay';
|
||||
import { useSoftImageSrcResourceSubscription } from '../require/guild-subscriptions';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { guildResourceSoftImgSrcState, guildResourceState, isFailed, isLoaded, useRecoilValueSoftImgSrc } from '../require/atoms-2';
|
||||
|
||||
export interface ImageOverlayProps {
|
||||
guild: CombinedGuild;
|
||||
resourceId: string;
|
||||
resourceName: string;
|
||||
resourceIdGuild: CombinedGuild;
|
||||
}
|
||||
|
||||
const ImageOverlay: FC<ImageOverlayProps> = (props: ImageOverlayProps) => {
|
||||
const { guild, resourceId, resourceName, resourceIdGuild } = props;
|
||||
const { guild, resourceId, resourceName } = props;
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [ imgSrc, resourceResult, resourceError ] = useSoftImageSrcResourceSubscription(guild, resourceId, resourceIdGuild);
|
||||
|
||||
const [ contextMenu, onContextMenu ] = useContextClickContextMenu((alignment: IAlignment, relativeToPos: { x: number, y: number }, close: () => void) => {
|
||||
if (!resourceResult || !resourceResult.value) return null;
|
||||
// NOTE: These will update together. How convenient!
|
||||
const resource = useRecoilValue(guildResourceState({ guildId: guild.id, resourceId }));
|
||||
const imgSrc = useRecoilValueSoftImgSrc(guildResourceSoftImgSrcState({ guildId: guild.id, resourceId }))
|
||||
|
||||
// NOTE: Alignment is based on the image element, not the default context-click alignment because of ImageContextMenu
|
||||
const [ contextMenu, onContextMenu ] = useContextClickContextMenu((_alignment: IAlignment, relativeToPos: { x: number, y: number }, close: () => void) => {
|
||||
if (!isLoaded(resource)) return null;
|
||||
return (
|
||||
<ImageContextMenu
|
||||
relativeToPos={relativeToPos} close={close}
|
||||
resourceName={resourceName} resourceBuff={resourceResult.value.data} isPreview={false}
|
||||
resourceName={resourceName} resourceBuff={resource.value.data} isPreview={false}
|
||||
/>
|
||||
);
|
||||
}, [ resourceResult, resourceName ]);
|
||||
}, [ resource, resourceName ]);
|
||||
|
||||
const sizeText = useMemo(
|
||||
() => resourceResult?.value ? ElementsUtil.humanSize(resourceResult.value.data.length) : 'Loading Size...',
|
||||
[ resourceResult ]
|
||||
);
|
||||
const sizeText = useMemo(() => {
|
||||
if (isFailed(resource)) return 'Failed to load';
|
||||
if (!isLoaded(resource)) return 'Loading size...';
|
||||
return ElementsUtil.humanSize(resource.value.data.length);
|
||||
}, [ resource ]);
|
||||
|
||||
return (
|
||||
<Overlay childRootRef={rootRef}>
|
||||
@ -52,7 +56,7 @@ const ImageOverlay: FC<ImageOverlayProps> = (props: ImageOverlayProps) => {
|
||||
</div>
|
||||
{/* TODO: I think this may break if the download button gets clicked before the resource is loaded... */}
|
||||
<DownloadButton
|
||||
downloadBuff={resourceResult?.value?.data} downloadBuffErr={!!resourceError}
|
||||
downloadBuff={isLoaded(resource) ? resource.value.data : undefined} downloadBuffErr={isFailed(resource)}
|
||||
resourceName={resourceName}></DownloadButton>
|
||||
</div>
|
||||
{contextMenu}
|
||||
|
@ -4,7 +4,7 @@ import Logger from '../../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import { ReactNode, useEffect } from "react";
|
||||
import { atom, atomFamily, GetRecoilValue, RecoilState, RecoilValue, RecoilValueReadOnly, selector, selectorFamily, useSetRecoilState } from "recoil";
|
||||
import { atom, atomFamily, GetRecoilValue, Loadable, RecoilState, RecoilValue, RecoilValueReadOnly, selector, selectorFamily, useRecoilValueLoadable, useSetRecoilState } from "recoil";
|
||||
import { Changes, Channel, GuildMetadata, Member, Resource, ShouldNeverHappenError } from "../../data-types";
|
||||
import CombinedGuild from "../../guild-combined";
|
||||
import GuildsManager from "../../guilds-manager";
|
||||
@ -641,6 +641,24 @@ export const currGuildActiveChannelState = selector<LoadableValue<Channel>>({
|
||||
dangerouslyAllowMutability: true
|
||||
});
|
||||
|
||||
// Helper functions for using softImgSrc states
|
||||
function useLoadedOrElse<T>(loadable: Loadable<T>, ifLoading: T, ifError: T) {
|
||||
if (loadable.state === 'hasValue') {
|
||||
return loadable.contents;
|
||||
} else if (loadable.state === 'loading') {
|
||||
return ifLoading;
|
||||
} else {
|
||||
return ifError;
|
||||
}
|
||||
}
|
||||
function useRecoilValueLoadableOrElse<T>(recoilValue: RecoilValue<T>, ifLoading: T, ifError: T): T {
|
||||
const loadableValue = useRecoilValueLoadable(recoilValue);
|
||||
return useLoadedOrElse(loadableValue, ifLoading, ifError);
|
||||
}
|
||||
export function useRecoilValueSoftImgSrc(recoilValue: RecoilValue<string>): string {
|
||||
return useRecoilValueLoadableOrElse(recoilValue, './img/loading.svg', './img/error.png');
|
||||
}
|
||||
|
||||
// Initialize with a guildsManager
|
||||
export function initRecoil(guildsManager: GuildsManager) {
|
||||
const setGuildsManager = useSetRecoilState(guildsManagerState);
|
||||
|
@ -760,7 +760,7 @@ function useResourceSubscription(guild: CombinedGuild, resourceId: string | null
|
||||
* fetchError: Any error from fetching
|
||||
* ]
|
||||
*/
|
||||
export function useSoftImageSrcResourceSubscription(guild: CombinedGuild, resourceId: string | null, resourceIdGuild: CombinedGuild | null): [
|
||||
function useSoftImageSrcResourceSubscription(guild: CombinedGuild, resourceId: string | null, resourceIdGuild: CombinedGuild | null): [
|
||||
imgSrc: string,
|
||||
resourceResult: SubscriptionResult<Resource> | null,
|
||||
fetchError: unknown | null
|
||||
|
Loading…
Reference in New Issue
Block a user