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