pure react overlays
This commit is contained in:
parent
4909998144
commit
c90641a14c
@ -4,35 +4,25 @@ import Logger from '../../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import React, { FC, RefObject, useCallback, useEffect } from "react";
|
||||
import ElementsUtil from '../require/elements-util';
|
||||
import ReactHelper from '../require/react-helper';
|
||||
|
||||
interface OverlayProps {
|
||||
childRootRef?: RefObject<HTMLElement>; // clicks outside this ref will close the overlay
|
||||
close?: () => void;
|
||||
close: () => void;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
const Overlay: FC<OverlayProps> = (props: OverlayProps) => {
|
||||
const { childRootRef, close, children } = props;
|
||||
|
||||
const removeSelf = useCallback(() => {
|
||||
if (close) {
|
||||
close();
|
||||
} else {
|
||||
LOG.warn('closing react overlay with ElementsUtil (deprecated)');
|
||||
ElementsUtil.closeReactOverlay(document);
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (childRootRef) {
|
||||
ReactHelper.useCloseWhenClickedOrContextOutsideEffect(childRootRef, () => { removeSelf(); });
|
||||
ReactHelper.useCloseWhenClickedOrContextOutsideEffect(childRootRef, close);
|
||||
}
|
||||
|
||||
const keyDownHandler = useCallback((e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
removeSelf();
|
||||
close()
|
||||
}
|
||||
}, [ removeSelf ]);
|
||||
}, [ close ]);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('keydown', keyDownHandler);
|
||||
|
@ -1,8 +1,7 @@
|
||||
import React, { FC, RefObject, useCallback, useMemo } from 'react';
|
||||
import React, { Dispatch, FC, ReactNode, RefObject, SetStateAction, useCallback, useMemo } from 'react';
|
||||
import { Member } from '../../data-types';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import PersonalizeOverlay from '../overlays/overlay-personalize';
|
||||
import ElementsUtil from '../require/elements-util';
|
||||
import ContextMenu from './components/context-menu';
|
||||
|
||||
export interface ConnectionInfoContextMenuProps {
|
||||
@ -10,10 +9,11 @@ export interface ConnectionInfoContextMenuProps {
|
||||
selfMember: Member;
|
||||
relativeToRef: RefObject<HTMLElement>;
|
||||
close: () => void;
|
||||
setOverlay: Dispatch<SetStateAction<ReactNode>>;
|
||||
}
|
||||
|
||||
const ConnectionInfoContextMenu: FC<ConnectionInfoContextMenuProps> = (props: ConnectionInfoContextMenuProps) => {
|
||||
const { guild, selfMember, relativeToRef, close } = props;
|
||||
const { guild, selfMember, relativeToRef, close, setOverlay } = props;
|
||||
|
||||
const setSelfStatus = useCallback(async (status: string) => {
|
||||
await guild.requestSetStatus(status);
|
||||
@ -33,9 +33,7 @@ const ConnectionInfoContextMenu: FC<ConnectionInfoContextMenuProps> = (props: Co
|
||||
|
||||
const openPersonalize = useCallback(() => {
|
||||
close();
|
||||
// Note: using global document, not very safe >:|
|
||||
// TODO: Do this in full react (also fixes global document problem)
|
||||
ElementsUtil.presentReactOverlay(document, <PersonalizeOverlay document={document} guild={guild} selfMember={selfMember} />);
|
||||
setOverlay(<PersonalizeOverlay document={document} guild={guild} selfMember={selfMember} close={() => setOverlay(null)} />);
|
||||
}, [ guild, selfMember, close ]);
|
||||
|
||||
const alignment = useMemo(() => {
|
||||
|
@ -1,10 +1,9 @@
|
||||
import React, { FC, RefObject, useCallback, useMemo } from 'react';
|
||||
import React, { Dispatch, FC, ReactNode, RefObject, SetStateAction, useCallback, useMemo } from 'react';
|
||||
import { GuildMetadata, Member } from '../../data-types';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import ChannelOverlay from '../overlays/overlay-channel';
|
||||
import GuildSettingsOverlay from '../overlays/overlay-guild-settings';
|
||||
import BaseElements from '../require/base-elements';
|
||||
import ElementsUtil from '../require/elements-util';
|
||||
import ContextMenu from './components/context-menu';
|
||||
|
||||
export interface GuildTitleContextMenuProps {
|
||||
@ -13,19 +12,20 @@ export interface GuildTitleContextMenuProps {
|
||||
guild: CombinedGuild;
|
||||
guildMeta: GuildMetadata;
|
||||
selfMember: Member;
|
||||
setOverlay: Dispatch<SetStateAction<ReactNode>>;
|
||||
}
|
||||
|
||||
const GuildTitleContextMenu: FC<GuildTitleContextMenuProps> = (props: GuildTitleContextMenuProps) => {
|
||||
const { close, relativeToRef, guild, guildMeta, selfMember } = props;
|
||||
const { close, relativeToRef, guild, guildMeta, selfMember, setOverlay } = props;
|
||||
|
||||
const openGuildSettings = useCallback(() => {
|
||||
close();
|
||||
ElementsUtil.presentReactOverlay(document, <GuildSettingsOverlay guild={guild} guildMeta={guildMeta} />);
|
||||
setOverlay(<GuildSettingsOverlay guild={guild} guildMeta={guildMeta} close={() => setOverlay(null)} />);
|
||||
}, [ guild, guildMeta, close ]);
|
||||
|
||||
const openCreateChannel = useCallback(() => {
|
||||
close();
|
||||
ElementsUtil.presentReactOverlay(document, <ChannelOverlay guild={guild} />);
|
||||
setOverlay(<ChannelOverlay guild={guild} close={() => setOverlay(null)} />);
|
||||
}, [ guild, close ]);
|
||||
|
||||
const guildSettingsElement = useMemo(() => {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { Dispatch, FC, SetStateAction, useMemo } from 'react'
|
||||
import React, { Dispatch, FC, ReactNode, SetStateAction, useMemo } from 'react'
|
||||
import { Channel, Member } from '../../data-types';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import ChannelElement from './components/channel-element';
|
||||
@ -10,10 +10,15 @@ export interface ChannelListProps {
|
||||
channelsFetchError: unknown | null;
|
||||
activeChannel: Channel | null;
|
||||
setActiveChannel: Dispatch<SetStateAction<Channel | null>>;
|
||||
setOverlay: Dispatch<SetStateAction<ReactNode>>;
|
||||
}
|
||||
|
||||
const ChannelList: FC<ChannelListProps> = (props: ChannelListProps) => {
|
||||
const { guild, selfMember, channels, channelsFetchError, activeChannel, setActiveChannel } = props;
|
||||
const { guild, selfMember, channels, channelsFetchError, activeChannel, setActiveChannel, setOverlay } = props;
|
||||
|
||||
const hasModifyPrivilege = selfMember && selfMember.privileges.includes('modify_channels');
|
||||
|
||||
const baseClassName = hasModifyPrivilege ? 'channel-list modify_channels' : 'channel-list'
|
||||
|
||||
const channelElements = useMemo(() => {
|
||||
if (!selfMember) return null;
|
||||
@ -29,12 +34,14 @@ const ChannelList: FC<ChannelListProps> = (props: ChannelListProps) => {
|
||||
key={channel.id} guild={guild}
|
||||
selfMember={selfMember}
|
||||
channel={channel} activeChannel={activeChannel}
|
||||
setActiveChannel={() => { setActiveChannel(channel); }} />
|
||||
setActiveChannel={() => { setActiveChannel(channel); }}
|
||||
setOverlay={setOverlay}
|
||||
/>
|
||||
));
|
||||
}, [ selfMember, channelsFetchError, channels, guild, selfMember, activeChannel ]);
|
||||
|
||||
return (
|
||||
<div className="channel-list">
|
||||
<div className={baseClassName}>
|
||||
{channelElements}
|
||||
</div>
|
||||
);
|
||||
|
@ -3,12 +3,11 @@ const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||
import Logger from '../../../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import React, { Dispatch, FC, MouseEvent, SetStateAction, useCallback, useRef } from 'react'
|
||||
import React, { Dispatch, FC, MouseEvent, ReactNode, SetStateAction, useCallback, useRef } from 'react'
|
||||
import { Channel, Member } from '../../../data-types';
|
||||
import CombinedGuild from '../../../guild-combined';
|
||||
import ChannelOverlay from '../../overlays/overlay-channel';
|
||||
import BaseElements from '../../require/base-elements';
|
||||
import ElementsUtil from '../../require/elements-util';
|
||||
import ReactHelper from '../../require/react-helper';
|
||||
import BasicHover, { BasicHoverSide } from '../../contexts/context-hover-basic';
|
||||
|
||||
@ -18,10 +17,11 @@ export interface ChannelElementProps {
|
||||
selfMember: Member; // Note: Expected to use this later since it may not be best to have css-based hiding
|
||||
activeChannel: Channel | null;
|
||||
setActiveChannel: Dispatch<SetStateAction<Channel | null>>;
|
||||
setOverlay: Dispatch<SetStateAction<ReactNode>>;
|
||||
}
|
||||
|
||||
const ChannelElement: FC<ChannelElementProps> = (props: ChannelElementProps) => {
|
||||
const { guild, channel, activeChannel, setActiveChannel } = props;
|
||||
const { guild, channel, selfMember, activeChannel, setActiveChannel, setOverlay } = props;
|
||||
|
||||
const modifyRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -45,9 +45,8 @@ const ChannelElement: FC<ChannelElementProps> = (props: ChannelElementProps) =>
|
||||
setActiveChannel(channel);
|
||||
}, [ modifyRef, channel ]);
|
||||
|
||||
// Note: this element will be hidden by CSS
|
||||
const launchModify = useCallback(() => {
|
||||
ElementsUtil.presentReactOverlay(document, <ChannelOverlay guild={guild} channel={channel} />);
|
||||
setOverlay(<ChannelOverlay guild={guild} channel={channel} close={() => { setOverlay(null); }} />);
|
||||
}, [ guild, channel ]);
|
||||
|
||||
return (
|
||||
|
@ -1,5 +1,5 @@
|
||||
import moment from 'moment';
|
||||
import React, { FC, MouseEvent, useCallback, useMemo, useState } from 'react';
|
||||
import React, { Dispatch, FC, MouseEvent, ReactNode, SetStateAction, useCallback, useMemo, useState } from 'react';
|
||||
import { Member, Message } from '../../../data-types';
|
||||
import CombinedGuild from '../../../guild-combined';
|
||||
import ImageContextMenu from '../../contexts/context-menu-image';
|
||||
@ -46,10 +46,11 @@ interface PreviewImageElementProps {
|
||||
resourcePreviewId: string;
|
||||
resourceId: string;
|
||||
resourceName: string;
|
||||
setOverlay: Dispatch<SetStateAction<ReactNode>>;
|
||||
}
|
||||
|
||||
const PreviewImageElement: FC<PreviewImageElementProps> = (props: PreviewImageElementProps) => {
|
||||
const { guild, previewWidth, previewHeight, resourcePreviewId, resourceId, resourceName } = props;
|
||||
const { guild, previewWidth, previewHeight, resourcePreviewId, resourceId, resourceName, setOverlay } = props;
|
||||
|
||||
// TODO: Handle resourceError
|
||||
const [ previewImgSrc, previewResource, previewResourceError ] = GuildSubscriptions.useSoftImageSrcResourceSubscription(guild, resourcePreviewId);
|
||||
@ -66,8 +67,7 @@ const PreviewImageElement: FC<PreviewImageElementProps> = (props: PreviewImageEl
|
||||
}, [ previewResource, relativeToPos, resourceName ]);
|
||||
|
||||
const openImageOverlay = useCallback(() => {
|
||||
// Note: document here isn't 100% guaranteed (I think) but we should be getting rid of this eventually anyway
|
||||
ElementsUtil.presentReactOverlay(document, <ImageOverlay guild={guild} resourceId={resourceId} resourceName={resourceName} />);
|
||||
setOverlay(<ImageOverlay guild={guild} resourceId={resourceId} resourceName={resourceName} close={() => setOverlay(null)} />);
|
||||
}, [ guild, resourceId, resourceName ]);
|
||||
|
||||
const onContextMenu = useCallback((event: MouseEvent<HTMLImageElement>) => {
|
||||
@ -90,10 +90,11 @@ export interface MessageElementProps {
|
||||
guild: CombinedGuild;
|
||||
message: Message;
|
||||
prevMessage: Message | null;
|
||||
setOverlay: Dispatch<SetStateAction<ReactNode>>;
|
||||
}
|
||||
|
||||
const MessageElement: FC<MessageElementProps> = (props: MessageElementProps) => {
|
||||
const { guild, message, prevMessage } = props;
|
||||
const { guild, message, prevMessage, setOverlay } = props;
|
||||
|
||||
const className = useMemo(() => {
|
||||
return message.isContinued(prevMessage) ? 'message-react continued' : 'message-react';
|
||||
@ -148,6 +149,7 @@ const MessageElement: FC<MessageElementProps> = (props: MessageElementProps) =>
|
||||
resourcePreviewId={message.resourcePreviewId}
|
||||
resourceId={message.resourceId}
|
||||
resourceName={message.resourceName ?? 'unknown.unk'}
|
||||
setOverlay={setOverlay}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
@ -157,7 +159,7 @@ const MessageElement: FC<MessageElementProps> = (props: MessageElementProps) =>
|
||||
/>
|
||||
);
|
||||
}
|
||||
}, [ message ])
|
||||
}, [ message ]);
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
|
@ -3,7 +3,7 @@ const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||
import Logger from '../../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import React, { FC, useMemo } from 'react';
|
||||
import React, { Dispatch, FC, ReactNode, SetStateAction, useMemo } from 'react';
|
||||
import { Channel, Message } from '../../data-types';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import MessageElement from './components/message-element';
|
||||
@ -13,10 +13,11 @@ import InfiniteScroll from '../components/infinite-scroll';
|
||||
interface MessageListProps {
|
||||
guild: CombinedGuild;
|
||||
channel: Channel;
|
||||
setOverlay: Dispatch<SetStateAction<ReactNode>>;
|
||||
}
|
||||
|
||||
const MessageList: FC<MessageListProps> = (props: MessageListProps) => {
|
||||
const { guild, channel } = props;
|
||||
const { guild, channel, setOverlay } = props;
|
||||
|
||||
const [
|
||||
fetchRetryCallable,
|
||||
@ -36,7 +37,7 @@ const MessageList: FC<MessageListProps> = (props: MessageListProps) => {
|
||||
for (let i = 0; i < messages.length; ++i) {
|
||||
const prevMessage = messages[i - 1] ?? null;
|
||||
const message = messages[i] as Message;
|
||||
result.push(<MessageElement key={message.id} guild={guild} message={message} prevMessage={prevMessage} />);
|
||||
result.push(<MessageElement key={message.id} guild={guild} message={message} prevMessage={prevMessage} setOverlay={setOverlay} />);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -1,6 +1,6 @@
|
||||
@import "../../styles/theme.scss";
|
||||
|
||||
#react-overlays > .overlay > .content.add-guild {
|
||||
.react-overlays > .overlay > .content.add-guild {
|
||||
min-width: 350px;
|
||||
background-color: $background-secondary;
|
||||
border-radius: 8px;
|
||||
|
@ -12,7 +12,6 @@ import Globals from '../../globals';
|
||||
import SubmitOverlayLower from '../components/submit-overlay-lower';
|
||||
import path from 'path';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import ElementsUtil from '../require/elements-util';
|
||||
import InvitePreview from '../components/invite-preview';
|
||||
import ReactHelper from '../require/react-helper';
|
||||
import * as fs from 'fs/promises';
|
||||
@ -54,10 +53,11 @@ export interface AddGuildOverlayProps {
|
||||
guildsManager: GuildsManager;
|
||||
addGuildData: IAddGuildData;
|
||||
setActiveGuild: Dispatch<SetStateAction<CombinedGuild | null>>;
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
const AddGuildOverlay: FC<AddGuildOverlayProps> = (props: AddGuildOverlayProps) => {
|
||||
const { guildsManager, addGuildData, setActiveGuild } = props;
|
||||
const { guildsManager, addGuildData, setActiveGuild, close } = props;
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -103,15 +103,15 @@ const AddGuildOverlay: FC<AddGuildOverlayProps> = (props: AddGuildOverlayProps)
|
||||
newGuild = await guildsManager.addNewGuild(addGuildData, displayName, avatarBuff);
|
||||
} catch (e: unknown) {
|
||||
LOG.error('error adding new guild', e);
|
||||
return { result: null, errorMessage: 'Error adding new guild' };
|
||||
return { result: null, errorMessage: (e as Error).message ?? 'Error adding new guild' };
|
||||
}
|
||||
|
||||
setActiveGuild(newGuild);
|
||||
|
||||
ElementsUtil.closeReactOverlay(document);
|
||||
close();
|
||||
return { result: newGuild, errorMessage: null };
|
||||
},
|
||||
[ displayName, avatarBuff, displayNameInputValid, avatarInputValid ]
|
||||
[ displayName, avatarBuff, displayNameInputValid, avatarInputValid, close ]
|
||||
);
|
||||
|
||||
const errorMessage = useMemo(() => {
|
||||
@ -121,7 +121,7 @@ const AddGuildOverlay: FC<AddGuildOverlayProps> = (props: AddGuildOverlayProps)
|
||||
}, [ validationErrorMessage, submitFailMessage ]);
|
||||
|
||||
return (
|
||||
<Overlay childRootRef={rootRef}>
|
||||
<Overlay childRootRef={rootRef} close={close} >
|
||||
<div ref={rootRef} className="content add-guild">
|
||||
<InvitePreview
|
||||
name={addGuildData.name} iconSrc={addGuildData.iconSrc}
|
||||
|
@ -1,6 +1,6 @@
|
||||
@import "../../styles/theme.scss";
|
||||
|
||||
#react-overlays > .overlay > .content.modify-channel {
|
||||
.react-overlays > .overlay > .content.modify-channel {
|
||||
min-width: 350px;
|
||||
max-width: calc(100vw - 80px);
|
||||
|
||||
|
@ -9,7 +9,6 @@ import BaseElements from '../require/base-elements';
|
||||
import TextInput from '../components/input-text';
|
||||
import SubmitOverlayLower from '../components/submit-overlay-lower';
|
||||
import Globals from '../../globals';
|
||||
import ElementsUtil from '../require/elements-util';
|
||||
import { Channel } from '../../data-types';
|
||||
import ReactHelper from '../require/react-helper';
|
||||
import Button from '../components/button';
|
||||
@ -18,9 +17,10 @@ import Overlay from '../components/overlay';
|
||||
export interface ChannelOverlayProps {
|
||||
guild: CombinedGuild;
|
||||
channel?: Channel;
|
||||
close: () => void;
|
||||
}
|
||||
const ChannelOverlay: FC<ChannelOverlayProps> = (props: ChannelOverlayProps) => {
|
||||
const { guild, channel } = props;
|
||||
const { guild, channel, close } = props;
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
const nameInputRef = useRef<HTMLInputElement>(null);
|
||||
@ -66,7 +66,7 @@ const ChannelOverlay: FC<ChannelOverlayProps> = (props: ChannelOverlayProps) =>
|
||||
if (validationErrorMessage) return { result: null, errorMessage: 'Invalid input' };
|
||||
|
||||
if (!edited) {
|
||||
ElementsUtil.closeReactOverlay(document);
|
||||
close();
|
||||
return { result: null, errorMessage: null };
|
||||
}
|
||||
|
||||
@ -82,10 +82,10 @@ const ChannelOverlay: FC<ChannelOverlayProps> = (props: ChannelOverlayProps) =>
|
||||
return { result: null, errorMessage: `Error ${channel ? 'updating' : 'creating'} channel`}
|
||||
}
|
||||
|
||||
ElementsUtil.closeReactOverlay(document);
|
||||
close();
|
||||
return { result: null, errorMessage: null };
|
||||
},
|
||||
[ edited, validationErrorMessage, name, flavorText ],
|
||||
[ edited, validationErrorMessage, name, flavorText, close ],
|
||||
{ start: channel ? 'Modify Channel' : 'Create Channel' }
|
||||
);
|
||||
|
||||
@ -96,7 +96,7 @@ const ChannelOverlay: FC<ChannelOverlayProps> = (props: ChannelOverlayProps) =>
|
||||
}, [ validationErrorMessage, submitFailMessage ]);
|
||||
|
||||
return (
|
||||
<Overlay childRootRef={rootRef}>
|
||||
<Overlay childRootRef={rootRef} close={close} >
|
||||
<div ref={rootRef} className="content submit-dialog modify-channel">
|
||||
<div className="preview channel-title">
|
||||
<div className="channel-icon">{BaseElements.TEXT_CHANNEL_ICON}</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
@import "../../styles/theme.scss";
|
||||
|
||||
#react-overlays > .overlay > .content.error-message {
|
||||
.react-overlays > .overlay > .content.error-message {
|
||||
background-color: $background-secondary;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
|
@ -4,14 +4,15 @@ import Overlay from '../components/overlay';
|
||||
export interface ErrorMessageOverlayProps {
|
||||
title: string;
|
||||
message: string;
|
||||
close: () => void;
|
||||
}
|
||||
const ErrorMessageOverlay: FC<ErrorMessageOverlayProps> = (props: ErrorMessageOverlayProps) => {
|
||||
const { title, message } = props;
|
||||
const { title, message, close } = props;
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
return (
|
||||
<Overlay childRootRef={rootRef}>
|
||||
<Overlay childRootRef={rootRef} close={close}>
|
||||
<div ref={rootRef} className="content error-message">
|
||||
<div className="icon">
|
||||
<img src="./img/error.png" alt="error" />
|
||||
|
@ -1,6 +1,6 @@
|
||||
@import "../../styles/theme.scss";
|
||||
|
||||
#react-overlays > .overlay > .content.display-swapper.guild-settings {
|
||||
.react-overlays > .overlay > .content.display-swapper.guild-settings {
|
||||
min-width: 350px;
|
||||
|
||||
.overview {
|
||||
|
@ -14,9 +14,10 @@ import Overlay from '../components/overlay';
|
||||
export interface GuildSettingsOverlayProps {
|
||||
guild: CombinedGuild;
|
||||
guildMeta: GuildMetadata;
|
||||
close: () => void;
|
||||
}
|
||||
const GuildSettingsOverlay: FC<GuildSettingsOverlayProps> = (props: GuildSettingsOverlayProps) => {
|
||||
const { guild, guildMeta } = props;
|
||||
const { guild, guildMeta, close } = props;
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -30,7 +31,7 @@ const GuildSettingsOverlay: FC<GuildSettingsOverlayProps> = (props: GuildSetting
|
||||
}, [ selectedId ]);
|
||||
|
||||
return (
|
||||
<Overlay childRootRef={rootRef}>
|
||||
<Overlay childRootRef={rootRef} close={close}>
|
||||
<div ref={rootRef} className="content display-swapper guild-settings">
|
||||
<ChoicesControl title={guildMeta.name} selectedId={selectedId} setSelectedId={setSelectedId} choices={[
|
||||
{ id: 'overview', display: 'Overview' },
|
||||
|
@ -1,6 +1,6 @@
|
||||
@import "../../styles/theme.scss";
|
||||
|
||||
#react-overlays > .overlay > .content.popup-image {
|
||||
.react-overlays > .overlay > .content.popup-image {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
|
@ -15,11 +15,12 @@ import Overlay from '../components/overlay';
|
||||
export interface ImageOverlayProps {
|
||||
guild: CombinedGuild
|
||||
resourceId: string,
|
||||
resourceName: string
|
||||
resourceName: string,
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
const ImageOverlay: FC<ImageOverlayProps> = (props: ImageOverlayProps) => {
|
||||
const { guild, resourceId, resourceName } = props;
|
||||
const { guild, resourceId, resourceName, close } = props;
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -44,7 +45,7 @@ const ImageOverlay: FC<ImageOverlayProps> = (props: ImageOverlayProps) => {
|
||||
const sizeText = useMemo(() => resource ? ElementsUtil.humanSize(resource.data.length) : 'Loading Size...', [ resource ]);
|
||||
|
||||
return (
|
||||
<Overlay childRootRef={rootRef}>
|
||||
<Overlay childRootRef={rootRef} close={close}>
|
||||
<div ref={rootRef} className="content popup-image">
|
||||
<img src={imgSrc} alt={resourceName} title={resourceName} onContextMenu={onContextMenu}></img>
|
||||
<div className="download">
|
||||
|
@ -1,6 +1,6 @@
|
||||
@import "../../styles/theme.scss";
|
||||
|
||||
#react-overlays > .overlay > .content.personalize {
|
||||
.react-overlays > .overlay > .content.personalize {
|
||||
background-color: $background-primary;
|
||||
border-radius: 8px;
|
||||
|
||||
|
@ -10,7 +10,6 @@ import CombinedGuild from '../../guild-combined';
|
||||
import ImageEditInput from '../components/input-image-edit';
|
||||
import TextInput from '../components/input-text';
|
||||
import SubmitOverlayLower from '../components/submit-overlay-lower';
|
||||
import ElementsUtil from '../require/elements-util';
|
||||
import GuildSubscriptions from '../require/guild-subscriptions';
|
||||
import ReactHelper from '../require/react-helper';
|
||||
import Button from '../components/button';
|
||||
@ -20,9 +19,10 @@ export interface PersonalizeOverlayProps {
|
||||
document: Document;
|
||||
guild: CombinedGuild;
|
||||
selfMember: Member;
|
||||
close: () => void;
|
||||
}
|
||||
const PersonalizeOverlay: FC<PersonalizeOverlayProps> = (props: PersonalizeOverlayProps) => {
|
||||
const { document, guild, selfMember } = props;
|
||||
const { document, guild, selfMember, close } = props;
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -94,10 +94,10 @@ const PersonalizeOverlay: FC<PersonalizeOverlayProps> = (props: PersonalizeOverl
|
||||
}
|
||||
}
|
||||
|
||||
ElementsUtil.closeReactOverlay(document);
|
||||
close();
|
||||
return { result: null, errorMessage: null };
|
||||
},
|
||||
[ validationErrorMessage, displayName, savedDisplayName, avatarBuff, savedAvatarBuff ]
|
||||
[ validationErrorMessage, displayName, savedDisplayName, avatarBuff, savedAvatarBuff, close ]
|
||||
);
|
||||
|
||||
//if (saveFailed) return 'Unable to save personalization';
|
||||
@ -108,7 +108,7 @@ const PersonalizeOverlay: FC<PersonalizeOverlayProps> = (props: PersonalizeOverl
|
||||
}, [ validationErrorMessage, submitFailMessage ]);
|
||||
|
||||
return (
|
||||
<Overlay childRootRef={rootRef}>
|
||||
<Overlay childRootRef={rootRef} close={close}>
|
||||
<div ref={rootRef} className="content personalize">
|
||||
<div className="personalization">
|
||||
<div className="avatar">
|
||||
|
@ -1,3 +1,4 @@
|
||||
import React from 'react';
|
||||
import * as fs from 'fs/promises';
|
||||
|
||||
import * as electronRemote from '@electron/remote';
|
||||
@ -11,9 +12,6 @@ import * as uuid from 'uuid';
|
||||
import Util from '../../util';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
export interface IAlignment {
|
||||
left?: string;
|
||||
centerX?: string;
|
||||
@ -317,18 +315,4 @@ export default class ElementsUtil {
|
||||
throw new Error('x-alignment not defined');
|
||||
}
|
||||
}
|
||||
|
||||
static presentReactOverlay(document: Document, overlay: JSX.Element) {
|
||||
// for aids reasons, the click event gets sent through to the overlay so we're just adding a sleep
|
||||
// here to break the event loop. Hopefully this gets better when we don't have to do a seperate render piece
|
||||
// and we handle overlays through 100% react
|
||||
(async () => {
|
||||
await Util.sleep(0);
|
||||
ReactDOM.render(overlay, document.querySelector('#react-overlays'));
|
||||
})();
|
||||
}
|
||||
|
||||
static closeReactOverlay(document: Document) {
|
||||
ReactDOM.unmountComponentAtNode(document.querySelector('#react-overlays') as HTMLElement);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { FC } from 'react';
|
||||
import React, { FC, ReactNode, useState } from 'react';
|
||||
import GuildsManager from '../guilds-manager';
|
||||
import GuildsManagerElement from './sections/guilds-manager';
|
||||
import TitleBar from './sections/title-bar';
|
||||
@ -10,10 +10,13 @@ export interface RootElementProps {
|
||||
const RootElement: FC<RootElementProps> = (props: RootElementProps) => {
|
||||
const { guildsManager } = props;
|
||||
|
||||
const [ overlay, setOverlay ] = useState<ReactNode>(null);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TitleBar />
|
||||
<GuildsManagerElement guildsManager={guildsManager} />
|
||||
<GuildsManagerElement guildsManager={guildsManager} setOverlay={setOverlay} />
|
||||
<div className="react-overlays">{overlay}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||
import Logger from '../../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import React, { FC, useMemo, useRef } from 'react';
|
||||
import React, { Dispatch, FC, ReactNode, SetStateAction, useMemo, useRef } from 'react';
|
||||
import { Member } from '../../data-types';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import MemberElement, { DummyMember } from '../lists/components/member-element';
|
||||
@ -13,10 +13,11 @@ import ReactHelper from '../require/react-helper';
|
||||
export interface ConnectionInfoProps {
|
||||
guild: CombinedGuild;
|
||||
selfMember: Member | null;
|
||||
setOverlay: Dispatch<SetStateAction<ReactNode>>;
|
||||
}
|
||||
|
||||
const ConnectionInfo: FC<ConnectionInfoProps> = (props: ConnectionInfoProps) => {
|
||||
const { guild, selfMember } = props;
|
||||
const { guild, selfMember, setOverlay } = props;
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -37,7 +38,8 @@ const ConnectionInfo: FC<ConnectionInfoProps> = (props: ConnectionInfoProps) =>
|
||||
if (!selfMember) return null;
|
||||
return (
|
||||
<ConnectionInfoContextMenu
|
||||
guild={guild} selfMember={selfMember} relativeToRef={rootRef} close={close}
|
||||
guild={guild} selfMember={selfMember} relativeToRef={rootRef}
|
||||
close={close} setOverlay={setOverlay}
|
||||
/>
|
||||
);
|
||||
}, [ guild, selfMember, rootRef ]);
|
||||
|
@ -3,13 +3,12 @@ const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||
import Logger from '../../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import React, { Dispatch, FC, SetStateAction, useRef } from 'react';
|
||||
import React, { Dispatch, FC, ReactNode, SetStateAction, useRef } from 'react';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import GuildsManager from '../../guilds-manager';
|
||||
import GuildList from '../lists/guild-list';
|
||||
import ReactHelper from '../require/react-helper';
|
||||
import fs from 'fs/promises';
|
||||
import ElementsUtil from '../require/elements-util';
|
||||
import AddGuildOverlay from '../overlays/overlay-add-guild';
|
||||
import ErrorMessageOverlay from '../overlays/overlay-error-message';
|
||||
import BasicHover, { BasicHoverSide } from '../contexts/context-hover-basic';
|
||||
@ -20,10 +19,11 @@ export interface GuildListContainerProps {
|
||||
guilds: CombinedGuild[];
|
||||
activeGuild: CombinedGuild | null;
|
||||
setActiveGuild: Dispatch<SetStateAction<CombinedGuild | null>>;
|
||||
setOverlay: Dispatch<SetStateAction<ReactNode>>;
|
||||
}
|
||||
|
||||
const GuildListContainer: FC<GuildListContainerProps> = (props: GuildListContainerProps) => {
|
||||
const { guildsManager, guilds, activeGuild, setActiveGuild } = props;
|
||||
const { guildsManager, guilds, activeGuild, setActiveGuild, setOverlay } = props;
|
||||
|
||||
const addGuildRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -66,9 +66,9 @@ const GuildListContainer: FC<GuildListContainerProps> = (props: GuildListContain
|
||||
typeof addGuildData?.iconSrc !== 'string'
|
||||
) {
|
||||
LOG.debug('bad guild data:', { addGuildData, fileText });
|
||||
ElementsUtil.presentReactOverlay(document, <ErrorMessageOverlay title="Unable to parse guild file" message={'bad guild data'} />);
|
||||
setOverlay(<ErrorMessageOverlay title="Unable to parse guild file" message="Bad guild data" close={() => setOverlay(null)} />);
|
||||
} else {
|
||||
ElementsUtil.presentReactOverlay(document, <AddGuildOverlay guildsManager={guildsManager} addGuildData={addGuildData} setActiveGuild={setActiveGuild} />);
|
||||
setOverlay(<AddGuildOverlay guildsManager={guildsManager} addGuildData={addGuildData} setActiveGuild={setActiveGuild} close={() => setOverlay(null)} />);
|
||||
}
|
||||
}, [ guildsManager ]);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { FC, useMemo, useRef } from 'react';
|
||||
import React, { Dispatch, FC, ReactNode, SetStateAction, useMemo, useRef } from 'react';
|
||||
import { GuildMetadata, Member } from '../../data-types';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import GuildTitleContextMenu from '../contexts/context-menu-guild-title';
|
||||
@ -8,10 +8,11 @@ export interface GuildTitleProps {
|
||||
guild: CombinedGuild;
|
||||
guildMeta: GuildMetadata | null;
|
||||
selfMember: Member | null;
|
||||
setOverlay: Dispatch<SetStateAction<ReactNode>>;
|
||||
}
|
||||
|
||||
const GuildTitle: FC<GuildTitleProps> = (props: GuildTitleProps) => {
|
||||
const { guild, guildMeta, selfMember } = props;
|
||||
const { guild, guildMeta, selfMember, setOverlay } = props;
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -32,6 +33,7 @@ const GuildTitle: FC<GuildTitleProps> = (props: GuildTitleProps) => {
|
||||
<GuildTitleContextMenu
|
||||
relativeToRef={rootRef} close={close}
|
||||
guild={guild} guildMeta={guildMeta} selfMember={selfMember}
|
||||
setOverlay={setOverlay}
|
||||
/>
|
||||
);
|
||||
}, [ guild, guildMeta, selfMember, rootRef ]);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { FC, useEffect, useState } from 'react';
|
||||
import React, { Dispatch, FC, ReactNode, SetStateAction, useEffect, useState } from 'react';
|
||||
import { Channel } from '../../data-types';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import ChannelList from '../lists/channel-list';
|
||||
@ -12,10 +12,11 @@ import SendMessage from './send-message';
|
||||
|
||||
export interface GuildElementProps {
|
||||
guild: CombinedGuild;
|
||||
setOverlay: Dispatch<SetStateAction<ReactNode>>;
|
||||
}
|
||||
|
||||
const GuildElement: FC<GuildElementProps> = (props: GuildElementProps) => {
|
||||
const { guild } = props;
|
||||
const { guild, setOverlay } = props;
|
||||
|
||||
// TODO: Handle fetch errors by allowing for retry
|
||||
// TODO: Handle fetch errors in message list
|
||||
@ -48,18 +49,20 @@ const GuildElement: FC<GuildElementProps> = (props: GuildElementProps) => {
|
||||
return (
|
||||
<div className="guild-react">
|
||||
<div className="guild-sidebar">
|
||||
<GuildTitle guild={guild} selfMember={selfMember} guildMeta={guildMeta} />
|
||||
<GuildTitle guild={guild} selfMember={selfMember} guildMeta={guildMeta} setOverlay={setOverlay} />
|
||||
<ChannelList
|
||||
guild={guild} selfMember={selfMember}
|
||||
channels={channels} channelsFetchError={channelsFetchError}
|
||||
activeChannel={activeChannel} setActiveChannel={setActiveChannel} />
|
||||
<ConnectionInfo guild={guild} selfMember={selfMember} />
|
||||
activeChannel={activeChannel} setActiveChannel={setActiveChannel}
|
||||
setOverlay={setOverlay}
|
||||
/>
|
||||
<ConnectionInfo guild={guild} selfMember={selfMember} setOverlay={setOverlay} />
|
||||
</div>
|
||||
<div className="guild-channel">
|
||||
<ChannelTitle channel={activeChannel} />
|
||||
<div className="guild-channel-content">
|
||||
<div className="guild-channel-feed-wrapper">
|
||||
{activeChannel && <MessageList guild={guild} channel={activeChannel} />}
|
||||
{activeChannel && <MessageList guild={guild} channel={activeChannel} setOverlay={setOverlay} />}
|
||||
{activeChannel && <SendMessage guild={guild} channel={activeChannel} />}
|
||||
</div>
|
||||
<div className="member-list-wrapper">
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { FC, useEffect, useState } from 'react';
|
||||
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';
|
||||
@ -7,10 +7,11 @@ import GuildListContainer from './guild-list-container';
|
||||
|
||||
export interface GuildsManagerElementProps {
|
||||
guildsManager: GuildsManager;
|
||||
setOverlay: Dispatch<SetStateAction<ReactNode>>;
|
||||
}
|
||||
|
||||
const GuildsManagerElement: FC<GuildsManagerElementProps> = (props: GuildsManagerElementProps) => {
|
||||
const { guildsManager } = props;
|
||||
const { guildsManager, setOverlay } = props;
|
||||
|
||||
const [ guilds ] = useGuildListSubscription(guildsManager);
|
||||
const [ activeGuild, setActiveGuild ] = useState<CombinedGuild | null>(null);
|
||||
@ -28,8 +29,10 @@ const GuildsManagerElement: FC<GuildsManagerElementProps> = (props: GuildsManage
|
||||
<div className="guilds-manager">
|
||||
<GuildListContainer
|
||||
guildsManager={guildsManager} guilds={guilds}
|
||||
activeGuild={activeGuild} setActiveGuild={setActiveGuild} />
|
||||
{activeGuild && <GuildElement guild={activeGuild} />}
|
||||
activeGuild={activeGuild} setActiveGuild={setActiveGuild}
|
||||
setOverlay={setOverlay}
|
||||
/>
|
||||
{activeGuild && <GuildElement guild={activeGuild} setOverlay={setOverlay} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -48,4 +48,8 @@
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.channel:hover .modify {
|
||||
display: unset;
|
||||
}
|
||||
}
|
||||
|
@ -10,59 +10,59 @@
|
||||
height: calc(100vh - 22px);
|
||||
padding-top: 8px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.guild-list {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
.guild-listguild-list::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.add-guild,
|
||||
.guild-list .guild {
|
||||
cursor: pointer;
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.add-guild .pill,
|
||||
.guild-list .guild .pill {
|
||||
background-color: $header-primary;
|
||||
width: 8px;
|
||||
height: 0;
|
||||
border-radius: 4px 4px;
|
||||
margin-left: -4px;
|
||||
margin-right: 8px;
|
||||
transition: height .1s ease-in-out;
|
||||
}
|
||||
|
||||
.guild-list .guild.active .pill {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.guild-list .guild.unread:not(.active) .pill {
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.add-guild:hover .pill,
|
||||
.guild-list .guild:not(.active):hover .pill {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.add-guild img,
|
||||
.guild-list .guild img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 24px;
|
||||
transition: border-radius .1s ease-in-out;
|
||||
}
|
||||
|
||||
.add-guild:hover img,
|
||||
.guild-list .guild:hover img,
|
||||
.guild-list .guild.active img {
|
||||
border-radius: 16px;
|
||||
.guild-list {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
.guild-listguild-list::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.add-guild,
|
||||
.guild-list .guild {
|
||||
cursor: pointer;
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.add-guild .pill,
|
||||
.guild-list .guild .pill {
|
||||
background-color: $header-primary;
|
||||
width: 8px;
|
||||
height: 0;
|
||||
border-radius: 4px 4px;
|
||||
margin-left: -4px;
|
||||
margin-right: 8px;
|
||||
transition: height .1s ease-in-out;
|
||||
}
|
||||
|
||||
.guild-list .guild.active .pill {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.guild-list .guild.unread:not(.active) .pill {
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.add-guild:hover .pill,
|
||||
.guild-list .guild:not(.active):hover .pill {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.add-guild img,
|
||||
.guild-list .guild img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 24px;
|
||||
transition: border-radius .1s ease-in-out;
|
||||
}
|
||||
|
||||
.add-guild:hover img,
|
||||
.guild-list .guild:hover img,
|
||||
.guild-list .guild.active img {
|
||||
border-radius: 16px;
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
/* Popup Image Overlay */
|
||||
|
||||
body > .overlay,
|
||||
#react-overlays > .overlay {
|
||||
.react-overlays > .overlay {
|
||||
/* Note: skip top 22px so we don't overlay on the title bar */
|
||||
position: absolute;
|
||||
width: 100vw;
|
||||
|
Loading…
Reference in New Issue
Block a user