Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/room ownership #833

Closed
wants to merge 13 commits into from
13 changes: 13 additions & 0 deletions src/assets/stylesheets/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,19 @@ body {
}
}
}

:local(.sign-in) {
flex: 3;
display: flex;
align-items: center;
justify-content: end;

a {
text-decoration: underline;
font-weight: bold;
cursor: pointer;
}
}
}

:local(.video-container) {
Expand Down
12 changes: 5 additions & 7 deletions src/hub.js
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,10 @@ document.addEventListener("DOMContentLoaded", async () => {

const pushSubscriptionEndpoint = await subscriptions.getCurrentEndpoint();
const joinPayload = { profile: store.state.profile, push_subscription_endpoint: pushSubscriptionEndpoint, context };
const { token } = store.state.credentials;
if (token) {
joinPayload.token = token;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

notes from other PR, but these should prob be auth_token vs perm_token

}
const hubPhxChannel = socket.channel(`hub:${hubId}`, joinPayload);

const presenceLogEntries = [];
Expand Down Expand Up @@ -559,15 +563,9 @@ document.addEventListener("DOMContentLoaded", async () => {
.join()
.receive("ok", async data => {
hubChannel.setPhoenixChannel(hubPhxChannel);

const { token } = store.state.credentials;
if (token) {
await hubChannel.signIn(token);
}

subscriptions.setHubChannel(hubChannel);
subscriptions.setSubscribed(data.subscriptions.web_push);
remountUI({ initialIsSubscribed: subscriptions.isSubscribed() });
remountUI({ initialIsSubscribed: subscriptions.isSubscribed(), isOwner: data.is_owner });
await handleHubChannelJoined(entryManager, hubChannel, messageDispatch, data);
})
.receive("error", res => {
Expand Down
10 changes: 9 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import ReactDOM from "react-dom";
import "./assets/stylesheets/index.scss";
import registerTelemetry from "./telemetry";
import HomeRoot from "./react-components/home-root";
import { createAndRedirectToNewHub } from "./utils/phoenix-utils";
import AuthChannel from "./utils/auth-channel";
import { createAndRedirectToNewHub, connectToReticulum } from "./utils/phoenix-utils";
import Store from "./storage/store";

const qs = new URLSearchParams(location.search);
registerTelemetry();
Expand All @@ -23,10 +25,16 @@ const sceneId = qs.get("scene_id") || (pathname.startsWith("/scenes/") && pathna
return;
}

const store = new Store();
const authChannel = new AuthChannel(store);
authChannel.setSocket(connectToReticulum());

const root = (
<HomeRoot
initialEnvironment={qs.get("initial_environment")}
sceneId={sceneId || ""}
store={store}
authChannel={authChannel}
authVerify={qs.has("auth_topic")}
authTopic={qs.get("auth_topic")}
authToken={qs.get("auth_token")}
Expand Down
93 changes: 66 additions & 27 deletions src/react-components/home-root.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import mozLogo from "../assets/images/moz-logo-black.png";
import classNames from "classnames";
import { ENVIRONMENT_URLS } from "../assets/environments/environments";
import { connectToReticulum } from "../utils/phoenix-utils";
import maskEmail from "../utils/mask-email";

import styles from "../assets/stylesheets/index.scss";

import HubCreatePanel from "./hub-create-panel.js";
import AuthDialog from "./auth-dialog.js";
import ReportDialog from "./report-dialog.js";
import JoinUsDialog from "./join-us-dialog.js";
import ReportDialog from "./report-dialog.js";
import SignInDialog from "./sign-in-dialog.js";
import UpdatesDialog from "./updates-dialog.js";
import DialogContainer from "./dialog-container.js";
import { WithHoverSound } from "./wrap-with-audio";
Expand All @@ -29,6 +31,8 @@ class HomeRoot extends Component {
static propTypes = {
intl: PropTypes.object,
sceneId: PropTypes.string,
store: PropTypes.object,
authChannel: PropTypes.object,
authVerify: PropTypes.bool,
authTopic: PropTypes.string,
authToken: PropTypes.string,
Expand All @@ -41,12 +45,18 @@ class HomeRoot extends Component {
state = {
environments: [],
dialog: null,
signedIn: null,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, maybe make this authenticated or signedIn everywhere, not both

mailingListEmail: "",
mailingListPrivacy: false
};

constructor(props) {
super(props);
this.state.signedIn = props.authChannel.authenticated;
this.state.email = props.authChannel.email;
}

componentDidMount() {
this.closeDialog = this.closeDialog.bind(this);
if (this.props.authVerify) {
this.showAuthDialog(true);
this.verifyAuth().then(this.showAuthDialog);
Expand Down Expand Up @@ -77,8 +87,14 @@ class HomeRoot extends Component {
channel.push("auth_verified", { token: this.props.authToken });
}

showDialog = (DialogClass, props = {}) => {
this.setState({
dialog: <DialogClass {...{ onClose: this.closeDialog, ...props }} />
});
};

showAuthDialog = verifying => {
this.setState({ dialog: <AuthDialog closable="false" verifying={verifying} authOrigin={this.props.authOrigin} /> });
this.showDialog(AuthDialog, { closable: false, verifying, authOrigin: this.props.authOrigin });
};

loadHomeVideo = () => {
Expand All @@ -87,33 +103,40 @@ class HomeRoot extends Component {
playVideoWithStopOnBlur(videoEl);
};

closeDialog() {
closeDialog = () => {
this.setState({ dialog: null });
}
};

showJoinUsDialog() {
this.setState({ dialog: <JoinUsDialog onClose={this.closeDialog} /> });
}
showJoinUsDialog = () => this.showDialog(JoinUsDialog);

showReportDialog() {
this.setState({ dialog: <ReportDialog onClose={this.closeDialog} /> });
}
showReportDialog = () => this.showDialog(ReportDialog);

showUpdatesDialog() {
this.setState({
dialog: <UpdatesDialog onClose={this.closeDialog} onSubmittedEmail={() => this.showEmailSubmittedDialog()} />
showUpdatesDialog = () =>
this.showDialog(UpdatesDialog, {
onSubmittedEmail: () => {
this.showDialog(
<DialogContainer>Great! Please check your e-mail to confirm your subscription.</DialogContainer>
);
}
});
}

showEmailSubmittedDialog() {
this.setState({
dialog: (
<DialogContainer onClose={this.closeDialog}>
Great! Please check your e-mail to confirm your subscription.
</DialogContainer>
)
showSignInDialog = () => {
this.showDialog(SignInDialog, {
message: messages["sign-in.prompt"],
onSignIn: async email => {
const { authComplete } = await this.props.authChannel.startAuthentication(email);
this.showDialog(SignInDialog, { authStarted: true });
await authComplete;
this.setState({ signedIn: true, email });
this.closeDialog();
}
});
}
};

signOut = () => {
this.props.authChannel.signOut();
this.setState({ signedIn: false });
};

loadEnvironmentFromScene = async () => {
let sceneUrlBase = "/api/v1/scenes";
Expand Down Expand Up @@ -155,7 +178,7 @@ class HomeRoot extends Component {
Promise.all(environmentLoads).then(() => this.setState({ environments }));
};

onDialogLinkClicked = trigger => {
onLinkClicked = trigger => {
return e => {
e.preventDefault();
e.stopPropagation();
Expand Down Expand Up @@ -204,6 +227,22 @@ class HomeRoot extends Component {
</WithHoverSound>
</div>
</div>
<div className={styles.signIn}>
{this.state.signedIn ? (
<div>
<span>
<FormattedMessage id="sign-in.as" /> {maskEmail(this.state.email)}
</span>{" "}
<a onClick={this.onLinkClicked(this.signOut)}>
<FormattedMessage id="sign-in.out" />
</a>
</div>
) : (
<a onClick={this.onLinkClicked(this.showSignInDialog)}>
<FormattedMessage id="sign-in.in" />
</a>
)}
</div>
</div>
<div className={styles.heroContent}>
<div className={styles.attribution}>
Expand Down Expand Up @@ -264,7 +303,7 @@ class HomeRoot extends Component {
className={styles.link}
rel="noopener noreferrer"
href="#"
onClick={this.onDialogLinkClicked(this.showJoinUsDialog.bind(this))}
onClick={this.onLinkClicked(this.showJoinUsDialog)}
>
<FormattedMessage id="home.join_us" />
</a>
Expand All @@ -274,7 +313,7 @@ class HomeRoot extends Component {
className={styles.link}
rel="noopener noreferrer"
href="#"
onClick={this.onDialogLinkClicked(this.showUpdatesDialog.bind(this))}
onClick={this.onLinkClicked(this.showUpdatesDialog)}
>
<FormattedMessage id="home.get_updates" />
</a>
Expand All @@ -284,7 +323,7 @@ class HomeRoot extends Component {
className={styles.link}
rel="noopener noreferrer"
href="#"
onClick={this.onDialogLinkClicked(this.showReportDialog.bind(this))}
onClick={this.onLinkClicked(this.showReportDialog)}
>
<FormattedMessage id="home.report_issue" />
</a>
Expand Down
22 changes: 3 additions & 19 deletions src/react-components/scene-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import en from "react-intl/locale-data/en";
import styles from "../assets/stylesheets/scene-ui.scss";
import hubLogo from "../assets/images/hub-preview-white.png";
import spokeLogo from "../assets/images/spoke_logo_black.png";
import { getReticulumFetchUrl } from "../utils/phoenix-utils";
import { generateHubName } from "../utils/name-generation";
import { createAndRedirectToNewHub } from "../utils/phoenix-utils";
import { WithHoverSound } from "./wrap-with-audio";
import CreateRoomDialog from "./create-room-dialog.js";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
Expand Down Expand Up @@ -52,23 +51,8 @@ class SceneUI extends Component {
this.props.scene.removeEventListener("loaded", this.onSceneLoaded);
}

createRoom = async () => {
const payload = { hub: { name: this.state.customRoomName || generateHubName(), scene_id: this.props.sceneId } };
const createUrl = getReticulumFetchUrl("/api/v1/hubs");

const res = await fetch(createUrl, {
body: JSON.stringify(payload),
headers: { "content-type": "application/json" },
method: "POST"
});

const hub = await res.json();

if (!process.env.RETICULUM_SERVER || document.location.host === process.env.RETICULUM_SERVER) {
document.location = hub.url;
} else {
document.location = `/hub.html?hub_id=${hub.hub_id}`;
}
createRoom = () => {
createAndRedirectToNewHub(this.state.customRoomName, this.props.sceneId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops

};

render() {
Expand Down
1 change: 1 addition & 0 deletions src/react-components/ui-root.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class UIRoot extends Component {
platformUnsupportedReason: PropTypes.string,
hubId: PropTypes.string,
hubName: PropTypes.string,
isOwner: PropTypes.bool,
hubScene: PropTypes.object,
isSupportAvailable: PropTypes.bool,
presenceLogEntries: PropTypes.array,
Expand Down
19 changes: 17 additions & 2 deletions src/utils/auth-channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,27 @@ export default class AuthChannel {
constructor(store) {
this.store = store;
this.socket = null;
this._authenticated = !!this.store.state.credentials.token;
}

setSocket = socket => {
this.socket = socket;
};

get email() {
return this.store.state.credentials.email;
}

get authenticated() {
return this._authenticated;
}

signOut = async hubChannel => {
await hubChannel.signOut();
if (hubChannel) {
await hubChannel.signOut();
}
this.store.update({ credentials: { token: null, email: null } });
this._authenticated = false;
};

async startAuthentication(email, hubChannel) {
Expand All @@ -27,7 +39,10 @@ export default class AuthChannel {
const authComplete = new Promise(resolve =>
channel.on("auth_credentials", async ({ credentials: token }) => {
this.store.update({ credentials: { email, token } });
await hubChannel.signIn(token);
if (hubChannel) {
await hubChannel.signIn(token);
}
this._authenticated = true;
resolve();
})
);
Expand Down
2 changes: 1 addition & 1 deletion src/utils/hub-channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function isSameDay(da, db) {
export default class HubChannel {
constructor(store) {
this.store = store;
this._signedIn = false;
this._signedIn = !!this.store.state.credentials.token;
}

get signedIn() {
Expand Down
10 changes: 9 additions & 1 deletion src/utils/phoenix-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import uuid from "uuid/v4";
import { Socket } from "phoenix";
import { generateHubName } from "../utils/name-generation";

import Store from "../storage/store";

export function connectToReticulum(debug = false) {
const qs = new URLSearchParams(location.search);

Expand Down Expand Up @@ -64,9 +66,15 @@ export async function createAndRedirectToNewHub(name, sceneId, sceneUrl, replace
payload.hub.default_environment_gltf_bundle_url = sceneUrl;
}

const headers = { "content-type": "application/json" };
const store = new Store();
if (store.state && store.state.credentials.token) {
headers.authorization = `bearer ${store.state.credentials.token}`;
}

const res = await fetch(createUrl, {
body: JSON.stringify(payload),
headers: { "content-type": "application/json" },
headers,
method: "POST"
});

Expand Down