Skip to content

Commit

Permalink
Merge branch 'room-ui-redesign' of github.com:mozilla/hubs into room-…
Browse files Browse the repository at this point in the history
…ui-redesign
  • Loading branch information
robertlong committed Oct 15, 2020
2 parents 88479cc + 2db2d6c commit f0786cf
Show file tree
Hide file tree
Showing 26 changed files with 989 additions and 589 deletions.
14 changes: 0 additions & 14 deletions src/react-components/avatar/AvatarPreviewCanvas.js

This file was deleted.

9 changes: 0 additions & 9 deletions src/react-components/avatar/AvatarPreviewCanvas.scss

This file was deleted.

12 changes: 5 additions & 7 deletions src/react-components/chat-message.js
Expand Up @@ -5,7 +5,6 @@ import styles from "../assets/stylesheets/presence-log.scss";
import classNames from "classnames";
import { toArray as toEmojis } from "react-emoji-render";
import serializeElement from "../utils/serialize-element";
import { navigateToClientInfo } from "./presence-list";
import { coerceToUrl } from "../utils/media-utils";
import { formatMessageBody } from "../utils/chat-message";

Expand All @@ -16,16 +15,16 @@ const textureLoader = new THREE.TextureLoader();

const CHAT_MESSAGE_TEXTURE_SIZE = 1024;

const messageBodyDom = (body, from, fromSessionId, includeFromLink, history) => {
const messageBodyDom = (body, from, fromSessionId, onViewProfile) => {
const { formattedBody, multiline, monospace } = formatMessageBody(body);
const wrapStyle = multiline ? styles.messageWrapMulti : styles.messageWrap;
const messageBodyClasses = {
[styles.messageBody]: true,
[styles.messageBodyMulti]: multiline,
[styles.messageBodyMono]: monospace
};
const includeClientLink = includeFromLink && fromSessionId && history && NAF.clientId !== fromSessionId;
const onFromClick = includeClientLink ? () => navigateToClientInfo(history, fromSessionId) : () => {};
const includeClientLink = onViewProfile && fromSessionId && history && NAF.clientId !== fromSessionId;
const onFromClick = includeClientLink ? () => onViewProfile(fromSessionId) : () => {};

return (
<div className={wrapStyle}>
Expand Down Expand Up @@ -198,7 +197,7 @@ export default function ChatMessage(props) {
onClick={() => spawnChatMessage(props.body)}
/>
)}
<p>{messageBodyDom(props.body, props.name, props.sessionId, props.includeFromLink, props.history)}</p>
<p>{messageBodyDom(props.body, props.name, props.sessionId, props.onViewProfile)}</p>
</div>
);
}
Expand All @@ -208,7 +207,6 @@ ChatMessage.propTypes = {
maySpawn: PropTypes.bool,
body: PropTypes.string,
sessionId: PropTypes.string,
history: PropTypes.object,
className: PropTypes.string,
includeFromLink: PropTypes.bool
onViewProfile: PropTypes.func
};
158 changes: 53 additions & 105 deletions src/react-components/client-info-dialog.js
@@ -1,28 +1,16 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import DialogContainer from "./dialog-container.js";
import PromoteClientDialog from "./promote-client-dialog.js";
import styles from "../assets/stylesheets/client-info-dialog.scss";
import { FormattedMessage } from "react-intl";
import { sluglessPath } from "../utils/history";
import { getAvatarThumbnailUrl } from "../utils/avatar-utils";

export function getClientInfoClientId(location) {
const { search } = location;
const urlParams = new URLSearchParams(search);
const pathname = sluglessPath(location);

if (!pathname.startsWith("/client") && !urlParams.get("client_id")) return null;
return urlParams.get("client_id") || pathname.substring(9);
}
import { UserProfileSidebar } from "./room/UserProfileSidebar.js";

export default class ClientInfoDialog extends Component {
static propTypes = {
clientId: PropTypes.string,
history: PropTypes.object,
user: PropTypes.object.isRequired,
hubChannel: PropTypes.object,
presences: PropTypes.object,
performConditionalSignIn: PropTypes.func,
showBackButton: PropTypes.bool,
onBack: PropTypes.func,
onClose: PropTypes.func,
showNonHistoriedDialog: PropTypes.func
};
Expand All @@ -32,157 +20,117 @@ export default class ClientInfoDialog extends Component {
};

kick() {
const { clientId, performConditionalSignIn, hubChannel, onClose } = this.props;
const { user, performConditionalSignIn, hubChannel, onClose, onBack } = this.props;

performConditionalSignIn(
() => hubChannel.can("kick_users"),
async () => await hubChannel.kick(clientId),
async () => await hubChannel.kick(user.id),
"kick-user"
);

onClose();
if (onClose) {
onClose();
} else if (onBack) {
onBack();
}
}

hide() {
const { clientId, onClose, hubChannel } = this.props;
hubChannel.hide(clientId);
onClose();
const { user, hubChannel } = this.props;
hubChannel.hide(user.id);

this.forceUpdate();
}

mute() {
const { clientId, performConditionalSignIn, hubChannel, onClose } = this.props;
const { user, performConditionalSignIn, hubChannel } = this.props;

performConditionalSignIn(
() => hubChannel.can("mute_users"),
async () => await hubChannel.mute(clientId),
async () => await hubChannel.mute(user.id),
"mute-user"
);

onClose();
this.forceUpdate();
}

addOwner() {
const { clientId, performConditionalSignIn, hubChannel, onClose } = this.props;
const { profile } = this.getPresenceEntry();
const { user, performConditionalSignIn, hubChannel } = this.props;
const { profile } = this.props.user;

performConditionalSignIn(
() => hubChannel.can("update_roles"),
async () => {
onClose();

this.props.showNonHistoriedDialog(PromoteClientDialog, {
displayName: profile.displayName,
onConfirm: () => hubChannel.addOwner(clientId)
onConfirm: () => hubChannel.addOwner(user.id)
});
},
"add-owner"
);

this.forceUpdate();
}

removeOwner() {
const { clientId, performConditionalSignIn, hubChannel, onClose } = this.props;
const { user, performConditionalSignIn, hubChannel } = this.props;

performConditionalSignIn(
() => hubChannel.can("update_roles"),
async () => await hubChannel.removeOwner(clientId),
async () => await hubChannel.removeOwner(user.id),
"remove-owner"
);

onClose();
this.forceUpdate();
}

unhide() {
const { clientId, hubChannel, onClose } = this.props;
hubChannel.unhide(clientId);
onClose();
}

getPresenceEntry() {
if (!this.props.presences) return null;

const presence = Object.entries(this.props.presences).find(([k]) => k === this.props.clientId);
if (!presence) return { profile: {}, roles: {} };

const metas = presence[1].metas;
return metas[metas.length - 1];
const { user, hubChannel } = this.props;
hubChannel.unhide(user.id);
this.forceUpdate();
}

componentDidMount() {
const { profile } = this.getPresenceEntry();
const { profile } = this.props.user;
if (profile.avatarId) {
getAvatarThumbnailUrl(profile.avatarId).then(avatarThumbnailUrl => this.setState({ avatarThumbnailUrl }));
}
}

render() {
const { profile, roles } = this.getPresenceEntry();
const { profile, roles } = this.props.user;

const { displayName, identityName } = profile;
const { hubChannel, clientId, onClose } = this.props;
const title = (
<div className={styles.title}>
{displayName}
<div className={styles.identityName}>{identityName}</div>
</div>
);
const { hubChannel, user, showBackButton, onClose, onBack } = this.props;
const mayKick = hubChannel.canOrWillIfCreator("kick_users");
const mayMute = hubChannel.canOrWillIfCreator("mute_users");
const mayMute = user.micPresence && !user.micPresence.muted && hubChannel.canOrWillIfCreator("mute_users");
const targetIsOwner = !!roles.owner;
const targetIsCreator = !!roles.creator;
const targetIsSignedIn = !!roles.signed_in;
const mayAddOwner = hubChannel.canOrWillIfCreator("update_roles") && !targetIsOwner && !targetIsCreator;
const mayRemoveOwner = hubChannel.canOrWillIfCreator("update_roles") && targetIsOwner && !targetIsCreator;
const isHidden = hubChannel.isHidden(clientId);
const isHidden = hubChannel.isHidden(user.id);

return (
<DialogContainer className={styles.clientInfoDialog} title={title} wide={true} {...this.props}>
<div className={styles.roomInfo}>
<div className={styles.clientProfileImage}>
<img src={this.state.avatarThumbnailUrl} />
</div>
<div className={styles.primaryActionButtons}>
{mayAddOwner && (
<button
onClick={() => this.addOwner()}
disabled={!targetIsSignedIn}
title={targetIsSignedIn ? "Promote" : `${profile.displayName} is signed out.`}
>
<img className={styles.buttonIcon} src="../assets/images/add-owner.png" />
<FormattedMessage id="client-info.add-owner" />
</button>
)}
{mayRemoveOwner && (
<button onClick={() => this.removeOwner()}>
<img className={styles.buttonIcon} src="../assets/images/remove-owner.png" />
<FormattedMessage id="client-info.remove-owner" />
</button>
)}
{!isHidden && (
<button onClick={() => this.hide()}>
<FormattedMessage id="client-info.hide-button" />
</button>
)}
{isHidden && (
<button onClick={() => this.unhide()}>
<FormattedMessage id="client-info.unhide-button" />
</button>
)}
{mayMute && (
<button onClick={() => this.mute()}>
<FormattedMessage id="client-info.mute-button" />
</button>
)}
{mayKick && (
<button onClick={() => this.kick()}>
<FormattedMessage id="client-info.kick-button" />
</button>
)}
<button className={styles.cancel} onClick={onClose}>
<FormattedMessage id="client-info.cancel" />
</button>
</div>
</div>
</DialogContainer>
<UserProfileSidebar
displayName={displayName}
identityName={identityName}
avatarPreview={<img src={this.state.avatarThumbnailUrl} />}
isSignedIn={targetIsSignedIn}
canPromote={mayAddOwner}
onPromote={() => this.addOwner()}
canDemote={mayRemoveOwner}
onDemote={() => this.removeOwner()}
isHidden={isHidden}
onToggleHidden={() => (isHidden ? this.unhide() : this.hide())}
canMute={mayMute}
onMute={() => this.mute()}
canKick={mayKick}
onKick={() => this.kick()}
showBackButton={showBackButton}
onClose={onClose}
onBack={onBack}
/>
);
}
}
45 changes: 45 additions & 0 deletions src/react-components/layout/List.js
@@ -0,0 +1,45 @@
import React from "react";
import classNames from "classnames";
import PropTypes from "prop-types";
import styles from "./List.scss";

export function List({ className, children, ...rest }) {
return (
<ul {...rest} className={classNames(styles.list, className)}>
{children}
</ul>
);
}

List.propTypes = {
className: PropTypes.string,
children: PropTypes.node
};

export function ListItem({ className, children, ...rest }) {
return (
<li {...rest} className={classNames(styles.listItem, styles.listItemContent, className)}>
{children}
</li>
);
}

ListItem.propTypes = {
className: PropTypes.string,
children: PropTypes.node
};

export function ButtonListItem({ className, children, ...rest }) {
return (
<li className={styles.listItem}>
<button {...rest} className={classNames(styles.listItemContent, styles.buttonListItem, className)}>
{children}
</button>
</li>
);
}

ButtonListItem.propTypes = {
className: PropTypes.string,
children: PropTypes.node
};

0 comments on commit f0786cf

Please sign in to comment.