react guild title
This commit is contained in:
parent
80c2a352da
commit
7fde5e70cf
@ -36,7 +36,7 @@ export default function createChannel(document: Document, q: Q, ui: UI, guild: C
|
||||
if (modifyContextElement.parentElement) {
|
||||
modifyContextElement.parentElement.removeChild(modifyContextElement);
|
||||
}
|
||||
ElementsUtil.presentReactOverlay(document, <ChannelOverlay document={document} guild={guild} channel={channel} />);
|
||||
ElementsUtil.presentReactOverlay(document, <ChannelOverlay guild={guild} channel={channel} />);
|
||||
});
|
||||
|
||||
q.$$$(element, '.modify').addEventListener('mouseenter', () => {
|
||||
|
@ -1,66 +0,0 @@
|
||||
import * as electronRemote from '@electron/remote';
|
||||
const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||
import Logger from '../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import ElementsUtil from './require/elements-util.js';
|
||||
import BaseElements from './require/base-elements.js';
|
||||
import Q from '../q-module';
|
||||
import UI from '../ui';
|
||||
|
||||
import CombinedGuild from '../guild-combined';
|
||||
|
||||
import React from 'react';
|
||||
import ReactHelper from './require/react-helper';
|
||||
import GuildSettingsOverlay from './overlays/overlay-guild-settings';
|
||||
import ChannelOverlay from './overlays/overlay-channel';
|
||||
|
||||
export default function createGuildTitleContextMenu(document: Document, q: Q, ui: UI, guild: CombinedGuild): Element {
|
||||
if (ui.activeConnection === null) {
|
||||
LOG.warn('no active connection when creating guild title context menu');
|
||||
return ReactHelper.createElementFromJSX(<div></div>);
|
||||
}
|
||||
|
||||
const menuItems: JSX.Element[] = [];
|
||||
|
||||
if (ui.activeConnection.privileges.includes('modify_profile')) {
|
||||
menuItems.push(
|
||||
<div key="guild-settings" className="item guild-settings">
|
||||
<div className="icon">{BaseElements.COG}</div>
|
||||
<div>Guild Settings</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (ui.activeConnection.privileges.includes('modify_channels')) {
|
||||
if (ui.activeConnection.privileges.includes('modify_profile')) {
|
||||
menuItems.push(<div key="spacer-1" className="item-spacer"></div>);
|
||||
}
|
||||
menuItems.push(
|
||||
<div key="create-channel" className="item create-channel">
|
||||
<div className="icon">{BaseElements.CREATE}</div>
|
||||
<div>Create Channel</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const element = BaseElements.createContextMenu(document, (
|
||||
<div className="guild-title-context">{menuItems}</div>
|
||||
));
|
||||
|
||||
if (ui.activeConnection.privileges.includes('modify_profile')) {
|
||||
q.$$$(element, '.item.guild-settings').addEventListener('click', async () => {
|
||||
element.removeSelf();
|
||||
ElementsUtil.presentReactOverlay(document, <GuildSettingsOverlay guild={guild} />);
|
||||
});
|
||||
}
|
||||
|
||||
if (ui.activeConnection.privileges.includes('modify_channels')) {
|
||||
q.$$$(element, '.item.create-channel').addEventListener('click', () => {
|
||||
element.removeSelf();
|
||||
ElementsUtil.presentReactOverlay(document, <ChannelOverlay document={document} guild={guild} />);
|
||||
});
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
@ -5,14 +5,14 @@ import PersonalizeOverlay from '../overlays/overlay-personalize';
|
||||
import ElementsUtil from '../require/elements-util';
|
||||
import ContextMenu from './components/context-menu';
|
||||
|
||||
export interface ConnectionInfoContextProps {
|
||||
export interface ConnectionInfoContextMenuProps {
|
||||
guild: CombinedGuild;
|
||||
selfMember: Member;
|
||||
relativeToRef: RefObject<HTMLElement | null>;
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
const ConnectionInfoContext: FC<ConnectionInfoContextProps> = (props: ConnectionInfoContextProps) => {
|
||||
const ConnectionInfoContextMenu: FC<ConnectionInfoContextMenuProps> = (props: ConnectionInfoContextMenuProps) => {
|
||||
const { guild, selfMember, relativeToRef, close } = props;
|
||||
|
||||
const setSelfStatus = useCallback(async (status: string) => {
|
||||
@ -56,4 +56,4 @@ const ConnectionInfoContext: FC<ConnectionInfoContextProps> = (props: Connection
|
||||
);
|
||||
}
|
||||
|
||||
export default ConnectionInfoContext;
|
||||
export default ConnectionInfoContextMenu;
|
@ -0,0 +1,64 @@
|
||||
import React, { FC, RefObject, 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, { IAlignment } from '../require/elements-util';
|
||||
import ContextMenu from './components/context-menu';
|
||||
|
||||
export interface GuildTitleContextMenuProps {
|
||||
alignment: IAlignment;
|
||||
close: () => void;
|
||||
relativeToRef: RefObject<HTMLElement>
|
||||
guild: CombinedGuild;
|
||||
guildMeta: GuildMetadata;
|
||||
selfMember: Member;
|
||||
}
|
||||
|
||||
const GuildTitleContextMenu: FC<GuildTitleContextMenuProps> = (props: GuildTitleContextMenuProps) => {
|
||||
const { alignment, close, relativeToRef, guild, guildMeta, selfMember } = props;
|
||||
|
||||
const openGuildSettings = useCallback(() => {
|
||||
close();
|
||||
ElementsUtil.presentReactOverlay(document, <GuildSettingsOverlay guild={guild} guildMeta={guildMeta} />);
|
||||
}, [ guild, guildMeta, close ]);
|
||||
|
||||
const openCreateChannel = useCallback(() => {
|
||||
close();
|
||||
ElementsUtil.presentReactOverlay(document, <ChannelOverlay guild={guild} />);
|
||||
}, [ guild, close ]);
|
||||
|
||||
const guildSettingsElement = useMemo(() => {
|
||||
if (!selfMember.privileges.includes('modify_profile')) return null;
|
||||
return (
|
||||
<div className="item guild-settings" onClick={openGuildSettings}>
|
||||
<div className="icon">{BaseElements.COG}</div>
|
||||
<div>Guild Settings</div>
|
||||
</div>
|
||||
);
|
||||
}, [ selfMember, openGuildSettings ]);
|
||||
|
||||
const createChannelElement = useMemo(() => {
|
||||
if (!selfMember.privileges.includes('modify_channels')) return null;
|
||||
return (
|
||||
<div className="item create-channel" onClick={openCreateChannel}>
|
||||
<div className="icon">{BaseElements.CREATE}</div>
|
||||
<div>Create Channel</div>
|
||||
</div>
|
||||
);
|
||||
}, [ selfMember, openCreateChannel ]);
|
||||
|
||||
if (guildSettingsElement === null && createChannelElement === null) return null;
|
||||
|
||||
return (
|
||||
<ContextMenu alignment={alignment} close={close} relativeToRef={relativeToRef}>
|
||||
<div className="guild-title-context-menu">
|
||||
{guildSettingsElement}
|
||||
{createChannelElement}
|
||||
</div>
|
||||
</ContextMenu>
|
||||
);
|
||||
}
|
||||
|
||||
export default GuildTitleContextMenu;
|
@ -1,20 +0,0 @@
|
||||
import Q from '../q-module';
|
||||
import UI from '../ui';
|
||||
import createGuildTitleContextMenu from './context-menu-guild-title';
|
||||
import ElementsUtil from './require/elements-util';
|
||||
|
||||
export default function bindAddGuildTitleEvents(document: Document, q: Q, ui: UI) {
|
||||
q.$('#guild-name-container').addEventListener('click', () => {
|
||||
if (ui.activeConnection === null) return;
|
||||
if (ui.activeGuild === null) return;
|
||||
if (!ui.activeGuild.isSocketVerified()) return;
|
||||
if (
|
||||
!ui.activeConnection.privileges.includes('modify_profile') &&
|
||||
!ui.activeConnection.privileges.includes('modify_members')
|
||||
) return;
|
||||
|
||||
const contextMenu = createGuildTitleContextMenu(document, q, ui, ui.activeGuild) as HTMLElement;
|
||||
document.body.appendChild(contextMenu);
|
||||
ElementsUtil.alignContextElement(contextMenu, q.$('#guild-name-container'), { top: 'bottom', centerX: 'centerX' });
|
||||
});
|
||||
}
|
@ -7,6 +7,7 @@ import MemberList from "./lists/member-list";
|
||||
import MessageList from './lists/message-list';
|
||||
import ChannelTitle from './sections/channel-title';
|
||||
import ConnectionInfo from './sections/connection-info';
|
||||
import GuildTitle from './sections/guild-title';
|
||||
|
||||
export function mountBaseComponents() {
|
||||
// guild-list
|
||||
@ -14,6 +15,10 @@ export function mountBaseComponents() {
|
||||
}
|
||||
|
||||
export function mountGuildComponents(q: Q, guild: CombinedGuild) {
|
||||
// guild title
|
||||
ElementsUtil.unmountReactComponent(q.$('.guild-title-anchor'));
|
||||
ElementsUtil.mountReactComponent(q.$('.guild-title-anchor'), <GuildTitle guild={guild} />);
|
||||
|
||||
// connection info
|
||||
ElementsUtil.unmountReactComponent(q.$('.connection-anchor'));
|
||||
ElementsUtil.mountReactComponent(q.$('.connection-anchor'), <ConnectionInfo guild={guild} />);
|
||||
|
@ -15,12 +15,11 @@ import ReactHelper from '../require/react-helper';
|
||||
import Button from '../components/button';
|
||||
|
||||
export interface ChannelOverlayProps {
|
||||
document: Document;
|
||||
guild: CombinedGuild;
|
||||
channel?: Channel;
|
||||
}
|
||||
const ChannelOverlay: FC<ChannelOverlayProps> = (props: ChannelOverlayProps) => {
|
||||
const { document, guild, channel } = props;
|
||||
const { guild, channel } = props;
|
||||
|
||||
const nameInputRef = createRef<HTMLInputElement>();
|
||||
|
||||
|
@ -3,20 +3,19 @@ const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||
import Logger from '../../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import React, { FC, useEffect, useMemo, useState } from "react";
|
||||
import React, { FC, useEffect, useState } from "react";
|
||||
import CombinedGuild from "../../guild-combined";
|
||||
import ChoicesControl from "../components/control-choices";
|
||||
import GuildInvitesDisplay from "../displays/display-guild-invites";
|
||||
import GuildOverviewDisplay from "../displays/display-guild-overview";
|
||||
import GuildSubscriptions from "../require/guild-subscriptions";
|
||||
import { GuildMetadata } from '../../data-types';
|
||||
|
||||
export interface GuildSettingsOverlayProps {
|
||||
guild: CombinedGuild;
|
||||
guildMeta: GuildMetadata;
|
||||
}
|
||||
const GuildSettingsOverlay: FC<GuildSettingsOverlayProps> = (props: GuildSettingsOverlayProps) => {
|
||||
const { guild } = props;
|
||||
|
||||
const [ guildMeta, guildMetaError ] = GuildSubscriptions.useGuildMetadataSubscription(guild);
|
||||
const { guild, guildMeta } = props;
|
||||
|
||||
const [ selectedId, setSelectedId ] = useState<string>('overview');
|
||||
const [ display, setDisplay ] = useState<JSX.Element>();
|
||||
@ -27,13 +26,9 @@ const GuildSettingsOverlay: FC<GuildSettingsOverlayProps> = (props: GuildSetting
|
||||
if (selectedId === 'invites' ) setDisplay(<GuildInvitesDisplay guild={guild} />);
|
||||
}, [ selectedId ]);
|
||||
|
||||
const guildNameText = useMemo(() => {
|
||||
return guildMetaError ? 'metadata error' : guildMeta?.name ?? 'loading...';
|
||||
}, [ guildMeta, guildMetaError ])
|
||||
|
||||
return (
|
||||
<div className="content display-swapper guild-settings">
|
||||
<ChoicesControl title={guildNameText} selectedId={selectedId} setSelectedId={setSelectedId} choices={[
|
||||
<ChoicesControl title={guildMeta.name} selectedId={selectedId} setSelectedId={setSelectedId} choices={[
|
||||
{ id: 'overview', display: 'Overview' },
|
||||
{ id: 'roles', display: 'Roles' },
|
||||
{ id: 'invites', display: 'Invites' },
|
||||
|
@ -6,9 +6,9 @@ const LOG = Logger.create(__filename, electronConsole);
|
||||
import React, { FC, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { Member } from '../../data-types';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import ConnectionInfoContext from '../context-menus/context-connection-info';
|
||||
import MemberElement, { DummyMember } from '../lists/components/member-element';
|
||||
import GuildSubscriptions from '../require/guild-subscriptions';
|
||||
import ConnectionInfoContextMenu from '../context-menus/context-menu-connection-info';
|
||||
|
||||
export interface ConnectionInfoProps {
|
||||
guild: CombinedGuild;
|
||||
@ -19,8 +19,6 @@ const ConnectionInfo: FC<ConnectionInfoProps> = (props: ConnectionInfoProps) =>
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// TODO: Respond to and emit global context menu events to prevent multiple
|
||||
// context menus from being open at once. (maybe this isn't very reacty though)
|
||||
const [ contextMenuOpen, setContextMenuOpen ] = useState<boolean>(false);
|
||||
|
||||
const [ selfMember ] = GuildSubscriptions.useSelfMemberSubscription(guild);
|
||||
@ -40,7 +38,7 @@ const ConnectionInfo: FC<ConnectionInfoProps> = (props: ConnectionInfoProps) =>
|
||||
|
||||
const contextMenu = useMemo(() => {
|
||||
if (!selfMember) return null;
|
||||
return <ConnectionInfoContext guild={guild} selfMember={selfMember} relativeToRef={rootRef} close={() => { console.log('close'); setContextMenuOpen(false); }} />
|
||||
return <ConnectionInfoContextMenu guild={guild} selfMember={selfMember} relativeToRef={rootRef} close={() => { setContextMenuOpen(false); }} />
|
||||
}, [ guild, selfMember, rootRef ]);
|
||||
|
||||
const toggleContextMenu = useCallback(() => {
|
||||
|
50
src/client/webapp/elements/sections/guild-title.tsx
Normal file
50
src/client/webapp/elements/sections/guild-title.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import React, { FC, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import GuildTitleContextMenu from '../context-menus/context-menu-guild-title';
|
||||
import GuildSubscriptions from '../require/guild-subscriptions';
|
||||
|
||||
export interface GuildTitleProps {
|
||||
guild: CombinedGuild;
|
||||
}
|
||||
|
||||
const GuildTitle: FC<GuildTitleProps> = (props: GuildTitleProps) => {
|
||||
const { guild } = props;
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [ contextMenuOpen, setContextMenuOpen ] = useState<boolean>(false);
|
||||
|
||||
// TODO: Handle fetch error
|
||||
const [ guildMeta, fetchError ] = GuildSubscriptions.useGuildMetadataSubscription(guild);
|
||||
const [ selfMember ] = GuildSubscriptions.useSelfMemberSubscription(guild);
|
||||
|
||||
const alignment = useMemo(() => {
|
||||
return { top: 'bottom', centerX: 'centerX' }
|
||||
}, []);
|
||||
|
||||
const contextMenu = useMemo(() => {
|
||||
if (!guildMeta) return null;
|
||||
if (!selfMember) return null;
|
||||
return (
|
||||
<GuildTitleContextMenu
|
||||
alignment={alignment} relativeToRef={rootRef} close={() => { setContextMenuOpen(false); }}
|
||||
guild={guild} guildMeta={guildMeta} selfMember={selfMember}
|
||||
/>
|
||||
);
|
||||
}, [ guild, guildMeta, selfMember, rootRef ]);
|
||||
|
||||
const toggleContextMenu = useCallback(() => {
|
||||
setContextMenuOpen(oldContextMenuOpen => !!contextMenu && !oldContextMenuOpen);
|
||||
}, [ contextMenu ]);
|
||||
|
||||
return (
|
||||
<div className="guild-title" ref={rootRef}>
|
||||
<div className="guild-name-container" onClick={toggleContextMenu}>
|
||||
<span className="guild-name">{guildMeta?.name ?? null}</span>
|
||||
</div>
|
||||
{contextMenuOpen ? contextMenu : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GuildTitle;
|
@ -44,7 +44,7 @@
|
||||
</div>
|
||||
<div id="guild">
|
||||
<div id="guild-sidebar">
|
||||
<div id="guild-name-container"><span id="guild-name"></span></div>
|
||||
<div class="guild-title-anchor"></div>
|
||||
<div id="channel-list"></div>
|
||||
<div class="connection-anchor"></div>
|
||||
</div>
|
||||
|
@ -18,7 +18,6 @@ import { Changes, Channel, ConnectionInfo, GuildMetadata, Member, Resource, Toke
|
||||
import Q from './q-module';
|
||||
import bindWindowButtonEvents from './elements/events-window-buttons';
|
||||
import bindTextInputEvents from './elements/events-text-input';
|
||||
import bindAddGuildTitleEvents from './elements/events-guild-title';
|
||||
import bindAddGuildEvents from './elements/events-add-guild';
|
||||
import PersonalDB from './personal-db';
|
||||
import MessageRAMCache from './message-ram-cache';
|
||||
@ -73,7 +72,6 @@ window.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
bindWindowButtonEvents(q);
|
||||
bindTextInputEvents(document, q, ui);
|
||||
bindAddGuildTitleEvents(document, q, ui);
|
||||
bindAddGuildEvents(document, q, ui, guildsManager);
|
||||
|
||||
LOG.silly('events bound');
|
||||
|
@ -53,8 +53,8 @@
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.guild-title-context .item .icon img,
|
||||
.guild-title-context .item .icon svg {
|
||||
.guild-title-context-menu .item .icon img,
|
||||
.guild-title-context-menu .item .icon svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
@ -14,25 +14,25 @@
|
||||
border-top-left-radius: 8px;
|
||||
}
|
||||
|
||||
#guild-name-container {
|
||||
// TODO: just do this with inline styles
|
||||
.privilege-modify_profile .guild-name-container,
|
||||
.privilege-modify_members .guild-name-container {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.guild-name-container {
|
||||
padding: 0 16px;
|
||||
height: 48px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
color: $header-primary;
|
||||
border-bottom: 1px solid $background-secondary-alt;
|
||||
}
|
||||
|
||||
#guild-name {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#guild.privilege-modify_profile #guild-name,
|
||||
#guild.privilege-modify_members #guild-name {
|
||||
cursor: pointer;
|
||||
.guild-name {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
@ -180,7 +180,6 @@ export default class UI {
|
||||
|
||||
public async updateGuildName(guild: CombinedGuild, name: string): Promise<void>{
|
||||
await this.lockGuildName(guild, () => {
|
||||
this.q.$('#guild-name').innerText = name;
|
||||
const baseElement = this.q.$('#guild-list .guild[data-id="' + guild.id + '"]');
|
||||
baseElement.setAttribute('meta-name', name);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user