From c90641a14c26f1f95a16acd18e3a908fbb70cc09 Mon Sep 17 00:00:00 2001 From: Michael Peters Date: Wed, 29 Dec 2021 23:53:04 -0600 Subject: [PATCH] pure react overlays --- .../webapp/elements/components/overlay.tsx | 18 +-- .../contexts/context-menu-connection-info.tsx | 10 +- .../contexts/context-menu-guild-title.tsx | 10 +- .../webapp/elements/lists/channel-list.tsx | 15 ++- .../lists/components/channel-element.tsx | 9 +- .../lists/components/message-element.tsx | 14 ++- .../webapp/elements/lists/message-list.tsx | 7 +- .../elements/overlays/overlay-add-guild.scss | 2 +- .../elements/overlays/overlay-add-guild.tsx | 12 +- .../elements/overlays/overlay-channel.scss | 2 +- .../elements/overlays/overlay-channel.tsx | 12 +- .../overlays/overlay-error-message.scss | 2 +- .../overlays/overlay-error-message.tsx | 5 +- .../overlays/overlay-guild-settings.scss | 2 +- .../overlays/overlay-guild-settings.tsx | 5 +- .../elements/overlays/overlay-image.scss | 2 +- .../elements/overlays/overlay-image.tsx | 7 +- .../overlays/overlay-personalize.scss | 2 +- .../elements/overlays/overlay-personalize.tsx | 10 +- .../webapp/elements/require/elements-util.tsx | 18 +-- src/client/webapp/elements/root.tsx | 7 +- .../elements/sections/connection-info.tsx | 8 +- .../sections/guild-list-container.tsx | 10 +- .../webapp/elements/sections/guild-title.tsx | 6 +- src/client/webapp/elements/sections/guild.tsx | 15 ++- .../elements/sections/guilds-manager.tsx | 11 +- src/client/webapp/styles/channel-list.scss | 4 + src/client/webapp/styles/guild-list.scss | 108 +++++++++--------- src/client/webapp/styles/overlays.scss | 2 +- 29 files changed, 168 insertions(+), 167 deletions(-) diff --git a/src/client/webapp/elements/components/overlay.tsx b/src/client/webapp/elements/components/overlay.tsx index 3837a60..a3e1700 100644 --- a/src/client/webapp/elements/components/overlay.tsx +++ b/src/client/webapp/elements/components/overlay.tsx @@ -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; // clicks outside this ref will close the overlay - close?: () => void; + close: () => void; children: React.ReactNode; } const Overlay: FC = (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); diff --git a/src/client/webapp/elements/contexts/context-menu-connection-info.tsx b/src/client/webapp/elements/contexts/context-menu-connection-info.tsx index ac16044..12e4af9 100644 --- a/src/client/webapp/elements/contexts/context-menu-connection-info.tsx +++ b/src/client/webapp/elements/contexts/context-menu-connection-info.tsx @@ -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; close: () => void; + setOverlay: Dispatch>; } const ConnectionInfoContextMenu: FC = (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 = (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, ); + setOverlay( setOverlay(null)} />); }, [ guild, selfMember, close ]); const alignment = useMemo(() => { diff --git a/src/client/webapp/elements/contexts/context-menu-guild-title.tsx b/src/client/webapp/elements/contexts/context-menu-guild-title.tsx index 11e67a4..82ff559 100644 --- a/src/client/webapp/elements/contexts/context-menu-guild-title.tsx +++ b/src/client/webapp/elements/contexts/context-menu-guild-title.tsx @@ -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>; } const GuildTitleContextMenu: FC = (props: GuildTitleContextMenuProps) => { - const { close, relativeToRef, guild, guildMeta, selfMember } = props; + const { close, relativeToRef, guild, guildMeta, selfMember, setOverlay } = props; const openGuildSettings = useCallback(() => { close(); - ElementsUtil.presentReactOverlay(document, ); + setOverlay( setOverlay(null)} />); }, [ guild, guildMeta, close ]); const openCreateChannel = useCallback(() => { close(); - ElementsUtil.presentReactOverlay(document, ); + setOverlay( setOverlay(null)} />); }, [ guild, close ]); const guildSettingsElement = useMemo(() => { diff --git a/src/client/webapp/elements/lists/channel-list.tsx b/src/client/webapp/elements/lists/channel-list.tsx index 638862d..e8d78d3 100644 --- a/src/client/webapp/elements/lists/channel-list.tsx +++ b/src/client/webapp/elements/lists/channel-list.tsx @@ -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>; + setOverlay: Dispatch>; } const ChannelList: FC = (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 = (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 ( -
+
{channelElements}
); diff --git a/src/client/webapp/elements/lists/components/channel-element.tsx b/src/client/webapp/elements/lists/components/channel-element.tsx index 0e272fe..2fd964e 100644 --- a/src/client/webapp/elements/lists/components/channel-element.tsx +++ b/src/client/webapp/elements/lists/components/channel-element.tsx @@ -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>; + setOverlay: Dispatch>; } const ChannelElement: FC = (props: ChannelElementProps) => { - const { guild, channel, activeChannel, setActiveChannel } = props; + const { guild, channel, selfMember, activeChannel, setActiveChannel, setOverlay } = props; const modifyRef = useRef(null); @@ -45,9 +45,8 @@ const ChannelElement: FC = (props: ChannelElementProps) => setActiveChannel(channel); }, [ modifyRef, channel ]); - // Note: this element will be hidden by CSS const launchModify = useCallback(() => { - ElementsUtil.presentReactOverlay(document, ); + setOverlay( { setOverlay(null); }} />); }, [ guild, channel ]); return ( diff --git a/src/client/webapp/elements/lists/components/message-element.tsx b/src/client/webapp/elements/lists/components/message-element.tsx index 1b27019..a986d61 100644 --- a/src/client/webapp/elements/lists/components/message-element.tsx +++ b/src/client/webapp/elements/lists/components/message-element.tsx @@ -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>; } const PreviewImageElement: FC = (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 = (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, ); + setOverlay( setOverlay(null)} />); }, [ guild, resourceId, resourceName ]); const onContextMenu = useCallback((event: MouseEvent) => { @@ -90,10 +90,11 @@ export interface MessageElementProps { guild: CombinedGuild; message: Message; prevMessage: Message | null; + setOverlay: Dispatch>; } const MessageElement: FC = (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 = (props: MessageElementProps) => resourcePreviewId={message.resourcePreviewId} resourceId={message.resourceId} resourceName={message.resourceName ?? 'unknown.unk'} + setOverlay={setOverlay} /> ); } else { @@ -157,7 +159,7 @@ const MessageElement: FC = (props: MessageElementProps) => /> ); } - }, [ message ]) + }, [ message ]); return (
diff --git a/src/client/webapp/elements/lists/message-list.tsx b/src/client/webapp/elements/lists/message-list.tsx index 69d1be8..47cc7c5 100644 --- a/src/client/webapp/elements/lists/message-list.tsx +++ b/src/client/webapp/elements/lists/message-list.tsx @@ -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>; } const MessageList: FC = (props: MessageListProps) => { - const { guild, channel } = props; + const { guild, channel, setOverlay } = props; const [ fetchRetryCallable, @@ -36,7 +37,7 @@ const MessageList: FC = (props: MessageListProps) => { for (let i = 0; i < messages.length; ++i) { const prevMessage = messages[i - 1] ?? null; const message = messages[i] as Message; - result.push(); + result.push(); } } return result; diff --git a/src/client/webapp/elements/overlays/overlay-add-guild.scss b/src/client/webapp/elements/overlays/overlay-add-guild.scss index 8ad14d9..c13ba44 100644 --- a/src/client/webapp/elements/overlays/overlay-add-guild.scss +++ b/src/client/webapp/elements/overlays/overlay-add-guild.scss @@ -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; diff --git a/src/client/webapp/elements/overlays/overlay-add-guild.tsx b/src/client/webapp/elements/overlays/overlay-add-guild.tsx index 1b57f2d..fe0625e 100644 --- a/src/client/webapp/elements/overlays/overlay-add-guild.tsx +++ b/src/client/webapp/elements/overlays/overlay-add-guild.tsx @@ -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>; + close: () => void; } const AddGuildOverlay: FC = (props: AddGuildOverlayProps) => { - const { guildsManager, addGuildData, setActiveGuild } = props; + const { guildsManager, addGuildData, setActiveGuild, close } = props; const rootRef = useRef(null); @@ -103,15 +103,15 @@ const AddGuildOverlay: FC = (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 = (props: AddGuildOverlayProps) }, [ validationErrorMessage, submitFailMessage ]); return ( - +
.overlay > .content.modify-channel { +.react-overlays > .overlay > .content.modify-channel { min-width: 350px; max-width: calc(100vw - 80px); diff --git a/src/client/webapp/elements/overlays/overlay-channel.tsx b/src/client/webapp/elements/overlays/overlay-channel.tsx index 2c03a1f..06cc787 100644 --- a/src/client/webapp/elements/overlays/overlay-channel.tsx +++ b/src/client/webapp/elements/overlays/overlay-channel.tsx @@ -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 = (props: ChannelOverlayProps) => { - const { guild, channel } = props; + const { guild, channel, close } = props; const rootRef = useRef(null); const nameInputRef = useRef(null); @@ -66,7 +66,7 @@ const ChannelOverlay: FC = (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 = (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 = (props: ChannelOverlayProps) => }, [ validationErrorMessage, submitFailMessage ]); return ( - +
{BaseElements.TEXT_CHANNEL_ICON}
diff --git a/src/client/webapp/elements/overlays/overlay-error-message.scss b/src/client/webapp/elements/overlays/overlay-error-message.scss index 6ed88dc..32fd289 100644 --- a/src/client/webapp/elements/overlays/overlay-error-message.scss +++ b/src/client/webapp/elements/overlays/overlay-error-message.scss @@ -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; diff --git a/src/client/webapp/elements/overlays/overlay-error-message.tsx b/src/client/webapp/elements/overlays/overlay-error-message.tsx index fa483de..9310b38 100644 --- a/src/client/webapp/elements/overlays/overlay-error-message.tsx +++ b/src/client/webapp/elements/overlays/overlay-error-message.tsx @@ -4,14 +4,15 @@ import Overlay from '../components/overlay'; export interface ErrorMessageOverlayProps { title: string; message: string; + close: () => void; } const ErrorMessageOverlay: FC = (props: ErrorMessageOverlayProps) => { - const { title, message } = props; + const { title, message, close } = props; const rootRef = useRef(null); return ( - +
error diff --git a/src/client/webapp/elements/overlays/overlay-guild-settings.scss b/src/client/webapp/elements/overlays/overlay-guild-settings.scss index c0b1847..3c57821 100644 --- a/src/client/webapp/elements/overlays/overlay-guild-settings.scss +++ b/src/client/webapp/elements/overlays/overlay-guild-settings.scss @@ -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 { diff --git a/src/client/webapp/elements/overlays/overlay-guild-settings.tsx b/src/client/webapp/elements/overlays/overlay-guild-settings.tsx index 4e721ef..39d7202 100644 --- a/src/client/webapp/elements/overlays/overlay-guild-settings.tsx +++ b/src/client/webapp/elements/overlays/overlay-guild-settings.tsx @@ -14,9 +14,10 @@ import Overlay from '../components/overlay'; export interface GuildSettingsOverlayProps { guild: CombinedGuild; guildMeta: GuildMetadata; + close: () => void; } const GuildSettingsOverlay: FC = (props: GuildSettingsOverlayProps) => { - const { guild, guildMeta } = props; + const { guild, guildMeta, close } = props; const rootRef = useRef(null); @@ -30,7 +31,7 @@ const GuildSettingsOverlay: FC = (props: GuildSetting }, [ selectedId ]); return ( - +
.overlay > .content.popup-image { +.react-overlays > .overlay > .content.popup-image { display: flex; flex-flow: column; align-items: center; diff --git a/src/client/webapp/elements/overlays/overlay-image.tsx b/src/client/webapp/elements/overlays/overlay-image.tsx index d28dc4e..c994d1b 100644 --- a/src/client/webapp/elements/overlays/overlay-image.tsx +++ b/src/client/webapp/elements/overlays/overlay-image.tsx @@ -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 = (props: ImageOverlayProps) => { - const { guild, resourceId, resourceName } = props; + const { guild, resourceId, resourceName, close } = props; const rootRef = useRef(null); @@ -44,7 +45,7 @@ const ImageOverlay: FC = (props: ImageOverlayProps) => { const sizeText = useMemo(() => resource ? ElementsUtil.humanSize(resource.data.length) : 'Loading Size...', [ resource ]); return ( - +
{resourceName}
diff --git a/src/client/webapp/elements/overlays/overlay-personalize.scss b/src/client/webapp/elements/overlays/overlay-personalize.scss index 85c92b7..55faded 100644 --- a/src/client/webapp/elements/overlays/overlay-personalize.scss +++ b/src/client/webapp/elements/overlays/overlay-personalize.scss @@ -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; diff --git a/src/client/webapp/elements/overlays/overlay-personalize.tsx b/src/client/webapp/elements/overlays/overlay-personalize.tsx index 445868a..09f3fee 100644 --- a/src/client/webapp/elements/overlays/overlay-personalize.tsx +++ b/src/client/webapp/elements/overlays/overlay-personalize.tsx @@ -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 = (props: PersonalizeOverlayProps) => { - const { document, guild, selfMember } = props; + const { document, guild, selfMember, close } = props; const rootRef = useRef(null); @@ -94,10 +94,10 @@ const PersonalizeOverlay: FC = (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 = (props: PersonalizeOverl }, [ validationErrorMessage, submitFailMessage ]); return ( - +
diff --git a/src/client/webapp/elements/require/elements-util.tsx b/src/client/webapp/elements/require/elements-util.tsx index d25f86d..5e9c39a 100644 --- a/src/client/webapp/elements/require/elements-util.tsx +++ b/src/client/webapp/elements/require/elements-util.tsx @@ -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); - } } diff --git a/src/client/webapp/elements/root.tsx b/src/client/webapp/elements/root.tsx index 3c66890..6e20cfa 100644 --- a/src/client/webapp/elements/root.tsx +++ b/src/client/webapp/elements/root.tsx @@ -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 = (props: RootElementProps) => { const { guildsManager } = props; + const [ overlay, setOverlay ] = useState(null); + return (
- + +
{overlay}
); } diff --git a/src/client/webapp/elements/sections/connection-info.tsx b/src/client/webapp/elements/sections/connection-info.tsx index 193a121..611e6cd 100644 --- a/src/client/webapp/elements/sections/connection-info.tsx +++ b/src/client/webapp/elements/sections/connection-info.tsx @@ -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>; } const ConnectionInfo: FC = (props: ConnectionInfoProps) => { - const { guild, selfMember } = props; + const { guild, selfMember, setOverlay } = props; const rootRef = useRef(null); @@ -37,7 +38,8 @@ const ConnectionInfo: FC = (props: ConnectionInfoProps) => if (!selfMember) return null; return ( ); }, [ guild, selfMember, rootRef ]); diff --git a/src/client/webapp/elements/sections/guild-list-container.tsx b/src/client/webapp/elements/sections/guild-list-container.tsx index 1230224..8930752 100644 --- a/src/client/webapp/elements/sections/guild-list-container.tsx +++ b/src/client/webapp/elements/sections/guild-list-container.tsx @@ -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>; + setOverlay: Dispatch>; } const GuildListContainer: FC = (props: GuildListContainerProps) => { - const { guildsManager, guilds, activeGuild, setActiveGuild } = props; + const { guildsManager, guilds, activeGuild, setActiveGuild, setOverlay } = props; const addGuildRef = useRef(null); @@ -66,9 +66,9 @@ const GuildListContainer: FC = (props: GuildListContain typeof addGuildData?.iconSrc !== 'string' ) { LOG.debug('bad guild data:', { addGuildData, fileText }); - ElementsUtil.presentReactOverlay(document, ); + setOverlay( setOverlay(null)} />); } else { - ElementsUtil.presentReactOverlay(document, ); + setOverlay( setOverlay(null)} />); } }, [ guildsManager ]); diff --git a/src/client/webapp/elements/sections/guild-title.tsx b/src/client/webapp/elements/sections/guild-title.tsx index c54e1fa..cb24f1f 100644 --- a/src/client/webapp/elements/sections/guild-title.tsx +++ b/src/client/webapp/elements/sections/guild-title.tsx @@ -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>; } const GuildTitle: FC = (props: GuildTitleProps) => { - const { guild, guildMeta, selfMember } = props; + const { guild, guildMeta, selfMember, setOverlay } = props; const rootRef = useRef(null); @@ -32,6 +33,7 @@ const GuildTitle: FC = (props: GuildTitleProps) => { ); }, [ guild, guildMeta, selfMember, rootRef ]); diff --git a/src/client/webapp/elements/sections/guild.tsx b/src/client/webapp/elements/sections/guild.tsx index 65b5ec4..43a817f 100644 --- a/src/client/webapp/elements/sections/guild.tsx +++ b/src/client/webapp/elements/sections/guild.tsx @@ -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>; } const GuildElement: FC = (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 = (props: GuildElementProps) => { return (
- + - + activeChannel={activeChannel} setActiveChannel={setActiveChannel} + setOverlay={setOverlay} + /> +
- {activeChannel && } + {activeChannel && } {activeChannel && }
diff --git a/src/client/webapp/elements/sections/guilds-manager.tsx b/src/client/webapp/elements/sections/guilds-manager.tsx index 68895bb..17571df 100644 --- a/src/client/webapp/elements/sections/guilds-manager.tsx +++ b/src/client/webapp/elements/sections/guilds-manager.tsx @@ -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>; } const GuildsManagerElement: FC = (props: GuildsManagerElementProps) => { - const { guildsManager } = props; + const { guildsManager, setOverlay } = props; const [ guilds ] = useGuildListSubscription(guildsManager); const [ activeGuild, setActiveGuild ] = useState(null); @@ -28,8 +29,10 @@ const GuildsManagerElement: FC = (props: GuildsManage
- {activeGuild && } + activeGuild={activeGuild} setActiveGuild={setActiveGuild} + setOverlay={setOverlay} + /> + {activeGuild && }
); } diff --git a/src/client/webapp/styles/channel-list.scss b/src/client/webapp/styles/channel-list.scss index 9ee39bc..6b17071 100644 --- a/src/client/webapp/styles/channel-list.scss +++ b/src/client/webapp/styles/channel-list.scss @@ -48,4 +48,8 @@ margin-left: 6px; } } + + .channel:hover .modify { + display: unset; + } } diff --git a/src/client/webapp/styles/guild-list.scss b/src/client/webapp/styles/guild-list.scss index 57266f9..8bca549 100644 --- a/src/client/webapp/styles/guild-list.scss +++ b/src/client/webapp/styles/guild-list.scss @@ -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; + } } diff --git a/src/client/webapp/styles/overlays.scss b/src/client/webapp/styles/overlays.scss index a022f59..f52bb08 100644 --- a/src/client/webapp/styles/overlays.scss +++ b/src/client/webapp/styles/overlays.scss @@ -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;