From cff506e832d3f992a9f314fe8a77020a1f973064 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Fri, 13 Nov 2020 11:50:57 -0800 Subject: [PATCH 01/31] Reduce custom CSS --- src/react-components/auth/SignInModal.js | 57 ++++++++++--------- src/react-components/auth/SignInModal.scss | 16 ------ src/react-components/auth/VerifyModal.js | 8 +-- src/react-components/auth/VerifyModal.scss | 7 --- src/react-components/input/InputField.scss | 2 + .../input/RadioInputField.stories.js | 34 ++++++----- src/react-components/layout/Column.js | 26 ++++++++- src/react-components/layout/Column.scss | 17 ++++++ .../layout/LoadingScreenLayout.scss | 8 --- src/react-components/modal/Modal.js | 2 +- src/react-components/modal/Modal.scss | 5 -- .../room/AutoExitWarningModal.js | 3 +- .../room/AutoExitWarningModal.scss | 3 - .../room/EnterOnDeviceModal.js | 23 ++++---- .../room/EnterOnDeviceModal.scss | 44 -------------- .../room/EnterOnDeviceModal.stories.js | 17 +++--- src/react-components/room/LeaveRoomModal.js | 3 +- src/react-components/room/LeaveRoomModal.scss | 9 --- .../room/MicPermissionsModal.js | 8 +-- .../room/MicPermissionsModal.scss | 13 ----- src/react-components/room/ObjectMenu.js | 2 +- src/react-components/room/ObjectMenu.scss | 5 -- .../room/PromoteClientModal.js | 3 +- .../room/PromoteClientModal.scss | 3 - src/react-components/room/RoomEntryModal.scss | 6 -- .../room/RoomSettingsSidebar.js | 52 ++++++++--------- .../room/UserProfileSidebar.js | 1 - .../room/UserProfileSidebar.scss | 8 --- src/react-components/sidebar/Sidebar.js | 2 +- src/react-components/sidebar/Sidebar.scss | 5 -- src/react-components/styles/global.scss | 41 +++++++++---- 31 files changed, 183 insertions(+), 250 deletions(-) delete mode 100644 src/react-components/auth/SignInModal.scss delete mode 100644 src/react-components/auth/VerifyModal.scss delete mode 100644 src/react-components/room/AutoExitWarningModal.scss delete mode 100644 src/react-components/room/LeaveRoomModal.scss delete mode 100644 src/react-components/room/PromoteClientModal.scss diff --git a/src/react-components/auth/SignInModal.js b/src/react-components/auth/SignInModal.js index 742d0f0c8c..d56adba026 100644 --- a/src/react-components/auth/SignInModal.js +++ b/src/react-components/auth/SignInModal.js @@ -3,7 +3,6 @@ import PropTypes from "prop-types"; import { CloseButton } from "../input/CloseButton"; import { Modal } from "../modal/Modal"; import { FormattedMessage } from "react-intl"; -import styles from "./SignInModal.scss"; import { Button } from "../input/Button"; import { TextInputField } from "../input/TextInputField"; import { Column } from "../layout/Column"; @@ -33,7 +32,7 @@ export function SubmitEmail({ onSubmitEmail, initialEmail, showPrivacy, privacyU ); return ( - +

{message || }

{(showTerms || showPrivacy) && ( - - By proceeding, you agree to the{" "} - {showTerms && ( - <> - - terms of use - {" "} - - )} - {showTerms && showPrivacy && "and "} - {showPrivacy && ( - - privacy notice - - )}. - +

+ + By proceeding, you agree to the{" "} + {showTerms && ( + <> + + terms of use + {" "} + + )} + {showTerms && showPrivacy && "and "} + {showPrivacy && ( + + privacy notice + + )}. + +

)} diff --git a/src/react-components/room/MicPermissionsModal.scss b/src/react-components/room/MicPermissionsModal.scss index e920a855eb..8a2cdadb9f 100644 --- a/src/react-components/room/MicPermissionsModal.scss +++ b/src/react-components/room/MicPermissionsModal.scss @@ -1,18 +1,5 @@ @use "../styles/theme.scss"; -:local(.content) { - h1 { - font-weight: theme.$font-weight-bold; - font-size: theme.$font-size-md; - } - - p { - font-size: theme.$font-size-sm; - font-weight: theme.$font-weight-medium; - line-height: 1.2; - } -} - :local(.error) { color: theme.$red; } diff --git a/src/react-components/room/ObjectMenu.js b/src/react-components/room/ObjectMenu.js index 6930d57c1f..a99ef76b09 100644 --- a/src/react-components/room/ObjectMenu.js +++ b/src/react-components/room/ObjectMenu.js @@ -43,7 +43,7 @@ export function ObjectMenu({ -

{title}

+
{title}
{joinChildren(children, () =>
)}
diff --git a/src/react-components/room/ObjectMenu.scss b/src/react-components/room/ObjectMenu.scss index 5f1186e4f1..36d663f1a3 100644 --- a/src/react-components/room/ObjectMenu.scss +++ b/src/react-components/room/ObjectMenu.scss @@ -56,11 +56,6 @@ align-items: center; justify-content: center; - h1 { - font-size: theme.$font-size-sm; - font-weight: theme.$font-weight-bold; - } - @media(min-width: theme.$breakpoint-md) { display: flex; } diff --git a/src/react-components/room/PromoteClientModal.js b/src/react-components/room/PromoteClientModal.js index 05ab65d443..70d3d5ef03 100644 --- a/src/react-components/room/PromoteClientModal.js +++ b/src/react-components/room/PromoteClientModal.js @@ -2,7 +2,6 @@ import React from "react"; import PropTypes from "prop-types"; import { FormattedMessage } from "react-intl"; import { Modal } from "../modal/Modal"; -import styles from "./PromoteClientModal.scss"; import { Button } from "../input/Button"; import { CloseButton } from "../input/CloseButton"; import { Column } from "../layout/Column"; @@ -10,7 +9,7 @@ import { Column } from "../layout/Column"; export function PromoteClientModal({ onClose, onConfirm, displayName }) { return ( }> - +

diff --git a/src/react-components/room/PromoteClientModal.scss b/src/react-components/room/PromoteClientModal.scss deleted file mode 100644 index 5059da19a1..0000000000 --- a/src/react-components/room/PromoteClientModal.scss +++ /dev/null @@ -1,3 +0,0 @@ -:local(.modal-content) { - line-height: 1.25; -} \ No newline at end of file diff --git a/src/react-components/room/RoomEntryModal.scss b/src/react-components/room/RoomEntryModal.scss index 3fa0f13e60..f1cc66d803 100644 --- a/src/react-components/room/RoomEntryModal.scss +++ b/src/react-components/room/RoomEntryModal.scss @@ -3,12 +3,6 @@ :local(.content) { padding: 24px 8px; - hr { - width: 100%; - border: none; - border-bottom: 1px solid theme.$grey; - } - button { width: 156px; } diff --git a/src/react-components/room/RoomSettingsSidebar.js b/src/react-components/room/RoomSettingsSidebar.js index 01760db201..942f32a545 100644 --- a/src/react-components/room/RoomSettingsSidebar.js +++ b/src/react-components/room/RoomSettingsSidebar.js @@ -129,34 +129,32 @@ export function RoomSettingsSidebar({ /> {entryMode === "invite" && ( -
- - {" "} - - - {" "} - /{" "} - - - - - ) : ( - - + + {" "} + + + {" "} + /{" "} + + - )) - } - fullWidth - /> -
+ + ) : ( + + + + )) + } + fullWidth + /> )} {showPublicRoomSetting && ( : } className={className} - contentClassName={styles.content} {...rest} > diff --git a/src/react-components/room/UserProfileSidebar.scss b/src/react-components/room/UserProfileSidebar.scss index 2f634e4fa5..991bdfb08f 100644 --- a/src/react-components/room/UserProfileSidebar.scss +++ b/src/react-components/room/UserProfileSidebar.scss @@ -1,13 +1,5 @@ @use "../styles/theme.scss"; -:local(.content) { - display: flex; - flex-direction: column; - align-items: center; - padding: 24px; - text-align: center; -} - :local(.avatar-preview-container) { display: flex; flex-direction: column; diff --git a/src/react-components/sidebar/Sidebar.js b/src/react-components/sidebar/Sidebar.js index 2b3981f919..f345101893 100644 --- a/src/react-components/sidebar/Sidebar.js +++ b/src/react-components/sidebar/Sidebar.js @@ -9,7 +9,7 @@ export function Sidebar({ title, beforeTitle, afterTitle, children, contentClass {(title || beforeTitle || afterTitle) && (
{beforeTitle}
-

{title}

+
{title}
{afterTitle}
)} diff --git a/src/react-components/sidebar/Sidebar.scss b/src/react-components/sidebar/Sidebar.scss index 92bcb47c23..b39a76783c 100644 --- a/src/react-components/sidebar/Sidebar.scss +++ b/src/react-components/sidebar/Sidebar.scss @@ -20,11 +20,6 @@ border-bottom: 1px solid theme.$lightgrey; align-items: center; justify-content: center; - - h1 { - font-size: theme.$font-size-sm; - font-weight: theme.$font-weight-bold; - } } :local(.before-title) { diff --git a/src/react-components/styles/global.scss b/src/react-components/styles/global.scss index 6ae0ac8f39..1944c296af 100644 --- a/src/react-components/styles/global.scss +++ b/src/react-components/styles/global.scss @@ -69,14 +69,6 @@ html { font-family: 'Poppins', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" } -/* - * Ensure horizontal rules are visible by default - */ - -hr { - border-top-width: 1px; -} - textarea { resize: vertical; } @@ -98,7 +90,7 @@ h4, h5, h6 { font-size: inherit; - font-weight: inherit; + font-weight: theme.$font-weight-bold; } /** @@ -232,14 +224,21 @@ body::before { h5 { font-size: theme.$font-size-sm; - font-weight: theme.$font-weight-bold; } -label, small { +label, small, strong { font-size: theme.$font-size-xs; font-weight: theme.$font-weight-bold; } +small { + color: theme.$darkgrey; +} + +p, small, strong { + line-height: 1.25; +} + a { color: theme.$blue; @@ -252,6 +251,26 @@ a { } } +hr { + position: relative; + width: 90%; + border: none; + border-bottom: 1px solid theme.$grey; + margin: 16px 0; + + &:after { + background: theme.$white; + content: attr(data-or-text); + padding: 0 4px; + position: relative; + color: theme.$black; + font-size: theme.$font-size-sm; + font-weight: theme.$font-weight-bold; + position: absolute; + transform: translateY(-50%) translateX(-50%); + } +} + input::placeholder { color: theme.$darkgrey; } From a5aa8ed5a343f4d714cad672786a571368dac382 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Fri, 13 Nov 2020 11:51:31 -0800 Subject: [PATCH 02/31] Migrate avatar/scene/object modals --- package-lock.json | 199 +++++++++++------- package.json | 2 +- src/assets/stylesheets/avatar-url-dialog.scss | 16 -- .../stylesheets/change-scene-dialog.scss | 72 ------- .../stylesheets/create-object-dialog.scss | 79 ------- src/react-components/avatar-url-dialog.js | 61 ------ src/react-components/change-scene-dialog.js | 120 ----------- src/react-components/create-object-dialog.js | 187 ---------------- src/react-components/media-browser.js | 22 +- src/react-components/room/AvatarUrlModal.js | 40 ++++ .../room/AvatarUrlModal.stories.js | 12 ++ .../room/AvatarUrlModalContainer.js | 21 ++ src/react-components/room/ObjectUrlModal.js | 112 ++++++++++ src/react-components/room/ObjectUrlModal.scss | 4 + .../room/ObjectUrlModal.stories.js | 12 ++ .../room/ObjectUrlModalContainer.js | 32 +++ src/react-components/room/SceneUrlModal.js | 60 ++++++ .../room/SceneUrlModal.stories.js | 14 ++ .../room/SceneUrlModalContainer.js | 40 ++++ src/react-components/ui-root.js | 37 +--- 20 files changed, 495 insertions(+), 647 deletions(-) delete mode 100644 src/assets/stylesheets/avatar-url-dialog.scss delete mode 100644 src/assets/stylesheets/change-scene-dialog.scss delete mode 100644 src/assets/stylesheets/create-object-dialog.scss delete mode 100644 src/react-components/avatar-url-dialog.js delete mode 100644 src/react-components/change-scene-dialog.js delete mode 100644 src/react-components/create-object-dialog.js create mode 100644 src/react-components/room/AvatarUrlModal.js create mode 100644 src/react-components/room/AvatarUrlModal.stories.js create mode 100644 src/react-components/room/AvatarUrlModalContainer.js create mode 100644 src/react-components/room/ObjectUrlModal.js create mode 100644 src/react-components/room/ObjectUrlModal.scss create mode 100644 src/react-components/room/ObjectUrlModal.stories.js create mode 100644 src/react-components/room/ObjectUrlModalContainer.js create mode 100644 src/react-components/room/SceneUrlModal.js create mode 100644 src/react-components/room/SceneUrlModal.stories.js create mode 100644 src/react-components/room/SceneUrlModalContainer.js diff --git a/package-lock.json b/package-lock.json index 2b8a9287b4..53b4482fb2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5563,67 +5563,114 @@ "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==", "dev": true }, - "@formatjs/intl-datetimeformat": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@formatjs/intl-datetimeformat/-/intl-datetimeformat-2.3.0.tgz", - "integrity": "sha512-wPlVtqPJjXyEIHO05JZ8G+t9fV00ho2cL8BCaDlT8vJQ1V2fnBp3cx7jlKpKCV7wNZm3yEebTATv0e0T3WisWw==", + "@formatjs/ecma402-abstract": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.4.0.tgz", + "integrity": "sha512-Mv027hcLFjE45K8UJ8PjRpdDGfR0aManEFj1KzoN8zXNveHGEygpZGfFf/FTTMl+QEVSrPAUlyxaCApvmv47AQ==", "requires": { - "@formatjs/intl-getcanonicallocales": "^1.3.1", - "@formatjs/intl-utils": "^3.8.2" + "tslib": "^2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + } } }, - "@formatjs/intl-displaynames": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-3.1.7.tgz", - "integrity": "sha512-a0aOuCa8HUEq/vnc3cT88Ocww9tC6KbXhgfk4OzB38acFBWi34lAHTEC34ZmkJg1ASdWkbXOi18WdXNzEZCXZg==", + "@formatjs/intl": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-1.4.5.tgz", + "integrity": "sha512-ur7OtcLnfvLH3j7R9fkJoucwXuCTY54dVAN55O6nEn8s+XL36Fs5td689AzaCe5/Y2wwO4aS4/fXw45Ndy2Twg==", "requires": { - "@formatjs/intl-utils": "^3.8.2" + "@formatjs/ecma402-abstract": "1.4.0", + "@formatjs/intl-datetimeformat": "2.8.4", + "@formatjs/intl-displaynames": "3.4.6", + "@formatjs/intl-listformat": "4.3.6", + "@formatjs/intl-relativetimeformat": "7.3.6", + "fast-memoize": "^2.5.2", + "intl-messageformat": "9.3.18", + "intl-messageformat-parser": "6.0.16", + "tslib": "^2.0.1" + }, + "dependencies": { + "intl-messageformat-parser": { + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-6.0.16.tgz", + "integrity": "sha512-Qy3Zz0vF4fhMVuW4BDqUr55LsOl9enM03wuwbP4Yg7v29rYNpf7Z76Whstu6uDLDJokrjbpgDvRcjSDTAhxKJw==", + "requires": { + "@formatjs/ecma402-abstract": "1.4.0", + "tslib": "^2.0.1" + } + }, + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + } } }, - "@formatjs/intl-getcanonicallocales": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@formatjs/intl-getcanonicallocales/-/intl-getcanonicallocales-1.3.1.tgz", - "integrity": "sha512-0E1ZOTwIB8zEjqnW4V7k0gBVdmtbXiLfq1sWhX28Uq59iTmo+zoADre9dIr15Sx6Kl+4JgC6Mu9/bP3F746Fjg==", + "@formatjs/intl-datetimeformat": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/@formatjs/intl-datetimeformat/-/intl-datetimeformat-2.8.4.tgz", + "integrity": "sha512-aCD6VoUvdLiJrAfKfHojbzX/e5inqvO+N9rj9kdOzCMTEdDGJ+Jd9SmXPn2YKb1glp785mYxAPp5vxjgErjq/g==", "requires": { - "cldr-core": "36.0.0" + "@formatjs/ecma402-abstract": "1.4.0", + "tslib": "^2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + } } }, - "@formatjs/intl-listformat": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-4.0.0.tgz", - "integrity": "sha512-ptWeK9LSuvWZswZP0NpGkchoBOSi+brrbGC2MxakitkXb6o1smDtnLnQP0Ai/yv0/xR1bV+D8n9o++ycUgXwaA==", + "@formatjs/intl-displaynames": { + "version": "3.4.6", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-3.4.6.tgz", + "integrity": "sha512-XWVhTQjcyWweHm2Hi0n92hYILpktbnh8x5Sj5nSBIUNz4Os4uHDj2itJI3xCxl+aDNW8M7VRJ9m0OsnhI8qQrg==", "requires": { - "@formatjs/intl-utils": "^3.8.2" + "@formatjs/ecma402-abstract": "1.4.0", + "tslib": "^2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + } } }, - "@formatjs/intl-numberformat": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/@formatjs/intl-numberformat/-/intl-numberformat-5.3.3.tgz", - "integrity": "sha512-keU4arK7WoQMMfUuXzVP5nPzKFXrNpcNne/58OxxDxoJfqiv4vHiBjHHfPzy5xf+1rgnoplaV1mabNt1HTPtwA==", + "@formatjs/intl-listformat": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-4.3.6.tgz", + "integrity": "sha512-+ePU3/rmgUd8QHcf7EGJAIU12+Y6eMGaDhI3tPw3kKN/vByE9hJAyR8etXTk9fSIV6D7us2tEDM4h0wn8eenig==", "requires": { - "@formatjs/intl-utils": "^3.8.2" + "@formatjs/ecma402-abstract": "1.4.0", + "tslib": "^2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + } } }, "@formatjs/intl-relativetimeformat": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-7.0.0.tgz", - "integrity": "sha512-QQpQezSY8Q5H/PZVvPAs8SFhoUVXM52le+UgKAYTyAkeC4lr+DkX4ZP7Msateu81K1jFB8jhxcSOUEzGie7g2w==", - "requires": { - "@formatjs/intl-utils": "^3.8.2" - } - }, - "@formatjs/intl-utils": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/@formatjs/intl-utils/-/intl-utils-3.8.2.tgz", - "integrity": "sha512-34J1HmuNSRYQgJ6Gi/dYJQzdDsyAPJOrzXx977UajrhlXlI+0dvoqleP4W+Qtyc65EqzTGtUSgqLYhoPty8v3w==", + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-7.3.6.tgz", + "integrity": "sha512-RtmdGG6Lwf99xlGjFiIk6pYe9LQJpTYa7VofV322Q3gmb3tAU6Pgyn7LML4xbG7Pd1F7JBmnVNVwFyofUfyJQA==", "requires": { - "emojis-list": "^3.0.0" + "@formatjs/ecma402-abstract": "1.4.0", + "tslib": "^2.0.1" }, "dependencies": { - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" } } }, @@ -17405,11 +17452,6 @@ "resolved": "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz", "integrity": "sha1-Q5Nb/90pHzJtrQogUwmzjQD2UM4=" }, - "cldr-core": { - "version": "36.0.0", - "resolved": "https://registry.npmjs.org/cldr-core/-/cldr-core-36.0.0.tgz", - "integrity": "sha512-QLnAjt20rZe38c8h8OJ9jPND+O4o5O8Nw0TK/P3KpNn1cmOhMu0rk6Kc3ap96c5OStQ9gAngs9+Be2sum26NOw==" - }, "clean-css": { "version": "4.1.11", "resolved": "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.11.tgz", @@ -22658,21 +22700,28 @@ "dev": true }, "intl-messageformat": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.2.0.tgz", - "integrity": "sha512-mdASJPZpKfhbM//NLNIETArWgVL06j5M9ZdhgiKBO4XayS4GQHnXW+zVUE5iAwHcGZIKzvdWCj4D4kmNzUmEzA==", + "version": "9.3.18", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.3.18.tgz", + "integrity": "sha512-OKrLWppdxXtRdRCPjmRZ9Ru7UZkZJDlMl+1Vpb3sCLWK0mFpr129K+gIlIb5zrWoAH3NiYDzekBXPTRWCyHSIA==", "requires": { "fast-memoize": "^2.5.2", - "intl-messageformat-parser": "^5.3.7" + "intl-messageformat-parser": "6.0.16", + "tslib": "^2.0.1" }, "dependencies": { "intl-messageformat-parser": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-5.3.7.tgz", - "integrity": "sha512-83C+eODXu/zfR35bKjSz/HKEGkNSHbsF1WOuZDgFs/TudaEuVIZ8pstQYFyY0CIuCGXomcSevrFW1d4y7nW5xw==", + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-6.0.16.tgz", + "integrity": "sha512-Qy3Zz0vF4fhMVuW4BDqUr55LsOl9enM03wuwbP4Yg7v29rYNpf7Z76Whstu6uDLDJokrjbpgDvRcjSDTAhxKJw==", "requires": { - "@formatjs/intl-numberformat": "^5.3.3" + "@formatjs/ecma402-abstract": "1.4.0", + "tslib": "^2.0.1" } + }, + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" } } }, @@ -29686,31 +29735,37 @@ } }, "react-intl": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.4.5.tgz", - "integrity": "sha512-XFs4Q70p17AVSLYBMopgNtWe537+U6SuXM1is7b9ThqZnZkAXqhJcXUA/0OsDFSIDilQ82NvVO1NAgEBXkRBFQ==", - "requires": { - "@formatjs/intl-datetimeformat": "^2.3.0", - "@formatjs/intl-displaynames": "^3.1.7", - "@formatjs/intl-listformat": "^4.0.0", - "@formatjs/intl-numberformat": "^5.3.3", - "@formatjs/intl-relativetimeformat": "^7.0.0", - "@formatjs/intl-utils": "^3.8.2", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.10.1.tgz", + "integrity": "sha512-D5zP6P7D70qFQc4KSYlwZ5iw1r2kXkmCGD1V6opM7ckIWj6FKs4p1za3eFOn0GpcEJNUQzTwNP4qu1NCHnHS2w==", + "requires": { + "@formatjs/ecma402-abstract": "1.4.0", + "@formatjs/intl": "1.4.5", + "@formatjs/intl-displaynames": "3.4.6", + "@formatjs/intl-listformat": "4.3.6", + "@formatjs/intl-relativetimeformat": "7.3.6", "@types/hoist-non-react-statics": "^3.3.1", "fast-memoize": "^2.5.2", "hoist-non-react-statics": "^3.3.2", - "intl-messageformat": "^9.2.0", - "intl-messageformat-parser": "^5.3.7", - "shallow-equal": "^1.2.1" + "intl-messageformat": "9.3.18", + "intl-messageformat-parser": "6.0.16", + "shallow-equal": "^1.2.1", + "tslib": "^2.0.1" }, "dependencies": { "intl-messageformat-parser": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-5.3.7.tgz", - "integrity": "sha512-83C+eODXu/zfR35bKjSz/HKEGkNSHbsF1WOuZDgFs/TudaEuVIZ8pstQYFyY0CIuCGXomcSevrFW1d4y7nW5xw==", + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-6.0.16.tgz", + "integrity": "sha512-Qy3Zz0vF4fhMVuW4BDqUr55LsOl9enM03wuwbP4Yg7v29rYNpf7Z76Whstu6uDLDJokrjbpgDvRcjSDTAhxKJw==", "requires": { - "@formatjs/intl-numberformat": "^5.3.3" + "@formatjs/ecma402-abstract": "1.4.0", + "tslib": "^2.0.1" } + }, + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" } } }, diff --git a/package.json b/package.json index d7690e923e..4b88a3e2c5 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "react-emoji-render": "^0.4.6", "react-hook-form": "^6.10.1", "react-infinite-scroller": "^1.2.2", - "react-intl": "^5.4.5", + "react-intl": "^5.10.1", "react-linkify": "^0.2.2", "react-popper-2": "npm:react-popper@^2.2.3", "react-router": "^5.1.2", diff --git a/src/assets/stylesheets/avatar-url-dialog.scss b/src/assets/stylesheets/avatar-url-dialog.scss deleted file mode 100644 index 92a79b2c80..0000000000 --- a/src/assets/stylesheets/avatar-url-dialog.scss +++ /dev/null @@ -1,16 +0,0 @@ -@import 'shared'; - -:local(.avatar-url-dialog) { - .url-field { - @extend %input-field; - } - .submit { - @extend %bottom-action-button; - } - .form { - display: flex; - flex-direction: column; - align-items: center; - min-width: 20em; - } -} diff --git a/src/assets/stylesheets/change-scene-dialog.scss b/src/assets/stylesheets/change-scene-dialog.scss deleted file mode 100644 index 13757298c4..0000000000 --- a/src/assets/stylesheets/change-scene-dialog.scss +++ /dev/null @@ -1,72 +0,0 @@ -@import 'shared'; - -:local(.change-scene-button) { - display: flex; - flex-direction: column; - align-items: center; - text-align: center; - margin: 0; -} - -:local(.action-button) { - @extend %unselectable; - @extend %bottom-action-button; - margin-left: 6px; - margin-right: 6px; - width: 128px; - text-align: center; - &:disabled { - background-color: $action-color-disabled; - } -} - -:local(.buttons) { - @extend %unselectable; - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; -} - -:local(.input-border) { - display: flex; - @extend %rounded-border; - @extend %default-font; - margin: 12px 0px; - padding: 0.5em 0.75em; - width: 100%; - box-sizing: border-box; -} - -:local(.left-side-of-input) { - @extend %default-font; - flex-grow: 1; - border: none; - white-space: nowrap; - background: transparent; - color: var(--text-field-text-color); - font-size: 1.2em; - align-self: center; - overflow: hidden; - text-overflow: ellipsis; - width: 100%; -} - -:local(.spoke-create) { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - border-top: 1px solid var(--panel-rule-color); - margin-top: 64px; - - div { - margin-top: 28px; - } -} - -:local(.spoke-launch) { - @extend %spoke-action-button; - margin-top: 18px; - color: var(--spoke-action-text-color) !important; // TODO HACK because of dialog style overriding color -} diff --git a/src/assets/stylesheets/create-object-dialog.scss b/src/assets/stylesheets/create-object-dialog.scss deleted file mode 100644 index 6562bd5c88..0000000000 --- a/src/assets/stylesheets/create-object-dialog.scss +++ /dev/null @@ -1,79 +0,0 @@ -@import 'shared'; - -:local(.add-media-form) { - display: flex; - flex-direction: column; - align-items: center; - text-align: center; - margin: 0; -} - -:local(.action-button) { - @extend %unselectable; - @extend %bottom-action-button; - margin-left: 6px; - margin-right: 6px; - appearance: none; - width: 128px; - text-align: center; - -moz-appearance: none; - -webkit-appearance: none; -} - -:local(.buttons) { - @extend %unselectable; - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; -} - -:local(.small-button) { - @extend %unselectable; - margin-left: 0.25em; - font-size: 2em; - align-self: center; -} - -:local(.cancel-icon) { - color: var(--text-field-widget-color); - cursor: pointer; -} - -:local(.upload-icon) { - color: $action-label-color; - cursor: pointer; - - &:hover { - color: var(--action-label-color-highlight); - } -} - -:local(.input-border) { - display: flex; - @extend %rounded-border; - @extend %default-font; - margin: 1em; - padding: 0.5em 0.75em; - width: 100%; - box-sizing: border-box; -} - -:local(.left-side-of-input) { - @extend %default-font; - flex-grow: 1; - border: none; - white-space: nowrap; - background: transparent; - color: var(--text-field-text-color); - font-size: 1.2em; - align-self: center; - overflow: hidden; - text-overflow: ellipsis; - width: 100%; -} - -:local(.hide-file-input) { - visibility: hidden; - position: absolute; -} diff --git a/src/react-components/avatar-url-dialog.js b/src/react-components/avatar-url-dialog.js deleted file mode 100644 index 0d9576d613..0000000000 --- a/src/react-components/avatar-url-dialog.js +++ /dev/null @@ -1,61 +0,0 @@ -import React, { Component } from "react"; -import PropTypes from "prop-types"; -import { FormattedMessage } from "react-intl"; - -import styles from "../assets/stylesheets/avatar-url-dialog.scss"; -import DialogContainer from "./dialog-container.js"; -import { handleTextFieldFocus, handleTextFieldBlur } from "../utils/focus-utils"; - -export default class AvatarUrlDialog extends Component { - state = { - url: "" - }; - - static propTypes = { - onChange: PropTypes.func, - onClose: PropTypes.func - }; - - onUrlChange = e => { - this.setState({ - url: e.target && e.target.value - }); - }; - - onChangeClicked = e => { - e.preventDefault(); - - if (this.state.url) { - this.props.onChange(this.state.url); - } - - this.props.onClose(); - }; - - render() { - return ( - -

- Paste a URL to a{" "} - - GLB - . -

-
- handleTextFieldFocus(e.target)} - onBlur={() => handleTextFieldBlur()} - onChange={this.onUrlChange} - type="url" - value={this.state.url} - /> - -
- - ); - } -} diff --git a/src/react-components/change-scene-dialog.js b/src/react-components/change-scene-dialog.js deleted file mode 100644 index 0adb385347..0000000000 --- a/src/react-components/change-scene-dialog.js +++ /dev/null @@ -1,120 +0,0 @@ -import React, { Component } from "react"; -import PropTypes from "prop-types"; -import { FormattedMessage } from "react-intl"; -import cx from "classnames"; - -import IfFeature from "./if-feature"; -import styles from "../assets/stylesheets/change-scene-dialog.scss"; -import DialogContainer from "./dialog-container.js"; -import { handleTextFieldFocus, handleTextFieldBlur } from "../utils/focus-utils"; -import { getMessages } from "../utils/i18n"; -import { isValidSceneUrl } from "../utils/scene-url-utils"; - -export default class ChangeSceneDialog extends Component { - state = { - url: "", - submitting: false - }; - - static propTypes = { - onChange: PropTypes.func, - onClose: PropTypes.func - }; - - constructor(props) { - super(props); - this.urlValidationPromise = null; - } - - onUrlChange = e => { - const urlInput = e.target; - const url = urlInput.value; - this.setState({ url }); - - urlInput.setCustomValidity(""); - - this.urlValidationPromise = new Promise(async resolve => { - urlInput.setCustomValidity((await isValidSceneUrl(url.trim())) ? "" : getMessages()["invalid-scene-url"]); - resolve(); - }); - }; - - reset = e => { - e.preventDefault(); - this.setState({ url: "" }); - }; - - onSubmit = async e => { - e.preventDefault(); - - this.setState({ submitting: true }); - - const form = e.target; - - if (this.state.url) { - await this.urlValidationPromise; - if (form.checkValidity()) { - this.props.onChange(this.state.url.trim()); - this.props.onClose(); - } else { - form.reportValidity(); - this.setState({ submitting: false }); - } - } - }; - - render() { - const { onChange, onClose } = this.props; // eslint-disable-line no-unused-vars - - return ( - -
-
-

- Paste a URL to a{" "} - - - - {" "} - - scene or a URL to a{" "} - - GLB - . -

-
-
-
-
- handleTextFieldFocus(e.target)} - onBlur={() => handleTextFieldBlur()} - onChange={this.onUrlChange} - type="url" - value={this.state.url} - /> -
-
- -
- -
-
- -
- - - -
-
-
-
-
-
- ); - } -} diff --git a/src/react-components/create-object-dialog.js b/src/react-components/create-object-dialog.js deleted file mode 100644 index 9a15eec39b..0000000000 --- a/src/react-components/create-object-dialog.js +++ /dev/null @@ -1,187 +0,0 @@ -import React, { Component } from "react"; -import PropTypes from "prop-types"; -import cx from "classnames"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faPaperclip } from "@fortawesome/free-solid-svg-icons/faPaperclip"; -import { faTimes } from "@fortawesome/free-solid-svg-icons/faTimes"; - -import { getMessages } from "../utils/i18n"; -import configs from "../utils/configs"; -import IfFeature from "./if-feature"; -import giphyLogo from "../assets/images/giphy_logo.png"; -import styles from "../assets/stylesheets/create-object-dialog.scss"; -import ducky from "../assets/models/DuckyMesh.glb"; -import DialogContainer from "./dialog-container.js"; -import { handleTextFieldFocus, handleTextFieldBlur } from "../utils/focus-utils"; -import { getAbsoluteHref } from "../utils/media-url-utils"; -import { WithHoverSound } from "./wrap-with-audio"; - -const attributionHostnames = { - "giphy.com": giphyLogo, - "media.giphy.com": giphyLogo -}; - -const isMobile = AFRAME.utils.device.isMobile() || AFRAME.utils.device.isMobileVR(); -const instructions = "Paste a URL to an image, video, model, scene, or upload."; -const desktopTips = `Tip: You can paste URLs directly into ${getMessages()["app-name"]} with Ctrl+V`; -const references = ( - - For models, try{" "} - - Sketchfab - {" "} - and{" "} - - Google Poly - - - , or our{" "} - - collection - - . - -); - -const mobileInstructions = ( -
-

{instructions}

-

{references}

-
-); -const desktopInstructions = ( -
-

{instructions}

-

{references}

-

{desktopTips}

-
-); - -let lastUrl = ""; -const fileInputId = "file-input"; - -export default class CreateObjectDialog extends Component { - state = { - url: "", - file: null, - fileName: "" - }; - - static propTypes = { - onCreate: PropTypes.func, - onClose: PropTypes.func - }; - - componentDidMount() { - this.setState({ url: lastUrl }); - } - - componentWillUnmount() { - lastUrl = this.state.url; - } - - onUrlChange = e => { - let attributionImage = this.state.attributionImage; - if (e.target && e.target.value && e.target.validity.valid) { - const url = new URL(e.target.value); - attributionImage = attributionHostnames[url.hostname]; - } - this.setState({ - url: e.target && e.target.value, - attributionImage: attributionImage - }); - }; - - onFileChange = e => { - this.setState({ - file: e.target.files[0], - fileName: e.target.files[0].name - }); - }; - - reset = e => { - e.preventDefault(); - this.setState({ - url: "", - file: null, - fileName: "" - }); - this.fileInput.value = null; - }; - - onCreateClicked = e => { - e.preventDefault(); - this.props.onCreate(this.state.file || this.state.url || getAbsoluteHref(location.href, ducky)); - this.props.onClose(); - }; - - render() { - const { onCreate, onClose, ...other } = this.props; // eslint-disable-line no-unused-vars - - const cancelButton = ( - - - - ); - const uploadButton = ( - - - - ); - const filenameLabel = ; - const urlInput = ( - handleTextFieldFocus(e.target)} - onBlur={() => handleTextFieldBlur()} - onChange={this.onUrlChange} - type="url" - value={this.state.url} - /> - ); - - return ( - -
- {isMobile ? mobileInstructions : desktopInstructions} -
-
- (this.fileInput = f)} - className={styles.hideFileInput} - type="file" - onChange={this.onFileChange} - /> -
- {this.state.file ? filenameLabel : urlInput} - {this.state.url || this.state.fileName ? cancelButton : uploadButton} -
-
- - - -
- {this.state.attributionImage ? ( -
- -
- ) : null} -
-
-
-
- ); - } -} diff --git a/src/react-components/media-browser.js b/src/react-components/media-browser.js index 0d316eb963..af836a6d98 100644 --- a/src/react-components/media-browser.js +++ b/src/react-components/media-browser.js @@ -13,11 +13,14 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import configs from "../utils/configs"; import IfFeature from "./if-feature"; import styles from "../assets/stylesheets/media-browser.scss"; -import { pushHistoryPath, pushHistoryState, sluglessPath } from "../utils/history"; +import { pushHistoryPath, sluglessPath } from "../utils/history"; import { SOURCES } from "../storage/media-search-store"; import { handleTextFieldFocus, handleTextFieldBlur } from "../utils/focus-utils"; import { showFullScreenIfWasFullScreen } from "../utils/fullscreen"; import MediaTiles from "./media-tiles"; +import { AvatarUrlModalContainer } from "./room/AvatarUrlModalContainer"; +import { SceneUrlModalContainer } from "./room/SceneUrlModalContainer"; +import { ObjectUrlModalContainer } from "./room/ObjectUrlModalContainer"; const isMobile = AFRAME.utils.device.isMobile(); const isMobileVR = AFRAME.utils.device.isMobileVR(); @@ -83,7 +86,10 @@ class MediaBrowser extends Component { intl: PropTypes.object, hubChannel: PropTypes.object, onMediaSearchResultEntrySelected: PropTypes.func, - performConditionalSignIn: PropTypes.func + performConditionalSignIn: PropTypes.func, + showNonHistoriedDialog: PropTypes.func.isRequired, + scene: PropTypes.object.isRequired, + store: PropTypes.object.isRequired }; state = { query: "", facets: [], showNav: true, selectNextResult: false, clearStashedQueryOnClose: false }; @@ -229,11 +235,17 @@ class MediaBrowser extends Component { }; showCustomMediaDialog = source => { - const isSceneApiType = source === "scenes"; + const { scene, store, hubChannel } = this.props; const isAvatarApiType = source === "avatars"; this.pushExitMediaBrowserHistory(!isAvatarApiType); - const dialog = isSceneApiType ? "change_scene" : isAvatarApiType ? "avatar_url" : "create"; - pushHistoryState(this.props.history, "modal", dialog); + + if (source === "scenes") { + this.props.showNonHistoriedDialog(SceneUrlModalContainer, { hubChannel }); + } else if (isAvatarApiType) { + this.props.showNonHistoriedDialog(AvatarUrlModalContainer, { scene, store }); + } else { + this.props.showNonHistoriedDialog(ObjectUrlModalContainer, { scene }); + } }; close = () => { diff --git a/src/react-components/room/AvatarUrlModal.js b/src/react-components/room/AvatarUrlModal.js new file mode 100644 index 0000000000..79557380e9 --- /dev/null +++ b/src/react-components/room/AvatarUrlModal.js @@ -0,0 +1,40 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Modal } from "../modal/Modal"; +import { CloseButton } from "../input/CloseButton"; +import { TextInputField } from "../input/TextInputField"; +import { useForm } from "react-hook-form"; +import { Button } from "../input/Button"; +import { FormattedMessage } from "react-intl"; +import { Column } from "../layout/Column"; + +export function AvatarUrlModal({ onSubmit, onClose }) { + const { handleSubmit, register } = useForm(); + return ( + }> + + + Learn more about custom avatars + + } + /> + + + + ); +} + +AvatarUrlModal.propTypes = { + onSubmit: PropTypes.func, + onClose: PropTypes.func +}; diff --git a/src/react-components/room/AvatarUrlModal.stories.js b/src/react-components/room/AvatarUrlModal.stories.js new file mode 100644 index 0000000000..272e7430d8 --- /dev/null +++ b/src/react-components/room/AvatarUrlModal.stories.js @@ -0,0 +1,12 @@ +import React from "react"; +import { RoomLayout } from "../layout/RoomLayout"; +import { AvatarUrlModal } from "./AvatarUrlModal"; + +export default { + title: "AvatarUrlModal", + parameters: { + layout: "fullscreen" + } +}; + +export const Base = () => } />; diff --git a/src/react-components/room/AvatarUrlModalContainer.js b/src/react-components/room/AvatarUrlModalContainer.js new file mode 100644 index 0000000000..9634f41582 --- /dev/null +++ b/src/react-components/room/AvatarUrlModalContainer.js @@ -0,0 +1,21 @@ +import React, { useCallback } from "react"; +import PropTypes from "prop-types"; +import { AvatarUrlModal } from "./AvatarUrlModal"; + +export function AvatarUrlModalContainer({ store, scene, onClose }) { + const onSubmit = useCallback( + ({ url }) => { + store.update({ profile: { ...store.state.profile, ...{ avatarId: url } } }); + scene.emit("avatar_updated"); + }, + [store, scene] + ); + + return ; +} + +AvatarUrlModalContainer.propTypes = { + store: PropTypes.object.isRequired, + scene: PropTypes.object.isRequired, + onClose: PropTypes.func +}; diff --git a/src/react-components/room/ObjectUrlModal.js b/src/react-components/room/ObjectUrlModal.js new file mode 100644 index 0000000000..36ba6ef5d1 --- /dev/null +++ b/src/react-components/room/ObjectUrlModal.js @@ -0,0 +1,112 @@ +import React, { useCallback, useEffect } from "react"; +import PropTypes from "prop-types"; +import { Modal } from "../modal/Modal"; +import { CloseButton } from "../input/CloseButton"; +import { TextInputField } from "../input/TextInputField"; +import { useForm } from "react-hook-form"; +import { Button } from "../input/Button"; +import { FormattedMessage } from "react-intl"; +import { Column } from "../layout/Column"; +import { IconButton } from "../input/IconButton"; +import { ReactComponent as AttachIcon } from "../icons/Attach.svg"; +import styles from "./ObjectUrlModal.scss"; +import classNames from "classnames"; + +export function ObjectUrlModal({ showModelCollectionLink, modelCollectionUrl, onSubmit, onClose }) { + const { handleSubmit, register, watch, setValue } = useForm(); + + useEffect( + () => { + register("url"); + }, + [register] + ); + + const file = watch("file"); + const hasFile = file && file.length > 0; + const fileName = hasFile ? file[0].name : undefined; + + const onClear = useCallback( + () => { + if (hasFile) { + setValue("file", undefined); + } else { + setValue("url", ""); + } + }, + [hasFile, setValue] + ); + + const onChange = useCallback( + e => { + if (hasFile) { + return; + } + + setValue("url", e.target.value); + }, + [hasFile, setValue] + ); + + const url = watch("url", ""); + + const showCloseButton = hasFile || url.length > 0; + + return ( + }> + +

+ Upload or paste a URL to an image, video, model, or scene. Models can be found on{" "} + + Sketchfab + {" "} + and{" "} + + Google Poly + + {showModelCollectionLink && ( + <> + , or our{" "} + + collection + + + )}. +

+ + {showCloseButton && } + + + + + + } + description="Accepts glb, png, jpg, gif, mp4, and mp3 files" + /> + +
+
+ ); +} + +ObjectUrlModal.propTypes = { + isMobile: PropTypes.bool, + showModelCollectionLink: PropTypes.bool, + modelCollectionUrl: PropTypes.string, + onSubmit: PropTypes.func, + onClose: PropTypes.func +}; diff --git a/src/react-components/room/ObjectUrlModal.scss b/src/react-components/room/ObjectUrlModal.scss new file mode 100644 index 0000000000..92c6cdcfb7 --- /dev/null +++ b/src/react-components/room/ObjectUrlModal.scss @@ -0,0 +1,4 @@ +:local(.hidden) { + opacity: 0; + width: 0; +} \ No newline at end of file diff --git a/src/react-components/room/ObjectUrlModal.stories.js b/src/react-components/room/ObjectUrlModal.stories.js new file mode 100644 index 0000000000..617d6e8252 --- /dev/null +++ b/src/react-components/room/ObjectUrlModal.stories.js @@ -0,0 +1,12 @@ +import React from "react"; +import { RoomLayout } from "../layout/RoomLayout"; +import { ObjectUrlModal } from "./ObjectUrlModal"; + +export default { + title: "ObjectUrlModal", + parameters: { + layout: "fullscreen" + } +}; + +export const Base = () => } />; diff --git a/src/react-components/room/ObjectUrlModalContainer.js b/src/react-components/room/ObjectUrlModalContainer.js new file mode 100644 index 0000000000..ef7fd0a4fc --- /dev/null +++ b/src/react-components/room/ObjectUrlModalContainer.js @@ -0,0 +1,32 @@ +import React, { useCallback } from "react"; +import PropTypes from "prop-types"; +import { ObjectUrlModal } from "./ObjectUrlModal"; +import configs from "../../utils/configs"; +import ducky from "../../assets/models/DuckyMesh.glb"; + +const isMobile = AFRAME.utils.device.isMobile() || AFRAME.utils.device.isMobileVR(); + +export function ObjectUrlModalContainer({ scene, onClose }) { + const onSubmit = useCallback( + ({ file, url }) => { + scene.emit("add_media", (file && file.length > 0 && file[0]) || url || ducky); + onClose(); + }, + [scene, onClose] + ); + + return ( + + ); +} + +ObjectUrlModalContainer.propTypes = { + scene: PropTypes.object.isRequired, + onClose: PropTypes.func +}; diff --git a/src/react-components/room/SceneUrlModal.js b/src/react-components/room/SceneUrlModal.js new file mode 100644 index 0000000000..7058509436 --- /dev/null +++ b/src/react-components/room/SceneUrlModal.js @@ -0,0 +1,60 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Modal } from "../modal/Modal"; +import { CloseButton } from "../input/CloseButton"; +import { TextInputField } from "../input/TextInputField"; +import { useForm } from "react-hook-form"; +import { Button } from "../input/Button"; +import { FormattedMessage } from "react-intl"; +import { Column } from "../layout/Column"; + +export function SceneUrlModal({ enableSpoke, onValidateUrl, onSubmit, onClose }) { + const { isSubmitting, handleSubmit, register, errors } = useForm(); + return ( + }> + +

+ Paste a URL to a{" "} + {enableSpoke && ( + + + + )}{" "} + scene or a URL to a{" "} + + GLB + . +

+ + + {enableSpoke && ( + <> +

+ +

+ + + )} +
+
+ ); +} + +SceneUrlModal.propTypes = { + enableSpoke: PropTypes.bool, + onSubmit: PropTypes.func, + onClose: PropTypes.func, + onValidateUrl: PropTypes.isRequired +}; diff --git a/src/react-components/room/SceneUrlModal.stories.js b/src/react-components/room/SceneUrlModal.stories.js new file mode 100644 index 0000000000..f67bb6faa3 --- /dev/null +++ b/src/react-components/room/SceneUrlModal.stories.js @@ -0,0 +1,14 @@ +import React from "react"; +import { RoomLayout } from "../layout/RoomLayout"; +import { SceneUrlModal } from "./SceneUrlModal"; + +export default { + title: "SceneUrlModal", + parameters: { + layout: "fullscreen" + } +}; + +export const ValidUrl = () => true} />} />; + +export const InvalidUrl = () => "Invalid Scene Url"} />} />; diff --git a/src/react-components/room/SceneUrlModalContainer.js b/src/react-components/room/SceneUrlModalContainer.js new file mode 100644 index 0000000000..d6bf599d21 --- /dev/null +++ b/src/react-components/room/SceneUrlModalContainer.js @@ -0,0 +1,40 @@ +import React, { useCallback } from "react"; +import PropTypes from "prop-types"; +import { SceneUrlModal } from "./SceneUrlModal"; +import configs from "../../utils/configs"; +import { isValidSceneUrl } from "../../utils/scene-url-utils"; +import { useIntl } from "react-intl"; + +export function SceneUrlModalContainer({ hubChannel, onClose }) { + const intl = useIntl(); + + const onValidateUrl = useCallback( + async url => { + const valid = await isValidSceneUrl(url.trim()); + return valid || intl.formatMessage("invalid-scene-url"); + }, + [intl] + ); + + const onSubmit = useCallback( + ({ url }) => { + hubChannel.updateScene(url); + onClose(); + }, + [hubChannel, onClose] + ); + + return ( + + ); +} + +SceneUrlModalContainer.propTypes = { + hubChannel: PropTypes.object.isRequired, + onClose: PropTypes.func +}; diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index 81aa77f00d..a070e626d3 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -26,9 +26,6 @@ import { getMessages } from "../utils/i18n"; import ProfileEntryPanel from "./profile-entry-panel"; import MediaBrowser from "./media-browser"; -import CreateObjectDialog from "./create-object-dialog.js"; -import ChangeSceneDialog from "./change-scene-dialog.js"; -import AvatarUrlDialog from "./avatar-url-dialog.js"; import InviteDialog from "./invite-dialog.js"; import CloseRoomDialog from "./close-room-dialog.js"; import Tip from "./tip.js"; @@ -817,19 +814,6 @@ class UIRoot extends Component { this.setState({ showShareDialog: !this.state.showShareDialog }); }; - createObject = media => { - this.props.scene.emit("add_media", media); - }; - - changeScene = url => { - this.props.hubChannel.updateScene(url); - }; - - setAvatarUrl = url => { - this.props.store.update({ profile: { ...this.props.store.state.profile, ...{ avatarId: url } } }); - this.props.scene.emit("avatar_updated"); - }; - closeDialog = () => { if (this.state.dialog) { this.setState({ dialog: null }); @@ -1493,6 +1477,9 @@ class UIRoot extends Component { } }} performConditionalSignIn={this.props.performConditionalSignIn} + showNonHistoriedDialog={this.showNonHistoriedDialog} + store={this.props.store} + scene={this.props.scene} /> )} this.props.hubChannel.closeHub() }) } /> - this.renderDialog(CreateObjectDialog, { onCreate: this.createObject })} - /> - this.renderDialog(ChangeSceneDialog, { onChange: this.changeScene })} - /> - this.renderDialog(AvatarUrlDialog, { onChange: this.setAvatarUrl })} - /> Date: Fri, 13 Nov 2020 13:42:07 -0800 Subject: [PATCH 03/31] Fix ObjectUrlModal --- src/react-components/room/ObjectUrlModal.js | 4 +++- src/react-components/room/ObjectUrlModal.scss | 1 + src/react-components/room/PlacePopoverContainer.js | 9 +++++---- src/react-components/ui-root.js | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/react-components/room/ObjectUrlModal.js b/src/react-components/room/ObjectUrlModal.js index 36ba6ef5d1..b5c9b35656 100644 --- a/src/react-components/room/ObjectUrlModal.js +++ b/src/react-components/room/ObjectUrlModal.js @@ -26,6 +26,8 @@ export function ObjectUrlModal({ showModelCollectionLink, modelCollectionUrl, on const hasFile = file && file.length > 0; const fileName = hasFile ? file[0].name : undefined; + console.log(file, hasFile, fileName); + const onClear = useCallback( () => { if (hasFile) { @@ -81,7 +83,7 @@ export function ObjectUrlModal({ showModelCollectionLink, modelCollectionUrl, on name="url" label="Object URL or File" placeholder="https://example.com/avatar.glb" - type="url" + type={hasFile ? "text" : "url"} value={fileName || url || ""} onChange={onChange} afterInput={ diff --git a/src/react-components/room/ObjectUrlModal.scss b/src/react-components/room/ObjectUrlModal.scss index 92c6cdcfb7..1177687245 100644 --- a/src/react-components/room/ObjectUrlModal.scss +++ b/src/react-components/room/ObjectUrlModal.scss @@ -1,4 +1,5 @@ :local(.hidden) { opacity: 0; width: 0; + height: 0; } \ No newline at end of file diff --git a/src/react-components/room/PlacePopoverContainer.js b/src/react-components/room/PlacePopoverContainer.js index 30353300e6..c510662def 100644 --- a/src/react-components/room/PlacePopoverContainer.js +++ b/src/react-components/room/PlacePopoverContainer.js @@ -10,8 +10,9 @@ import { ReactComponent as AvatarIcon } from "../icons/Avatar.svg"; import { ReactComponent as SceneIcon } from "../icons/Scene.svg"; import { ReactComponent as UploadIcon } from "../icons/Upload.svg"; import { PlacePopoverButton } from "./PlacePopover"; +import { ObjectUrlModalContainer } from "./ObjectUrlModalContainer"; -export function PlacePopoverContainer({ scene, mediaSearchStore, pushHistoryState, hubChannel }) { +export function PlacePopoverContainer({ scene, mediaSearchStore, showNonHistoriedDialog, hubChannel }) { const [items, setItems] = useState([]); useEffect( @@ -79,7 +80,7 @@ export function PlacePopoverContainer({ scene, mediaSearchStore, pushHistoryStat icon: UploadIcon, color: "green", label: "Upload", - onSelect: () => pushHistoryState("modal", "create") + onSelect: () => showNonHistoriedDialog(ObjectUrlModalContainer, { scene }) } ]; } @@ -106,7 +107,7 @@ export function PlacePopoverContainer({ scene, mediaSearchStore, pushHistoryStat scene.removeEventListener("stateremoved", onSceneStateChange); }; }, - [hubChannel, mediaSearchStore, pushHistoryState, scene] + [hubChannel, mediaSearchStore, showNonHistoriedDialog, scene] ); return ; @@ -116,5 +117,5 @@ PlacePopoverContainer.propTypes = { hubChannel: PropTypes.object.isRequired, scene: PropTypes.object.isRequired, mediaSearchStore: PropTypes.object.isRequired, - pushHistoryState: PropTypes.func.isRequired + showNonHistoriedDialog: PropTypes.func.isRequired }; diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index a070e626d3..541ff134d5 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -1719,7 +1719,7 @@ class UIRoot extends Component { scene={this.props.scene} hubChannel={this.props.hubChannel} mediaSearchStore={this.props.mediaSearchStore} - pushHistoryState={this.pushHistoryState} + showNonHistoriedDialog={this.showNonHistoriedDialog} /> From 1447fabba0615faa58c7f11382faaf084216dde5 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Fri, 13 Nov 2020 14:02:44 -0800 Subject: [PATCH 04/31] Fix bugs with media-browser modals --- src/hub.js | 3 - src/react-components/auth/SignInModal.js | 2 +- src/react-components/media-browser.js | 3 + .../room/AvatarUrlModalContainer.js | 3 +- src/react-components/ui-root.js | 89 ++++++++++--------- 5 files changed, 52 insertions(+), 48 deletions(-) diff --git a/src/hub.js b/src/hub.js index d1e94adb43..e0c8d3b88d 100644 --- a/src/hub.js +++ b/src/hub.js @@ -806,15 +806,12 @@ document.addEventListener("DOMContentLoaded", async () => { const performConditionalSignIn = async (predicate, action, messageId, onFailure) => { if (predicate()) return action(); - const signInContinueTextId = scene.is("vr-mode") ? "entry.return-to-vr" : "dialog.close"; - await handleExitTo2DInterstitial(true, () => remountUI({ showSignInDialog: false })); remountUI({ showSignInDialog: true, signInMessageId: `sign-in.${messageId}`, signInCompleteMessageId: `sign-in.${messageId}-complete`, - signInContinueTextId, onContinueAfterSignIn: async () => { remountUI({ showSignInDialog: false }); let actionError = null; diff --git a/src/react-components/auth/SignInModal.js b/src/react-components/auth/SignInModal.js index d56adba026..2e43216acd 100644 --- a/src/react-components/auth/SignInModal.js +++ b/src/react-components/auth/SignInModal.js @@ -118,7 +118,7 @@ export function SignInComplete({ message, continueText, onContinue }) {

{message}

-

{continueText}

+ {continueText &&

{continueText}

} diff --git a/src/react-components/media-browser.js b/src/react-components/media-browser.js index af836a6d98..5e54bc48a6 100644 --- a/src/react-components/media-browser.js +++ b/src/react-components/media-browser.js @@ -239,6 +239,8 @@ class MediaBrowser extends Component { const isAvatarApiType = source === "avatars"; this.pushExitMediaBrowserHistory(!isAvatarApiType); + console.log("showCustomMediaDialog", source); + if (source === "scenes") { this.props.showNonHistoriedDialog(SceneUrlModalContainer, { hubChannel }); } else if (isAvatarApiType) { @@ -284,6 +286,7 @@ class MediaBrowser extends Component { if (isAvatarApiType) { this.showCustomMediaDialog(urlSource); } else { + console.log(isSceneApiType, this.props.hubChannel.can("update_hub"), urlSource); this.props.performConditionalSignIn( () => !isSceneApiType || this.props.hubChannel.can("update_hub"), () => this.showCustomMediaDialog(urlSource), diff --git a/src/react-components/room/AvatarUrlModalContainer.js b/src/react-components/room/AvatarUrlModalContainer.js index 9634f41582..3bef246f3c 100644 --- a/src/react-components/room/AvatarUrlModalContainer.js +++ b/src/react-components/room/AvatarUrlModalContainer.js @@ -7,8 +7,9 @@ export function AvatarUrlModalContainer({ store, scene, onClose }) { ({ url }) => { store.update({ profile: { ...store.state.profile, ...{ avatarId: url } } }); scene.emit("avatar_updated"); + onClose(); }, - [store, scene] + [store, scene, onClose] ); return ; diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index 541ff134d5..b6f3caf164 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -1433,55 +1433,58 @@ class UIRoot extends Component { onLoadClicked={this.props.onPreloadLoadClicked} /> )} - ( - { - if (props.location.state.detail && props.location.state.detail.returnToProfile) { - this.props.history.goBack(); + {!this.state.dialog && ( + ( + { + if (props.location.state.detail && props.location.state.detail.returnToProfile) { + this.props.history.goBack(); + } else { + this.props.history.goBack(); + // We are returning to the media browser. Trigger an update so that the filter switches to + // my-avatars, now that we've saved an avatar. + this.props.mediaSearchStore.sourceNavigateWithNoNav("avatars", "use"); + } + this.props.onAvatarSaved(); + }} + onClose={() => this.props.history.goBack()} + store={this.props.store} + debug={avatarEditorDebug} + avatarId={props.location.state.detail && props.location.state.detail.avatarId} + hideDelete={props.location.state.detail && props.location.state.detail.hideDelete} + /> + )} + /> + )} + {!this.state.dialog && + showMediaBrowser && ( + { + if (entry.type === "room") { + this.showNonHistoriedDialog(LeaveRoomModal, { + destinationUrl: entry.url, + reason: LeaveReason.joinRoom + }); } else { - this.props.history.goBack(); - // We are returning to the media browser. Trigger an update so that the filter switches to - // my-avatars, now that we've saved an avatar. - this.props.mediaSearchStore.sourceNavigateWithNoNav("avatars", "use"); + this.props.onMediaSearchResultEntrySelected(entry, selectAction); } - this.props.onAvatarSaved(); }} - onClose={() => this.props.history.goBack()} + performConditionalSignIn={this.props.performConditionalSignIn} + showNonHistoriedDialog={this.showNonHistoriedDialog} store={this.props.store} - debug={avatarEditorDebug} - avatarId={props.location.state.detail && props.location.state.detail.avatarId} - hideDelete={props.location.state.detail && props.location.state.detail.hideDelete} + scene={this.props.scene} /> )} - /> - {showMediaBrowser && ( - { - if (entry.type === "room") { - this.showNonHistoriedDialog(LeaveRoomModal, { - destinationUrl: entry.url, - reason: LeaveReason.joinRoom - }); - } else { - this.props.onMediaSearchResultEntrySelected(entry, selectAction); - } - }} - performConditionalSignIn={this.props.performConditionalSignIn} - showNonHistoriedDialog={this.showNonHistoriedDialog} - store={this.props.store} - scene={this.props.scene} - /> - )} Date: Fri, 13 Nov 2020 14:48:46 -0800 Subject: [PATCH 05/31] Migrate client-info-dialog --- src/react-components/client-info-dialog.js | 140 ---------------- .../room/PeopleSidebarContainer.js | 4 +- .../room/UserProfileSidebarContainer.js | 149 ++++++++++++++++++ src/react-components/ui-root.js | 4 +- 4 files changed, 153 insertions(+), 144 deletions(-) delete mode 100644 src/react-components/client-info-dialog.js create mode 100644 src/react-components/room/UserProfileSidebarContainer.js diff --git a/src/react-components/client-info-dialog.js b/src/react-components/client-info-dialog.js deleted file mode 100644 index e1262fa818..0000000000 --- a/src/react-components/client-info-dialog.js +++ /dev/null @@ -1,140 +0,0 @@ -import React, { Component } from "react"; -import PropTypes from "prop-types"; -import { PromoteClientModal } from "./room/PromoteClientModal"; -import { getAvatarThumbnailUrl } from "../utils/avatar-utils"; -import { UserProfileSidebar } from "./room/UserProfileSidebar.js"; - -export default class ClientInfoDialog extends Component { - static propTypes = { - user: PropTypes.object.isRequired, - hubChannel: PropTypes.object, - performConditionalSignIn: PropTypes.func, - showBackButton: PropTypes.bool, - onBack: PropTypes.func, - onClose: PropTypes.func, - onCloseDialog: PropTypes.func.isRequired, - showNonHistoriedDialog: PropTypes.func - }; - - state = { - avatarThumbnailUrl: null - }; - - kick() { - const { user, performConditionalSignIn, hubChannel, onClose, onBack } = this.props; - - performConditionalSignIn( - () => hubChannel.can("kick_users"), - async () => await hubChannel.kick(user.id), - "kick-user" - ); - - if (onClose) { - onClose(); - } else if (onBack) { - onBack(); - } - } - - hide() { - const { user, hubChannel } = this.props; - hubChannel.hide(user.id); - - this.forceUpdate(); - } - - mute() { - const { user, performConditionalSignIn, hubChannel } = this.props; - - performConditionalSignIn( - () => hubChannel.can("mute_users"), - async () => await hubChannel.mute(user.id), - "mute-user" - ); - - this.forceUpdate(); - } - - addOwner() { - const { user, performConditionalSignIn, hubChannel, onCloseDialog } = this.props; - const { profile } = this.props.user; - - performConditionalSignIn( - () => hubChannel.can("update_roles"), - async () => { - this.props.showNonHistoriedDialog(PromoteClientModal, { - displayName: profile.displayName, - onConfirm: () => { - hubChannel.addOwner(user.id); - onCloseDialog(); - } - }); - }, - "add-owner" - ); - - this.forceUpdate(); - } - - removeOwner() { - const { user, performConditionalSignIn, hubChannel } = this.props; - - performConditionalSignIn( - () => hubChannel.can("update_roles"), - async () => await hubChannel.removeOwner(user.id), - "remove-owner" - ); - - this.forceUpdate(); - } - - unhide() { - const { user, hubChannel } = this.props; - hubChannel.unhide(user.id); - this.forceUpdate(); - } - - componentDidMount() { - const { profile } = this.props.user; - if (profile.avatarId) { - getAvatarThumbnailUrl(profile.avatarId).then(avatarThumbnailUrl => this.setState({ avatarThumbnailUrl })); - } - } - - render() { - const { profile, roles } = this.props.user; - - const { displayName, identityName } = profile; - const { hubChannel, user, showBackButton, onClose, onBack } = this.props; - const mayKick = hubChannel.canOrWillIfCreator("kick_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(user.id); - - return ( - } - 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} - /> - ); - } -} diff --git a/src/react-components/room/PeopleSidebarContainer.js b/src/react-components/room/PeopleSidebarContainer.js index 930c62f788..4e7c1df725 100644 --- a/src/react-components/room/PeopleSidebarContainer.js +++ b/src/react-components/room/PeopleSidebarContainer.js @@ -3,7 +3,7 @@ import PropTypes from "prop-types"; import { PeopleSidebar } from "./PeopleSidebar"; import { getMicrophonePresences } from "../../utils/microphone-presence"; import ProfileEntryPanel from "../profile-entry-panel"; -import ClientInfoDialog from "../client-info-dialog"; +import { UserProfileSidebarContainer } from "./UserProfileSidebarContainer"; export function userFromPresence(sessionId, presence, micPresences, mySessionId) { const meta = presence.metas[presence.metas.length - 1]; @@ -110,7 +110,7 @@ export function PeopleSidebarContainer({ ); } else { return ( - { + if (avatarId) { + getAvatarThumbnailUrl(avatarId).then(avatarThumbnailUrl => setAvatarThumbnailUrl(avatarThumbnailUrl)); + } + }, + [avatarId, setAvatarThumbnailUrl] + ); + + const addOwner = useCallback( + () => { + performConditionalSignIn( + () => hubChannel.can("update_roles"), + async () => { + showNonHistoriedDialog(PromoteClientModal, { + displayName, + onConfirm: async () => { + setIsOwner(true); + await hubChannel.addOwner(userId); + onCloseDialog(); + } + }); + }, + "add-owner" + ); + }, + [performConditionalSignIn, hubChannel, showNonHistoriedDialog, userId, onCloseDialog, displayName] + ); + + const removeOwner = useCallback( + () => { + performConditionalSignIn( + () => hubChannel.can("update_roles"), + async () => { + setIsOwner(false); + await hubChannel.removeOwner(userId); + }, + "remove-owner" + ); + }, + [performConditionalSignIn, hubChannel, userId] + ); + + const toggleHidden = useCallback( + () => { + if (isHidden) { + hubChannel.hide(userId); + } else { + hubChannel.unhide(userId); + } + + setIsHidden(!isHidden); + }, + [isHidden, userId, hubChannel] + ); + + const mute = useCallback( + () => { + performConditionalSignIn( + () => hubChannel.can("mute_users"), + async () => await hubChannel.mute(userId), + "mute-user" + ); + }, + [performConditionalSignIn, hubChannel, userId] + ); + + const kick = useCallback( + () => { + performConditionalSignIn( + () => hubChannel.can("kick_users"), + async () => await hubChannel.kick(userId), + "kick-user" + ); + + if (onClose) { + onClose(); + } else if (onBack) { + onBack(); + } + }, + [performConditionalSignIn, hubChannel, userId, onClose, onBack] + ); + + return ( + } + isSignedIn={isSignedIn} + canPromote={mayAddOwner} + onPromote={addOwner} + canDemote={mayRemoveOwner} + onDemote={removeOwner} + isHidden={isHidden} + onToggleHidden={toggleHidden} + canMute={mayMute} + onMute={mute} + canKick={mayKick} + onKick={kick} + showBackButton={showBackButton} + onClose={onClose} + onBack={onBack} + /> + ); +} + +UserProfileSidebarContainer.propTypes = { + user: PropTypes.object.isRequired, + hubChannel: PropTypes.object, + performConditionalSignIn: PropTypes.func, + showBackButton: PropTypes.bool, + onBack: PropTypes.func, + onClose: PropTypes.func, + onCloseDialog: PropTypes.func.isRequired, + showNonHistoriedDialog: PropTypes.func +}; diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index b6f3caf164..5d4e6c8cd3 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -32,7 +32,6 @@ import Tip from "./tip.js"; import WebRTCScreenshareUnsupportedDialog from "./webrtc-screenshare-unsupported-dialog.js"; import WebVRRecommendDialog from "./webvr-recommend-dialog.js"; import FeedbackDialog from "./feedback-dialog.js"; -import ClientInfoDialog from "./client-info-dialog.js"; import OAuthDialog from "./oauth-dialog.js"; import TweetDialog from "./tweet-dialog.js"; import EntryStartPanel from "./entry-start-panel.js"; @@ -96,6 +95,7 @@ import { RoomSidebar } from "./room/RoomSidebar"; import { RoomSettingsSidebarContainer } from "./room/RoomSettingsSidebarContainer"; import { AutoExitWarningModal, AutoExitReason } from "./room/AutoExitWarningModal"; import { ExitReason } from "./room/ExitedRoomScreen"; +import { UserProfileSidebarContainer } from "./room/UserProfileSidebarContainer"; const avatarEditorDebug = qsTruthy("avatarEditorDebug"); @@ -1661,7 +1661,7 @@ class UIRoot extends Component { /> )} {this.state.sidebarId === "user" && ( - Date: Fri, 13 Nov 2020 14:56:16 -0800 Subject: [PATCH 06/31] Remove console.log --- src/react-components/room/ObjectUrlModal.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/react-components/room/ObjectUrlModal.js b/src/react-components/room/ObjectUrlModal.js index b5c9b35656..6884b74def 100644 --- a/src/react-components/room/ObjectUrlModal.js +++ b/src/react-components/room/ObjectUrlModal.js @@ -26,8 +26,6 @@ export function ObjectUrlModal({ showModelCollectionLink, modelCollectionUrl, on const hasFile = file && file.length > 0; const fileName = hasFile ? file[0].name : undefined; - console.log(file, hasFile, fileName); - const onClear = useCallback( () => { if (hasFile) { From 850e2cb539f5121dad8843622c0d8282fa7805cc Mon Sep 17 00:00:00 2001 From: Robert Long Date: Fri, 13 Nov 2020 14:56:45 -0800 Subject: [PATCH 07/31] Migrate CloseRoomModal --- src/assets/stylesheets/close-room-dialog.scss | 16 --------- src/react-components/close-room-dialog.js | 35 ------------------- src/react-components/room/CloseRoomModal.js | 30 ++++++++++++++++ src/react-components/ui-root.js | 15 ++++---- 4 files changed, 36 insertions(+), 60 deletions(-) delete mode 100644 src/assets/stylesheets/close-room-dialog.scss delete mode 100644 src/react-components/close-room-dialog.js create mode 100644 src/react-components/room/CloseRoomModal.js diff --git a/src/assets/stylesheets/close-room-dialog.scss b/src/assets/stylesheets/close-room-dialog.scss deleted file mode 100644 index 74ad2d6967..0000000000 --- a/src/assets/stylesheets/close-room-dialog.scss +++ /dev/null @@ -1,16 +0,0 @@ -@import 'shared'; - -:local(.confirm-button) { - @extend %action-button; - margin-top: 8px; -} - -:local(.cancel-button) { - @extend %action-button-secondary; - margin-top: 8px; -} - -:local(.message) { - margin-bottom: 12px; - white-space: pre-wrap; -} diff --git a/src/react-components/close-room-dialog.js b/src/react-components/close-room-dialog.js deleted file mode 100644 index 91135864f8..0000000000 --- a/src/react-components/close-room-dialog.js +++ /dev/null @@ -1,35 +0,0 @@ -import React, { Component } from "react"; -import PropTypes from "prop-types"; -import { FormattedMessage } from "react-intl"; - -import styles from "../assets/stylesheets/close-room-dialog.scss"; -import DialogContainer from "./dialog-container"; - -export default class CloseRoomDialog extends Component { - static propTypes = { - onConfirm: PropTypes.func, - onClose: PropTypes.func - }; - - render() { - return ( - -
- -
- - -
- ); - } -} diff --git a/src/react-components/room/CloseRoomModal.js b/src/react-components/room/CloseRoomModal.js new file mode 100644 index 0000000000..601b0c29c2 --- /dev/null +++ b/src/react-components/room/CloseRoomModal.js @@ -0,0 +1,30 @@ +import React from "react"; +import { FormattedMessage } from "react-intl"; +import PropTypes from "prop-types"; +import { Modal } from "../modal/Modal"; +import { CloseButton } from "../input/CloseButton"; +import { Button } from "../input/Button"; +import { Column } from "../layout/Column"; + +export function CloseRoomModal({ onClose, onConfirm }) { + return ( + }> + +

+ +

+ + +
+
+ ); +} + +CloseRoomModal.propTypes = { + onConfirm: PropTypes.func, + onClose: PropTypes.func +}; diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index 5d4e6c8cd3..904770eabf 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -96,6 +96,7 @@ import { RoomSettingsSidebarContainer } from "./room/RoomSettingsSidebarContaine import { AutoExitWarningModal, AutoExitReason } from "./room/AutoExitWarningModal"; import { ExitReason } from "./room/ExitedRoomScreen"; import { UserProfileSidebarContainer } from "./room/UserProfileSidebarContainer"; +import { CloseRoomModal } from "./room/CloseRoomModal"; const avatarEditorDebug = qsTruthy("avatarEditorDebug"); @@ -1351,7 +1352,11 @@ class UIRoot extends Component { this.props.performConditionalSignIn( () => this.props.hubChannel.can("update_hub"), () => { - this.pushHistoryState("modal", "close_room"); + this.showNonHistoriedDialog(CloseRoomModal, { + onConfirm: () => { + this.props.hubChannel.closeHub(); + } + }); }, "close-room" ) @@ -1510,14 +1515,6 @@ class UIRoot extends Component { )} - - this.renderDialog(CloseRoomDialog, { onConfirm: () => this.props.hubChannel.closeHub() }) - } - /> Date: Fri, 13 Nov 2020 15:01:48 -0800 Subject: [PATCH 08/31] Remove unused ProfileInfoHeader component --- src/react-components/profile-info-header.js | 35 --------------------- 1 file changed, 35 deletions(-) delete mode 100644 src/react-components/profile-info-header.js diff --git a/src/react-components/profile-info-header.js b/src/react-components/profile-info-header.js deleted file mode 100644 index 9b5b54ce5f..0000000000 --- a/src/react-components/profile-info-header.js +++ /dev/null @@ -1,35 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faQuestion } from "@fortawesome/free-solid-svg-icons/faQuestion"; -import { WithHoverSound } from "./wrap-with-audio"; - -export const ProfileInfoHeader = props => ( -
-
- - - -
-
- - - - -
- {props.name} -
-
-
-
-); - -ProfileInfoHeader.propTypes = { - onClickName: PropTypes.func, - onClickHelp: PropTypes.func, - name: PropTypes.string -}; From 8de25bd5f67b58bf2857a9312a1d67904453dace Mon Sep 17 00:00:00 2001 From: Robert Long Date: Fri, 13 Nov 2020 15:02:30 -0800 Subject: [PATCH 09/31] Remove reference to CloseRoomDialog --- src/react-components/ui-root.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index 904770eabf..09c9a5393a 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -27,7 +27,6 @@ import ProfileEntryPanel from "./profile-entry-panel"; import MediaBrowser from "./media-browser"; import InviteDialog from "./invite-dialog.js"; -import CloseRoomDialog from "./close-room-dialog.js"; import Tip from "./tip.js"; import WebRTCScreenshareUnsupportedDialog from "./webrtc-screenshare-unsupported-dialog.js"; import WebVRRecommendDialog from "./webvr-recommend-dialog.js"; From ba564b6456185f61b87a415f234fe3f34c201b92 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Fri, 13 Nov 2020 15:03:22 -0800 Subject: [PATCH 10/31] Add CloseRoomModal story --- src/react-components/room/CloseRoomModal.stories.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/react-components/room/CloseRoomModal.stories.js diff --git a/src/react-components/room/CloseRoomModal.stories.js b/src/react-components/room/CloseRoomModal.stories.js new file mode 100644 index 0000000000..b2324009db --- /dev/null +++ b/src/react-components/room/CloseRoomModal.stories.js @@ -0,0 +1,12 @@ +import React from "react"; +import { RoomLayout } from "../layout/RoomLayout"; +import { CloseRoomModal } from "./CloseRoomModal"; + +export default { + title: "CloseRoomModal", + parameters: { + layout: "fullscreen" + } +}; + +export const Base = () => } />; From bdb8fede4e336c07cf0143fdf750e371e2b03e2d Mon Sep 17 00:00:00 2001 From: Robert Long Date: Fri, 13 Nov 2020 15:15:30 -0800 Subject: [PATCH 11/31] Migrate to WebVRUnsupportedModal --- .../room/WebVRUnsupportedModal.js | 35 +++++++++++++++++++ .../room/WebVRUnsupportedModal.stories.js | 12 +++++++ src/react-components/ui-root.js | 10 ++---- .../webvr-recommend-dialog.js | 33 ----------------- 4 files changed, 49 insertions(+), 41 deletions(-) create mode 100644 src/react-components/room/WebVRUnsupportedModal.js create mode 100644 src/react-components/room/WebVRUnsupportedModal.stories.js delete mode 100644 src/react-components/webvr-recommend-dialog.js diff --git a/src/react-components/room/WebVRUnsupportedModal.js b/src/react-components/room/WebVRUnsupportedModal.js new file mode 100644 index 0000000000..29fd2e0da0 --- /dev/null +++ b/src/react-components/room/WebVRUnsupportedModal.js @@ -0,0 +1,35 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Modal } from "../modal/Modal"; +import { CloseButton } from "../input/CloseButton"; +import { Button } from "../input/Button"; +import { Column } from "../layout/Column"; + +export function WebVRUnsupportedModal({ onClose }) { + return ( + }> + +

{"WebVR isn't supported in this browser, to enter with Oculus or SteamVR, use Firefox."}

+ + + For a list of browsers with experimental VR support, visit{" "} + + WebVR Rocks + + +
+
+ ); +} + +WebVRUnsupportedModal.propTypes = { + onClose: PropTypes.func +}; diff --git a/src/react-components/room/WebVRUnsupportedModal.stories.js b/src/react-components/room/WebVRUnsupportedModal.stories.js new file mode 100644 index 0000000000..0278cd8e9c --- /dev/null +++ b/src/react-components/room/WebVRUnsupportedModal.stories.js @@ -0,0 +1,12 @@ +import React from "react"; +import { RoomLayout } from "../layout/RoomLayout"; +import { WebVRUnsupportedModal } from "./WebVRUnsupportedModal"; + +export default { + title: "WebVRUnsupportedModal", + parameters: { + layout: "fullscreen" + } +}; + +export const Base = () => } />; diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index 09c9a5393a..eb383a1733 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -29,7 +29,6 @@ import MediaBrowser from "./media-browser"; import InviteDialog from "./invite-dialog.js"; import Tip from "./tip.js"; import WebRTCScreenshareUnsupportedDialog from "./webrtc-screenshare-unsupported-dialog.js"; -import WebVRRecommendDialog from "./webvr-recommend-dialog.js"; import FeedbackDialog from "./feedback-dialog.js"; import OAuthDialog from "./oauth-dialog.js"; import TweetDialog from "./tweet-dialog.js"; @@ -96,6 +95,7 @@ import { AutoExitWarningModal, AutoExitReason } from "./room/AutoExitWarningModa import { ExitReason } from "./room/ExitedRoomScreen"; import { UserProfileSidebarContainer } from "./room/UserProfileSidebarContainer"; import { CloseRoomModal } from "./room/CloseRoomModal"; +import { WebVRUnsupportedModal } from "./room/WebVRUnsupportedModal"; const avatarEditorDebug = qsTruthy("avatarEditorDebug"); @@ -582,7 +582,7 @@ class UIRoot extends Component { if (this.props.forcedVREntryType || this.props.availableVREntryTypes.generic !== VR_DEVICE_AVAILABILITY.maybe) { await this.performDirectEntryFlow(true); } else { - this.pushHistoryState("modal", "webvr"); + this.showNonHistoriedDialog(WebVRUnsupportedModal); } }; @@ -1514,12 +1514,6 @@ class UIRoot extends Component { )} - this.renderDialog(WebVRRecommendDialog)} - /> -
-

To enter with Oculus or SteamVR, you can use Firefox.

- - - Download Firefox - - -

- For a list of browsers with experimental VR support, visit{" "} - - - WebVR Rocks - - -

-
- - ); - } -} From ba9d0d9ada8ee024f213d2efb9a85154135f5918 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Fri, 13 Nov 2020 15:17:08 -0800 Subject: [PATCH 12/31] Remove unused WebRTCScreenShareUnsupported dialog --- src/react-components/ui-root.js | 11 ------ .../webrtc-screenshare-unsupported-dialog.js | 38 ------------------- 2 files changed, 49 deletions(-) delete mode 100644 src/react-components/webrtc-screenshare-unsupported-dialog.js diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index eb383a1733..8a1b3dacca 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -28,7 +28,6 @@ import MediaBrowser from "./media-browser"; import InviteDialog from "./invite-dialog.js"; import Tip from "./tip.js"; -import WebRTCScreenshareUnsupportedDialog from "./webrtc-screenshare-unsupported-dialog.js"; import FeedbackDialog from "./feedback-dialog.js"; import OAuthDialog from "./oauth-dialog.js"; import TweetDialog from "./tweet-dialog.js"; @@ -853,10 +852,6 @@ class UIRoot extends Component { this.setState({ signedIn: false }); }; - showWebRTCScreenshareUnsupportedDialog = () => { - this.pushHistoryState("modal", "webrtc-screenshare"); - }; - onMiniInviteClicked = () => { const link = `https://${configs.SHORTLINK_DOMAIN}/${this.props.hub.hub_id}`; @@ -1514,12 +1509,6 @@ class UIRoot extends Component { )} - this.renderDialog(WebRTCScreenshareUnsupportedDialog)} - /> -
-

- Your browser doesn't seem to support screen sharing. -
- To share your screen in ${getMessages()["app-name"]}, you can use Firefox. -

- - Download Firefox - -

- You can try  - - testing WebRTC screen sharing -  in your browser. -

-
- - ); - } -} From 29b6f4fe327ce135781025b333a9139081ce22b9 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Mon, 16 Nov 2020 13:53:23 -0800 Subject: [PATCH 13/31] Migrate Tweet Modal --- src/components/tweet-media-button.js | 10 +- src/hub.js | 53 +-------- src/react-components/room/TweetEditorModal.js | 92 ++++++++++++++++ .../room/TweetEditorModal.scss | 61 +++++++++++ .../room/TweetEditorModal.stories.js | 53 +++++++++ .../room/TweetEditorModalContainer.js | 102 ++++++++++++++++++ .../room/TweetModalContainer.js | 38 +++++++ .../room/TwitterOAuthModal.js | 27 +++++ .../room/TwitterOAuthModal.stories.js | 12 +++ .../room/TwitterOAuthModalContainer.js | 72 +++++++++++++ src/react-components/styles/theme.scss | 2 + src/react-components/ui-root.js | 34 ++++-- 12 files changed, 492 insertions(+), 64 deletions(-) create mode 100644 src/react-components/room/TweetEditorModal.js create mode 100644 src/react-components/room/TweetEditorModal.scss create mode 100644 src/react-components/room/TweetEditorModal.stories.js create mode 100644 src/react-components/room/TweetEditorModalContainer.js create mode 100644 src/react-components/room/TweetModalContainer.js create mode 100644 src/react-components/room/TwitterOAuthModal.js create mode 100644 src/react-components/room/TwitterOAuthModal.stories.js create mode 100644 src/react-components/room/TwitterOAuthModalContainer.js diff --git a/src/components/tweet-media-button.js b/src/components/tweet-media-button.js index 56b6c02cea..81ae8912c5 100644 --- a/src/components/tweet-media-button.js +++ b/src/components/tweet-media-button.js @@ -21,15 +21,15 @@ AFRAME.registerComponent("tweet-media-button", { : `Taken in ${location.hostname} `; const { src, contentSubtype } = this.targetEl.components["media-loader"].data; - this.el.sceneEl.emit("action_media_tweet", { url: src, contentSubtype, text, el: this.targetEl }); + this.el.sceneEl.emit("action_media_tweet", { url: src, contentSubtype, text }); }; }, play() { - if (!configs.AVAILABLE_INTEGRATIONS.twitter) { - this.el.object3D.visible = false; - return; - } + // if (!configs.AVAILABLE_INTEGRATIONS.twitter) { + // this.el.object3D.visible = false; + // return; + // } this.el.object3D.addEventListener("interact", this.onClick); }, diff --git a/src/hub.js b/src/hub.js index e0c8d3b88d..d2d87e5ec3 100644 --- a/src/hub.js +++ b/src/hub.js @@ -224,6 +224,11 @@ const PHOENIX_RELIABLE_NAF = "phx-reliable"; NAF.options.firstSyncSource = PHOENIX_RELIABLE_NAF; NAF.options.syncSource = PHOENIX_RELIABLE_NAF; +// OAuth popup handler +if (window.opener) { + window.opener.postMessage("opened"); +} + const isBotMode = qsTruthy("bot"); const isTelemetryDisabled = qsTruthy("disable_telemetry"); const isDebug = qsTruthy("debug"); @@ -849,54 +854,6 @@ document.addEventListener("DOMContentLoaded", async () => { ); }); - scene.addEventListener("action_media_tweet", async e => { - let isInModal = false; - let isInOAuth = false; - - const exitOAuth = () => { - isInOAuth = false; - store.clearOnLoadActions(); - remountUI({ showOAuthDialog: false, oauthInfo: null }); - }; - - await handleExitTo2DInterstitial(true, () => { - if (isInModal) history.goBack(); - if (isInOAuth) exitOAuth(); - }); - - performConditionalSignIn( - () => hubChannel.signedIn, - async () => { - // Strip el from stored payload because it won't serialize into the store. - const serializableDetail = {}; - Object.assign(serializableDetail, e.detail); - delete serializableDetail.el; - - if (hubChannel.can("tweet")) { - isInModal = true; - pushHistoryState(history, "modal", "tweet", serializableDetail); - } else { - if (e.detail.el) { - // Pin the object if we have to go through OAuth, since the page will refresh and - // the object will otherwise be removed - e.detail.el.setAttribute("pinnable", "pinned", true); - } - - const url = await hubChannel.getTwitterOAuthURL(); - - isInOAuth = true; - store.enqueueOnLoadAction("emit_scene_event", { event: "action_media_tweet", detail: serializableDetail }); - remountUI({ - showOAuthDialog: true, - oauthInfo: [{ type: "twitter", url: url }], - onCloseOAuthDialog: () => exitOAuth() - }); - } - }, - "tweet" - ); - }); - remountUI({ performConditionalSignIn, embed: isEmbed, diff --git a/src/react-components/room/TweetEditorModal.js b/src/react-components/room/TweetEditorModal.js new file mode 100644 index 0000000000..8d172e4903 --- /dev/null +++ b/src/react-components/room/TweetEditorModal.js @@ -0,0 +1,92 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Modal } from "../modal/Modal"; +import { CloseButton } from "../input/CloseButton"; +import { Button } from "../input/Button"; +import { Column } from "../layout/Column"; +import createEmojiPlugin from "draft-js-emoji-plugin"; +import createHashtagPlugin from "draft-js-hashtag-plugin"; +import createLinkifyPlugin from "draft-js-linkify-plugin"; +import createCounterPlugin from "draft-js-counter-plugin"; +import Editor from "draft-js-plugins-editor"; +import "draft-js-emoji-plugin/lib/plugin.css"; +import "draft-js-hashtag-plugin/lib/plugin.css"; +import "draft-js-linkify-plugin/lib/plugin.css"; +import "draft-js-counter-plugin/lib/plugin.css"; +import styles from "./TweetEditorModal.scss"; +import { FormattedMessage } from "react-intl"; + +const emojiPlugin = createEmojiPlugin(); +const hashtagPlugin = createHashtagPlugin(); +const linkifyPlugin = createLinkifyPlugin(); +const counterPlugin = createCounterPlugin(); + +export function TweetEditorModal({ + editorState, + editorRef, + sending, + mediaThumbnailUrl, + contentSubtype, + onClickEditor, + onChange, + onSend, + onClose +}) { + const { EmojiSuggestions, EmojiSelect } = emojiPlugin; + const { CharCounter } = counterPlugin; + + const tweetLength = editorState.getCurrentContent().getPlainText().length; + + return ( + }> + +
+ {contentSubtype && contentSubtype.startsWith("video") ? ( +
+
+
+ +
+ +
+ +
+ +
+ +
+ + {tweetLength > 200 && ( +
+ / 280 +
+ )} +
+ +
+
+ ); +} + +TweetEditorModal.propTypes = { + editorRef: PropTypes.any, + editorState: PropTypes.object.isRequired, + onChange: PropTypes.func, + sending: PropTypes.bool, + mediaThumbnailUrl: PropTypes.string, + contentSubtype: PropTypes.string, + onClickEditor: PropTypes.func, + onSend: PropTypes.func, + onClose: PropTypes.func +}; diff --git a/src/react-components/room/TweetEditorModal.scss b/src/react-components/room/TweetEditorModal.scss new file mode 100644 index 0000000000..6174e62338 --- /dev/null +++ b/src/react-components/room/TweetEditorModal.scss @@ -0,0 +1,61 @@ +@use "../styles/theme.scss"; + +:local(.media) { + img, video { + border-radius: theme.$border-radius-regular; + background-color: theme.$lightgrey; + } +} + +:local(.editor) { + position: relative; + display: flex; + width: 100%; + border: 1px solid theme.$darkgrey; + border-radius: theme.$border-radius-regular; + color: theme.$black; + min-height: 5em; + padding: theme.$spacing-xs; + + &:focus-within { + border-color: theme.$blue; + box-shadow: 0 0 0 2px theme.$blue; + } +} + +:local(.editor-inner) { + width: 100%; + text-align: left; + padding-bottom: 36px; +} + +:local(.emoji-button) { + display: none; + position: absolute; + bottom: theme.$spacing-2xs; + right: theme.$spacing-2xs; + + @media(min-width: theme.$breakpoint-md) { + display: block; + } +} + +// Emoji pop out button +:local(.emoji-button) > div > button { + border: none; + font-size: theme.$font-size-xl; + width: 1.5em; +} + +// Emoji pop out menu +:local(.emoji-button) > div > div { + right: 0px; + cursor: pointer; +} + +:local(.counter) { + position: absolute; + bottom: theme.$spacing-xs; + left: theme.$spacing-xs; + color: theme.$grey; +} \ No newline at end of file diff --git a/src/react-components/room/TweetEditorModal.stories.js b/src/react-components/room/TweetEditorModal.stories.js new file mode 100644 index 0000000000..5f6e2f1753 --- /dev/null +++ b/src/react-components/room/TweetEditorModal.stories.js @@ -0,0 +1,53 @@ +import React, { useRef, useState } from "react"; +import { RoomLayout } from "../layout/RoomLayout"; +import { TweetEditorModal } from "./TweetEditorModal"; +import { createEditorStateWithText } from "draft-js-plugins-editor"; +import imgSrc from "../../assets/background.jpg"; +import videoSrc from "../../assets/video/home.mp4"; + +export default { + title: "TweetEditorModal", + parameters: { + layout: "fullscreen" + } +}; + +export const Image = () => { + const editorRef = useRef(); + const [editorState, setEditorState] = useState(() => createEditorStateWithText("Example tweet")); + + return ( + editorRef.current.focus()} + onChange={setEditorState} + /> + } + /> + ); +}; + +export const Video = () => { + const editorRef = useRef(); + const [editorState, setEditorState] = useState(() => createEditorStateWithText("Example tweet")); + + return ( + editorRef.current.focus()} + onChange={setEditorState} + /> + } + /> + ); +}; diff --git a/src/react-components/room/TweetEditorModalContainer.js b/src/react-components/room/TweetEditorModalContainer.js new file mode 100644 index 0000000000..17b34b61c6 --- /dev/null +++ b/src/react-components/room/TweetEditorModalContainer.js @@ -0,0 +1,102 @@ +import React, { useRef, useEffect, useState, useCallback, useMemo } from "react"; +import PropTypes from "prop-types"; +import { TweetEditorModal } from "./TweetEditorModal"; +import { Modifier, EditorState } from "draft-js"; +import { createEditorStateWithText } from "draft-js-plugins-editor"; +import { fetchReticulumAuthenticated } from "../../utils/phoenix-utils"; +import { scaledThumbnailUrlFor } from "../../utils/media-url-utils"; + +// Taken from draft-js-emoji +const addEmoji = (emoji, editorState) => { + const contentState = editorState.getCurrentContent(); + const contentStateWithEntity = contentState.createEntity("emoji", "IMMUTABLE", { emojiUnicode: emoji }); + const entityKey = contentStateWithEntity.getLastCreatedEntityKey(); + const currentSelectionState = editorState.getSelection(); + + let emojiAddedContent; + let emojiEndPos = 0; + let blockSize = 0; + + // in case text is selected it is removed and then the emoji is added + const afterRemovalContentState = Modifier.removeRange(contentState, currentSelectionState, "backward"); + + // deciding on the position to insert emoji + const targetSelection = afterRemovalContentState.getSelectionAfter(); + + emojiAddedContent = Modifier.insertText(afterRemovalContentState, targetSelection, emoji, null, entityKey); + + emojiEndPos = targetSelection.getAnchorOffset(); + const blockKey = targetSelection.getAnchorKey(); + blockSize = contentState.getBlockForKey(blockKey).getLength(); + + // If the emoji is inserted at the end, a space is appended right after for + // a smooth writing experience. + if (emojiEndPos === blockSize) { + emojiAddedContent = Modifier.insertText(emojiAddedContent, emojiAddedContent.getSelectionAfter(), " "); + } + + const newEditorState = EditorState.push(editorState, emojiAddedContent, "insert-emoji"); + return EditorState.forceSelection(newEditorState, emojiAddedContent.getSelectionAfter()); +}; + +export function TweetEditorModalContainer({ initialTweet, mediaUrl, contentSubtype, onClose }) { + const [editorState, setEditorState] = useState(() => createEditorStateWithText(initialTweet || "")); + const editorRef = useRef(); + + useEffect(() => { + // Calling this immediately seems to break editor initialization + editorRef.current.focus(); + + // Other attempts at doing this resulted in no visible cursor or weird editor behavior: + setEditorState(editorState => addEmoji("🐤", editorState)); + }, []); + + const [sending, setSending] = useState(false); + + const sendTweet = useCallback( + async () => { + setSending(true); + + try { + const body = editorState.getCurrentContent().getPlainText(); + // For now assume url is a stored file media url + await fetchReticulumAuthenticated("/api/v1/twitter/tweets", "POST", { media_stored_file_url: mediaUrl, body }); + } catch (error) { + setSending(false); + console.error(error); + } + + onClose(); + }, + [mediaUrl, editorState, onClose] + ); + + const mediaThumbnailUrl = useMemo( + () => { + return contentSubtype && !contentSubtype.startsWith("video") + ? scaledThumbnailUrlFor(mediaUrl, 450, 255) + : mediaUrl; + }, + [contentSubtype, mediaUrl] + ); + + return ( + + ); +} + +TweetEditorModalContainer.propTypes = { + mediaUrl: PropTypes.string, + contentSubtype: PropTypes.string, + initialTweet: PropTypes.object.isRequired, + onClose: PropTypes.func +}; diff --git a/src/react-components/room/TweetModalContainer.js b/src/react-components/room/TweetModalContainer.js new file mode 100644 index 0000000000..7f5c8cedcf --- /dev/null +++ b/src/react-components/room/TweetModalContainer.js @@ -0,0 +1,38 @@ +import React, { useCallback, useState } from "react"; +import PropTypes from "prop-types"; +import { TweetEditorModalContainer } from "./TweetEditorModalContainer"; +import { TwitterOAuthModalContainer } from "./TwitterOAuthModalContainer"; + +export function TweetModalContainer({ hubChannel, entity, initialTweet, mediaUrl, contentSubtype, onClose }) { + const [canTweet, setCanTweet] = useState(hubChannel.can("tweet")); + + const onConnected = useCallback( + () => { + console.log({ canTweet: hubChannel.can("tweet") }); + setCanTweet(true); + }, + [hubChannel] + ); + + if (canTweet) { + return ( + + ); + } else { + return ; + } +} + +TweetModalContainer.propTypes = { + hubChannel: PropTypes.object.isRequired, + initialTweet: PropTypes.string, + mediaUrl: PropTypes.string, + contentSubtype: PropTypes.string, + onClose: PropTypes.func +}; diff --git a/src/react-components/room/TwitterOAuthModal.js b/src/react-components/room/TwitterOAuthModal.js new file mode 100644 index 0000000000..d97abef1b2 --- /dev/null +++ b/src/react-components/room/TwitterOAuthModal.js @@ -0,0 +1,27 @@ +import React from "react"; +import { FormattedMessage } from "react-intl"; +import PropTypes from "prop-types"; +import { Modal } from "../modal/Modal"; +import { CloseButton } from "../input/CloseButton"; +import { Button } from "../input/Button"; +import { Column } from "../layout/Column"; + +export function TwitterOAuthModal({ onConnect, onClose }) { + return ( + }> + +

+ +

+ +
+
+ ); +} + +TwitterOAuthModal.propTypes = { + onConnect: PropTypes.func, + onClose: PropTypes.func +}; diff --git a/src/react-components/room/TwitterOAuthModal.stories.js b/src/react-components/room/TwitterOAuthModal.stories.js new file mode 100644 index 0000000000..edbfc0fcd1 --- /dev/null +++ b/src/react-components/room/TwitterOAuthModal.stories.js @@ -0,0 +1,12 @@ +import React from "react"; +import { RoomLayout } from "../layout/RoomLayout"; +import { TwitterOAuthModal } from "./TwitterOAuthModal"; + +export default { + title: "TwitterOAuthModal", + parameters: { + layout: "fullscreen" + } +}; + +export const Base = () => } />; diff --git a/src/react-components/room/TwitterOAuthModalContainer.js b/src/react-components/room/TwitterOAuthModalContainer.js new file mode 100644 index 0000000000..3973af4f3d --- /dev/null +++ b/src/react-components/room/TwitterOAuthModalContainer.js @@ -0,0 +1,72 @@ +import React, { useCallback, useEffect, useRef } from "react"; +import PropTypes from "prop-types"; +import { TwitterOAuthModal } from "./TwitterOAuthModal"; + +export function TwitterOAuthModalContainer({ hubChannel, onConnected, onClose }) { + const popupRef = useRef(); + + const onConnect = useCallback( + async () => { + try { + if (popupRef.current) { + popupRef.current.close(); + } + + const url = await hubChannel.getTwitterOAuthURL(); + + const popupWidth = 600; + const popupHeight = 400; + const screenLeft = window.screenLeft !== undefined ? window.screenLeft : window.screenX; + const screenTop = window.screenTop !== undefined ? window.screenTop : window.screenY; + + const width = window.innerWidth + ? window.innerWidth + : document.documentElement.clientWidth + ? document.documentElement.clientWidth + : screen.width; + const height = window.innerHeight + ? window.innerHeight + : document.documentElement.clientHeight + ? document.documentElement.clientHeight + : screen.height; + + const systemZoom = width / window.screen.availWidth; + const left = (width - popupWidth) / 2 / systemZoom + screenLeft; + const top = (height - popupHeight) / 2 / systemZoom + screenTop; + + const popup = window.open( + url, + "_blank", + `resizable=yes,width=${popupWidth},height=${popupHeight},left=${left},top=${top}toolbar=no,titlebar=no,menubar=no,scrollbars=yes` + ); + + popup.addEventListener("message", () => { + popup.close(); + onConnected(); + }); + + popup.focus(); + popupRef.current = popup; + } catch (error) { + console.error(error); + } + }, + [hubChannel, onConnected] + ); + + useEffect(() => { + return () => { + if (popupRef.current) { + popupRef.current.close(); + } + }; + }, []); + + return ; +} + +TwitterOAuthModalContainer.propTypes = { + hubChannel: PropTypes.object.isRequired, + onConnected: PropTypes.func.isRequired, + onClose: PropTypes.func +}; diff --git a/src/react-components/styles/theme.scss b/src/react-components/styles/theme.scss index eae04e562c..093448bf24 100644 --- a/src/react-components/styles/theme.scss +++ b/src/react-components/styles/theme.scss @@ -56,6 +56,7 @@ $font-size-xs: 10px; $font-size-sm: 12px; $font-size-md: 16px; $font-size-lg: 20px; +$font-size-xl: 24px; $font-weight-regular: 400; $font-weight-medium: 500; @@ -65,6 +66,7 @@ $border-radius-regular: 8px; $outline-width: 3px; +$spacing-2xs: 4px; $spacing-xs: 8px; $spacing-sm: 12px; $spacing-md: 16px; diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index 8a1b3dacca..c695d08143 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -30,7 +30,6 @@ import InviteDialog from "./invite-dialog.js"; import Tip from "./tip.js"; import FeedbackDialog from "./feedback-dialog.js"; import OAuthDialog from "./oauth-dialog.js"; -import TweetDialog from "./tweet-dialog.js"; import EntryStartPanel from "./entry-start-panel.js"; import AvatarEditor from "./avatar-editor"; import PreferencesScreen from "./preferences-screen.js"; @@ -38,7 +37,7 @@ import PresenceLog from "./presence-log.js"; import PreloadOverlay from "./preload-overlay.js"; import { SpectatingLabel } from "./spectating-label"; import { showFullScreenIfAvailable, showFullScreenIfWasFullScreen } from "../utils/fullscreen"; -import { exit2DInterstitialAndEnterVR, isIn2DInterstitial } from "../utils/vr-interstitial"; +import { handleExitTo2DInterstitial, exit2DInterstitialAndEnterVR, isIn2DInterstitial } from "../utils/vr-interstitial"; import { resetTips } from "../systems/tips"; import { faTimes } from "@fortawesome/free-solid-svg-icons/faTimes"; @@ -95,6 +94,7 @@ import { ExitReason } from "./room/ExitedRoomScreen"; import { UserProfileSidebarContainer } from "./room/UserProfileSidebarContainer"; import { CloseRoomModal } from "./room/CloseRoomModal"; import { WebVRUnsupportedModal } from "./room/WebVRUnsupportedModal"; +import { TweetModalContainer } from "./room/TweetModalContainer"; const avatarEditorDebug = qsTruthy("avatarEditorDebug"); @@ -381,6 +381,8 @@ class UIRoot extends Component { } this.playerRig = scene.querySelector("#avatar-rig"); + + scene.addEventListener("action_media_tweet", this.onTweet); } UNSAFE_componentWillMount() { @@ -393,6 +395,7 @@ class UIRoot extends Component { this.props.scene.removeEventListener("share_video_enabled", this.onShareVideoEnabled); this.props.scene.removeEventListener("share_video_disabled", this.onShareVideoDisabled); this.props.scene.removeEventListener("share_video_failed", this.onShareVideoFailed); + this.props.scene.removeEventListener("action_media_tweet", this.onTweet); this.props.store.removeEventListener("statechanged", this.storeUpdated); window.removeEventListener("concurrentload", this.onConcurrentLoad); window.removeEventListener("idle_detected", this.onIdleDetected); @@ -910,6 +913,23 @@ class UIRoot extends Component { return false; }; + onTweet = ({ detail: { src, contentSubtype, text } }) => { + handleExitTo2DInterstitial(true, () => {}).then(() => { + this.props.performConditionalSignIn( + () => this.props.hubChannel.signedIn, + () => { + this.showNonHistoriedDialog(TweetModalContainer, { + hubChannel: this.props.hubChannel, + initialTweet: text, + mediaUrl: src, + contentSubtype: contentSubtype + }); + }, + "tweet" + ); + }); + }; + pushHistoryState = (k, v) => pushHistoryState(this.props.history, k, v); setSidebar(sidebarId, otherState) { @@ -1283,7 +1303,7 @@ class UIRoot extends Component { onClick: () => this.showNonHistoriedDialog(LeaveRoomModal, { destinationUrl: "/", - reacon: LeaveReason.createRoom + reason: LeaveReason.createRoom }) }, { @@ -1520,14 +1540,6 @@ class UIRoot extends Component { }) } /> - - this.renderDialog(TweetDialog, { history: this.props.history, onClose: this.closeDialog }) - } - /> {this.props.activeObject && ( Date: Mon, 16 Nov 2020 14:05:00 -0800 Subject: [PATCH 14/31] Revert tweet-media-button changes --- src/components/tweet-media-button.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/tweet-media-button.js b/src/components/tweet-media-button.js index 81ae8912c5..367b9d75d8 100644 --- a/src/components/tweet-media-button.js +++ b/src/components/tweet-media-button.js @@ -26,10 +26,10 @@ AFRAME.registerComponent("tweet-media-button", { }, play() { - // if (!configs.AVAILABLE_INTEGRATIONS.twitter) { - // this.el.object3D.visible = false; - // return; - // } + if (!configs.AVAILABLE_INTEGRATIONS.twitter) { + this.el.object3D.visible = false; + return; + } this.el.object3D.addEventListener("interact", this.onClick); }, From 7c6b7ec2239cd4db977460404e550540ff400641 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Mon, 16 Nov 2020 15:19:02 -0800 Subject: [PATCH 15/31] Migrate OAuthDialog --- src/assets/stylesheets/oauth-dialog.scss | 34 ------- src/hub.js | 7 +- src/react-components/auth/OAuthScreen.js | 69 ++++++++++++++ src/react-components/auth/OAuthScreen.scss | 12 +++ .../auth/OAuthScreen.stories.js | 34 +++++++ .../auth/OAuthScreenContainer.js | 24 +++++ src/react-components/oauth-dialog.js | 93 ------------------- src/react-components/ui-root.js | 11 --- 8 files changed, 144 insertions(+), 140 deletions(-) delete mode 100644 src/assets/stylesheets/oauth-dialog.scss create mode 100644 src/react-components/auth/OAuthScreen.js create mode 100644 src/react-components/auth/OAuthScreen.scss create mode 100644 src/react-components/auth/OAuthScreen.stories.js create mode 100644 src/react-components/auth/OAuthScreenContainer.js delete mode 100644 src/react-components/oauth-dialog.js diff --git a/src/assets/stylesheets/oauth-dialog.scss b/src/assets/stylesheets/oauth-dialog.scss deleted file mode 100644 index 978fadd9b8..0000000000 --- a/src/assets/stylesheets/oauth-dialog.scss +++ /dev/null @@ -1,34 +0,0 @@ -@import 'shared.scss'; - -:local(.oauth-type-name) { - text-transform: capitalize; -} -:local(.oauth-icon) { - font-size: 32px; - margin-right: 0.2em; - margin-top: 2px; -} -:local(.privacy-notice) { - font-size: 7pt; -} -:local(.oauth-buttons) { - flex-direction: column; -} -:local(.oauth-button) { - @extend %action-button; - margin-top: 24px; - color: $action-text-color !important; - flex-direction: row; -} -:local(.oauth-twitter-button) { - @extend %twitter-action-button; -} -:local(.oauth-discord-button) { - @extend %discord-action-button; -} -:local(.oauth-slack-button) { - @extend %slack-action-button; -} -:local(.twitter-message) { - margin: 24px 36px 0 36px; -} diff --git a/src/hub.js b/src/hub.js index d2d87e5ec3..ba3c8f4aef 100644 --- a/src/hub.js +++ b/src/hub.js @@ -219,6 +219,7 @@ import detectConcurrentLoad from "./utils/concurrent-load-detector"; import qsTruthy from "./utils/qs_truthy"; import { WrappedIntlProvider } from "./react-components/wrapped-intl-provider"; import { ExitReason } from "./react-components/room/ExitedRoomScreen"; +import { OAuthScreenContainer } from "./react-components/auth/OAuthScreenContainer"; const PHOENIX_RELIABLE_NAF = "phx-reliable"; NAF.options.firstSyncSource = PHOENIX_RELIABLE_NAF; @@ -288,7 +289,9 @@ function mountUI(props = {}) { - props.roomUnavailableReason ? ( + props.showOAuthScreen ? ( + + ) : props.roomUnavailableReason ? ( ) : ( { remountUI({ roomUnavailableReason: ExitReason.closed }); } else if (res.reason === "oauth_required") { entryManager.exitScene(); - remountUI({ oauthInfo: res.oauth_info, showOAuthDialog: true }); + remountUI({ oauthInfo: res.oauth_info, showOAuthScreen: true }); } else if (res.reason === "join_denied") { entryManager.exitScene(); remountUI({ roomUnavailableReason: ExitReason.denied }); diff --git a/src/react-components/auth/OAuthScreen.js b/src/react-components/auth/OAuthScreen.js new file mode 100644 index 0000000000..388fa5597d --- /dev/null +++ b/src/react-components/auth/OAuthScreen.js @@ -0,0 +1,69 @@ +import React from "react"; +import PropTypes from "prop-types"; +import styles from "./OAuthScreen.scss"; +import { Modal } from "../modal/Modal"; +import { Column } from "../layout/Column"; +import { defineMessages, useIntl } from "react-intl"; +import { Button } from "../input/Button"; + +const providerLabel = { + discord: "Discord", + slack: "Slack" +}; + +const messages = defineMessages({ + discord: { + id: "oauth-dialog.sign-in.discord", + defaultMessage: "Sign in to Discord" + }, + slack: { + id: "oauth-dialog.sign-in.slack", + defaultMessage: "Sign in to Slack" + } +}); + +export function OAuthScreen({ provider, redirectUrl, showTerms, termsUrl, showPrivacy, privacyUrl, ...rest }) { + const intl = useIntl(); + + return ( +
+ + +

You'll need to sign in to {providerLabel[provider]} to access this room.

+
+

We'll ask for access to your e-mail address so you can skip signing in next time.

+ {(showTerms || showPrivacy) && ( + + By proceeding, you agree to the{" "} + {showTerms && ( + <> + + terms of use + {" "} + + )} + {showTerms && showPrivacy && "and "} + {showPrivacy && ( + + privacy notice + + )}. + + )} + +
+
+
+ ); +} + +OAuthScreen.propTypes = { + provider: PropTypes.string.isRequired, + redirectUrl: PropTypes.string.isRequired, + showTerms: PropTypes.bool, + termsUrl: PropTypes.string, + showPrivacy: PropTypes.bool, + privacyUrl: PropTypes.string +}; diff --git a/src/react-components/auth/OAuthScreen.scss b/src/react-components/auth/OAuthScreen.scss new file mode 100644 index 0000000000..7ff341465e --- /dev/null +++ b/src/react-components/auth/OAuthScreen.scss @@ -0,0 +1,12 @@ +@use "../styles/theme.scss"; + +:local(.oauth-screen) { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + background-color: theme.$lightgrey; + background-size: cover; +} \ No newline at end of file diff --git a/src/react-components/auth/OAuthScreen.stories.js b/src/react-components/auth/OAuthScreen.stories.js new file mode 100644 index 0000000000..e6fccccc5a --- /dev/null +++ b/src/react-components/auth/OAuthScreen.stories.js @@ -0,0 +1,34 @@ +import React from "react"; +import { OAuthScreen } from "./OAuthScreen"; +import backgroundUrl from "../../assets/images/home-hero-background-unbranded.png"; + +export default { + title: "OAuthScreen", + parameters: { + layout: "fullscreen" + } +}; + +export const Discord = () => ( + +); + +export const Slack = () => ( + +); diff --git a/src/react-components/auth/OAuthScreenContainer.js b/src/react-components/auth/OAuthScreenContainer.js new file mode 100644 index 0000000000..bce0ee8334 --- /dev/null +++ b/src/react-components/auth/OAuthScreenContainer.js @@ -0,0 +1,24 @@ +import React from "react"; +import PropTypes from "prop-types"; +import configs from "../../utils/configs"; +import { OAuthScreen } from "./OAuthScreen"; + +export function OAuthScreenContainer({ oauthInfo }) { + const { url, type } = oauthInfo[0]; + + return ( + + ); +} + +OAuthScreenContainer.propTypes = { + oauthInfo: PropTypes.array +}; diff --git a/src/react-components/oauth-dialog.js b/src/react-components/oauth-dialog.js deleted file mode 100644 index e7ee732e71..0000000000 --- a/src/react-components/oauth-dialog.js +++ /dev/null @@ -1,93 +0,0 @@ -import React, { Component } from "react"; -import PropTypes from "prop-types"; -import { FormattedMessage } from "react-intl"; -import classNames from "classnames"; - -import configs from "../utils/configs"; -import IfFeature from "./if-feature"; -import DialogContainer from "./dialog-container.js"; -import styles from "../assets/stylesheets/oauth-dialog.scss"; - -export default class OAuthDialog extends Component { - static propTypes = { - oauthInfo: PropTypes.array - }; - renderSignInDisclaimer(type) { - return ( -
-
- You'll need to sign in to {type} to access this room. -
-
-
We'll ask for access to your e-mail address so you can skip signing in next time.
-

- By proceeding, you agree to the{" "} - - - terms of use - {" "} - - {configs.feature("show_terms") && configs.feature("show_privacy") && "and "} - - - privacy notice - - . -

-
- ); - } - - renderTweetDisclaimer() { - return ( -
- -
- ); - } - - render() { - const info = this.props.oauthInfo && this.props.oauthInfo[0]; - const closable = info && info.type === "twitter"; // NOTE: for now, allow closing dialog if doing Twitter sign in (currently just for sharing) - - return ( - -
- {info && (info.type === "twitter" ? this.renderTweetDisclaimer() : this.renderSignInDisclaimer(info.type))} -
-
- {info ? ( - - - - ) : ( -
-
-
-
-
- )} -
-
-
- - ); - } -} diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index c695d08143..11345c6a82 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -29,7 +29,6 @@ import MediaBrowser from "./media-browser"; import InviteDialog from "./invite-dialog.js"; import Tip from "./tip.js"; import FeedbackDialog from "./feedback-dialog.js"; -import OAuthDialog from "./oauth-dialog.js"; import EntryStartPanel from "./entry-start-panel.js"; import AvatarEditor from "./avatar-editor"; import PreferencesScreen from "./preferences-screen.js"; @@ -162,9 +161,6 @@ class UIRoot extends Component { signInContinueTextId: PropTypes.string, onContinueAfterSignIn: PropTypes.func, showSafariMicDialog: PropTypes.bool, - showOAuthDialog: PropTypes.bool, - onCloseOAuthDialog: PropTypes.func, - oauthInfo: PropTypes.array, onMediaSearchResultEntrySelected: PropTypes.func, onAvatarSaved: PropTypes.func, activeTips: PropTypes.object, @@ -1147,13 +1143,6 @@ class UIRoot extends Component { const isLoading = !preload && !this.state.hideLoader && !this.props.showSafariMicDialog; - if (this.props.showOAuthDialog && !this.props.showInterstitialPrompt) - return ( -
- -
- ); - if (isLoading && this.state.showPrefs) { return (
From 18ae1893315cd88e758f82b76f2b03cf3dfba0b9 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Mon, 16 Nov 2020 17:26:37 -0800 Subject: [PATCH 16/31] Fix twitter oauth modal --- src/hub.js | 4 ++- .../room/TweetEditorModal.scss | 8 +++--- .../room/TweetEditorModalContainer.js | 3 --- .../room/TwitterOAuthModalContainer.js | 25 ++++--------------- 4 files changed, 11 insertions(+), 29 deletions(-) diff --git a/src/hub.js b/src/hub.js index ba3c8f4aef..8b4bf990be 100644 --- a/src/hub.js +++ b/src/hub.js @@ -226,8 +226,10 @@ NAF.options.firstSyncSource = PHOENIX_RELIABLE_NAF; NAF.options.syncSource = PHOENIX_RELIABLE_NAF; // OAuth popup handler -if (window.opener) { +// TODO: Replace with a new oauth callback route that has this postMessage script. +if (window.opener && window.opener.location.hostname === location.hostname) { window.opener.postMessage("opened"); + return; } const isBotMode = qsTruthy("bot"); diff --git a/src/react-components/room/TweetEditorModal.scss b/src/react-components/room/TweetEditorModal.scss index 6174e62338..9de10a7815 100644 --- a/src/react-components/room/TweetEditorModal.scss +++ b/src/react-components/room/TweetEditorModal.scss @@ -1,10 +1,8 @@ @use "../styles/theme.scss"; -:local(.media) { - img, video { - border-radius: theme.$border-radius-regular; - background-color: theme.$lightgrey; - } +:local(.media), :local(.media) img, :local(.media) video { + border-radius: theme.$border-radius-regular; + background-color: theme.$lightgrey; } :local(.editor) { diff --git a/src/react-components/room/TweetEditorModalContainer.js b/src/react-components/room/TweetEditorModalContainer.js index 17b34b61c6..b8e47bc299 100644 --- a/src/react-components/room/TweetEditorModalContainer.js +++ b/src/react-components/room/TweetEditorModalContainer.js @@ -44,9 +44,6 @@ export function TweetEditorModalContainer({ initialTweet, mediaUrl, contentSubty const editorRef = useRef(); useEffect(() => { - // Calling this immediately seems to break editor initialization - editorRef.current.focus(); - // Other attempts at doing this resulted in no visible cursor or weird editor behavior: setEditorState(editorState => addEmoji("🐤", editorState)); }, []); diff --git a/src/react-components/room/TwitterOAuthModalContainer.js b/src/react-components/room/TwitterOAuthModalContainer.js index 3973af4f3d..289e885087 100644 --- a/src/react-components/room/TwitterOAuthModalContainer.js +++ b/src/react-components/room/TwitterOAuthModalContainer.js @@ -14,30 +14,15 @@ export function TwitterOAuthModalContainer({ hubChannel, onConnected, onClose }) const url = await hubChannel.getTwitterOAuthURL(); - const popupWidth = 600; - const popupHeight = 400; - const screenLeft = window.screenLeft !== undefined ? window.screenLeft : window.screenX; - const screenTop = window.screenTop !== undefined ? window.screenTop : window.screenY; - - const width = window.innerWidth - ? window.innerWidth - : document.documentElement.clientWidth - ? document.documentElement.clientWidth - : screen.width; - const height = window.innerHeight - ? window.innerHeight - : document.documentElement.clientHeight - ? document.documentElement.clientHeight - : screen.height; - - const systemZoom = width / window.screen.availWidth; - const left = (width - popupWidth) / 2 / systemZoom + screenLeft; - const top = (height - popupHeight) / 2 / systemZoom + screenTop; + const width = 600; + const height = 760; + const left = (window.innerWidth - width) / 2 + window.screenLeft; + const top = (window.innerHeight - height) / 2 + window.screenTop; const popup = window.open( url, "_blank", - `resizable=yes,width=${popupWidth},height=${popupHeight},left=${left},top=${top}toolbar=no,titlebar=no,menubar=no,scrollbars=yes` + `resizable=yes,width=${width},height=${height},left=${left},top=${top}toolbar=no,titlebar=no,menubar=no,scrollbars=yes` ); popup.addEventListener("message", () => { From d272e516953f46ec3f6997f48d17d47485b579c7 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Mon, 16 Nov 2020 17:29:33 -0800 Subject: [PATCH 17/31] Throw an error instead of returning --- src/hub.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hub.js b/src/hub.js index 8b4bf990be..8e82eec555 100644 --- a/src/hub.js +++ b/src/hub.js @@ -229,7 +229,7 @@ NAF.options.syncSource = PHOENIX_RELIABLE_NAF; // TODO: Replace with a new oauth callback route that has this postMessage script. if (window.opener && window.opener.location.hostname === location.hostname) { window.opener.postMessage("opened"); - return; + throw Error("OAuth Dialog Detected. Stopping page load."); } const isBotMode = qsTruthy("bot"); From dca904ee43166a0d6621afd98baba2cb9aa206d2 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Mon, 16 Nov 2020 17:48:49 -0800 Subject: [PATCH 18/31] Change oauth check. --- src/hub.js | 15 ++++++++++++--- .../room/TwitterOAuthModalContainer.js | 2 ++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/hub.js b/src/hub.js index 8e82eec555..836f0e577b 100644 --- a/src/hub.js +++ b/src/hub.js @@ -225,11 +225,13 @@ const PHOENIX_RELIABLE_NAF = "phx-reliable"; NAF.options.firstSyncSource = PHOENIX_RELIABLE_NAF; NAF.options.syncSource = PHOENIX_RELIABLE_NAF; +let isOAuthModal = false; + // OAuth popup handler // TODO: Replace with a new oauth callback route that has this postMessage script. -if (window.opener && window.opener.location.hostname === location.hostname) { +if (window.opener && window.opener.doingTwitterOAuth) { window.opener.postMessage("opened"); - throw Error("OAuth Dialog Detected. Stopping page load."); + isOAuthModal = true; } const isBotMode = qsTruthy("bot"); @@ -241,7 +243,10 @@ if (!isBotMode && !isTelemetryDisabled) { } disableiOSZoom(); -detectConcurrentLoad(); + +if (!isOAuthModal) { + detectConcurrentLoad(); +} function setupLobbyCamera() { const camera = document.getElementById("scene-preview-node"); @@ -727,6 +732,10 @@ function checkForAccountRequired() { } document.addEventListener("DOMContentLoaded", async () => { + if (isOAuthModal) { + return; + } + await store.initProfile(); const canvas = document.querySelector(".a-canvas"); diff --git a/src/react-components/room/TwitterOAuthModalContainer.js b/src/react-components/room/TwitterOAuthModalContainer.js index 289e885087..0a1cc9172f 100644 --- a/src/react-components/room/TwitterOAuthModalContainer.js +++ b/src/react-components/room/TwitterOAuthModalContainer.js @@ -32,6 +32,8 @@ export function TwitterOAuthModalContainer({ hubChannel, onConnected, onClose }) popup.focus(); popupRef.current = popup; + + window.popup = popup; } catch (error) { console.error(error); } From 6bdaaf5f01a96a8c4b4a7dc01709cca9ed562813 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Mon, 16 Nov 2020 17:56:40 -0800 Subject: [PATCH 19/31] Set doingTwitterOAuth --- src/react-components/room/TwitterOAuthModalContainer.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/react-components/room/TwitterOAuthModalContainer.js b/src/react-components/room/TwitterOAuthModalContainer.js index 0a1cc9172f..f2fd0d35e5 100644 --- a/src/react-components/room/TwitterOAuthModalContainer.js +++ b/src/react-components/room/TwitterOAuthModalContainer.js @@ -25,6 +25,8 @@ export function TwitterOAuthModalContainer({ hubChannel, onConnected, onClose }) `resizable=yes,width=${width},height=${height},left=${left},top=${top}toolbar=no,titlebar=no,menubar=no,scrollbars=yes` ); + window.doingTwitterOAuth = true; + popup.addEventListener("message", () => { popup.close(); onConnected(); @@ -46,6 +48,8 @@ export function TwitterOAuthModalContainer({ hubChannel, onConnected, onClose }) if (popupRef.current) { popupRef.current.close(); } + + delete window.doingTwitterOAuth; }; }, []); From eb68c83f344ebb6aa00e0b50f5d56ea55bf3589b Mon Sep 17 00:00:00 2001 From: Robert Long Date: Mon, 16 Nov 2020 18:03:35 -0800 Subject: [PATCH 20/31] postMessage from the window not window.opener --- src/hub.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hub.js b/src/hub.js index 836f0e577b..60643cea1a 100644 --- a/src/hub.js +++ b/src/hub.js @@ -230,7 +230,7 @@ let isOAuthModal = false; // OAuth popup handler // TODO: Replace with a new oauth callback route that has this postMessage script. if (window.opener && window.opener.doingTwitterOAuth) { - window.opener.postMessage("opened"); + window.postMessage("opened"); isOAuthModal = true; } From 8db0b081c2b9c2a8a01996aa9ea99137b87a6a9c Mon Sep 17 00:00:00 2001 From: Robert Long Date: Mon, 16 Nov 2020 18:16:59 -0800 Subject: [PATCH 21/31] Use correct postmessage handler --- src/hub.js | 2 +- .../room/TwitterOAuthModalContainer.js | 40 ++++++++++--------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/hub.js b/src/hub.js index 60643cea1a..21378e2282 100644 --- a/src/hub.js +++ b/src/hub.js @@ -230,7 +230,7 @@ let isOAuthModal = false; // OAuth popup handler // TODO: Replace with a new oauth callback route that has this postMessage script. if (window.opener && window.opener.doingTwitterOAuth) { - window.postMessage("opened"); + window.opener.postMessage("oauth-successful"); isOAuthModal = true; } diff --git a/src/react-components/room/TwitterOAuthModalContainer.js b/src/react-components/room/TwitterOAuthModalContainer.js index f2fd0d35e5..31000f51ac 100644 --- a/src/react-components/room/TwitterOAuthModalContainer.js +++ b/src/react-components/room/TwitterOAuthModalContainer.js @@ -19,39 +19,43 @@ export function TwitterOAuthModalContainer({ hubChannel, onConnected, onClose }) const left = (window.innerWidth - width) / 2 + window.screenLeft; const top = (window.innerHeight - height) / 2 + window.screenTop; + window.doingTwitterOAuth = true; + const popup = window.open( url, "_blank", `resizable=yes,width=${width},height=${height},left=${left},top=${top}toolbar=no,titlebar=no,menubar=no,scrollbars=yes` ); - - window.doingTwitterOAuth = true; - - popup.addEventListener("message", () => { - popup.close(); - onConnected(); - }); - popup.focus(); popupRef.current = popup; - - window.popup = popup; } catch (error) { console.error(error); } }, - [hubChannel, onConnected] + [hubChannel] ); - useEffect(() => { - return () => { - if (popupRef.current) { - popupRef.current.close(); + useEffect( + () => { + function onMessage({ data }) { + if (data === "oauth-successful") { + onConnected(); + } } - delete window.doingTwitterOAuth; - }; - }, []); + window.addEventListener("message", onMessage); + + return () => { + if (popupRef.current) { + popupRef.current.close(); + } + + delete window.doingTwitterOAuth; + window.removeEventListener("message", onMessage); + }; + }, + [onConnected] + ); return ; } From e1d48ebcf20be1ae273c79dc3df41367c1521f48 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Mon, 16 Nov 2020 18:27:25 -0800 Subject: [PATCH 22/31] Update can tweet permission after connect --- src/react-components/room/TweetModalContainer.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/react-components/room/TweetModalContainer.js b/src/react-components/room/TweetModalContainer.js index 7f5c8cedcf..4d9f0fcf67 100644 --- a/src/react-components/room/TweetModalContainer.js +++ b/src/react-components/room/TweetModalContainer.js @@ -3,13 +3,14 @@ import PropTypes from "prop-types"; import { TweetEditorModalContainer } from "./TweetEditorModalContainer"; import { TwitterOAuthModalContainer } from "./TwitterOAuthModalContainer"; -export function TweetModalContainer({ hubChannel, entity, initialTweet, mediaUrl, contentSubtype, onClose }) { +export function TweetModalContainer({ hubChannel, initialTweet, mediaUrl, contentSubtype, onClose }) { const [canTweet, setCanTweet] = useState(hubChannel.can("tweet")); const onConnected = useCallback( () => { - console.log({ canTweet: hubChannel.can("tweet") }); - setCanTweet(true); + hubChannel.fetchPermissions().then(() => { + setCanTweet(hubChannel.can("tweet")); + }); }, [hubChannel] ); @@ -20,7 +21,6 @@ export function TweetModalContainer({ hubChannel, entity, initialTweet, mediaUrl initialTweet={initialTweet} mediaUrl={mediaUrl} contentSubtype={contentSubtype} - entity={entity} onClose={onClose} /> ); From 19b1892294cb9165e93b8562577639c1be68a6d9 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Mon, 16 Nov 2020 18:34:30 -0800 Subject: [PATCH 23/31] Add logging --- src/react-components/room/TweetEditorModalContainer.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/react-components/room/TweetEditorModalContainer.js b/src/react-components/room/TweetEditorModalContainer.js index b8e47bc299..bbda3fe30d 100644 --- a/src/react-components/room/TweetEditorModalContainer.js +++ b/src/react-components/room/TweetEditorModalContainer.js @@ -77,6 +77,8 @@ export function TweetEditorModalContainer({ initialTweet, mediaUrl, contentSubty [contentSubtype, mediaUrl] ); + console.log({ mediaUrl, mediaThumbnailUrl }); + return ( Date: Mon, 16 Nov 2020 18:37:54 -0800 Subject: [PATCH 24/31] Clean up tweet handler --- src/components/tweet-media-button.js | 4 ++-- src/react-components/ui-root.js | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/tweet-media-button.js b/src/components/tweet-media-button.js index 367b9d75d8..fd67f3cff2 100644 --- a/src/components/tweet-media-button.js +++ b/src/components/tweet-media-button.js @@ -15,13 +15,13 @@ AFRAME.registerComponent("tweet-media-button", { this.onClick = () => { const hasDiscordBridges = window.APP.hubChannel && window.APP.hubChannel.discordBridges().length > 0; - const text = !hasDiscordBridges + const initialTweet = !hasDiscordBridges ? `Taken in ${location.hostname} - ` + `join me now at ${configs.SHORTLINK_DOMAIN}/${window.APP.hubChannel.hubId}! ` : `Taken in ${location.hostname} `; const { src, contentSubtype } = this.targetEl.components["media-loader"].data; - this.el.sceneEl.emit("action_media_tweet", { url: src, contentSubtype, text }); + this.el.sceneEl.emit("action_media_tweet", { mediaUrl: src, contentSubtype, initialTweet }); }; }, diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index 11345c6a82..5a44aa008a 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -909,16 +909,14 @@ class UIRoot extends Component { return false; }; - onTweet = ({ detail: { src, contentSubtype, text } }) => { + onTweet = ({ detail }) => { handleExitTo2DInterstitial(true, () => {}).then(() => { this.props.performConditionalSignIn( () => this.props.hubChannel.signedIn, () => { this.showNonHistoriedDialog(TweetModalContainer, { hubChannel: this.props.hubChannel, - initialTweet: text, - mediaUrl: src, - contentSubtype: contentSubtype + ...detail }); }, "tweet" From a32e1c21ea6735275b7773fcd04d86828eab6d11 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Mon, 16 Nov 2020 18:52:09 -0800 Subject: [PATCH 25/31] Fix close tweet modal --- src/react-components/room/TweetModalContainer.js | 11 +++-------- .../room/TwitterOAuthModalContainer.js | 4 ++++ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/react-components/room/TweetModalContainer.js b/src/react-components/room/TweetModalContainer.js index 4d9f0fcf67..6337408c92 100644 --- a/src/react-components/room/TweetModalContainer.js +++ b/src/react-components/room/TweetModalContainer.js @@ -6,14 +6,9 @@ import { TwitterOAuthModalContainer } from "./TwitterOAuthModalContainer"; export function TweetModalContainer({ hubChannel, initialTweet, mediaUrl, contentSubtype, onClose }) { const [canTweet, setCanTweet] = useState(hubChannel.can("tweet")); - const onConnected = useCallback( - () => { - hubChannel.fetchPermissions().then(() => { - setCanTweet(hubChannel.can("tweet")); - }); - }, - [hubChannel] - ); + const onConnected = useCallback(() => { + setCanTweet(true); + }, []); if (canTweet) { return ( diff --git a/src/react-components/room/TwitterOAuthModalContainer.js b/src/react-components/room/TwitterOAuthModalContainer.js index 31000f51ac..092e8e7c0a 100644 --- a/src/react-components/room/TwitterOAuthModalContainer.js +++ b/src/react-components/room/TwitterOAuthModalContainer.js @@ -40,6 +40,10 @@ export function TwitterOAuthModalContainer({ hubChannel, onConnected, onClose }) function onMessage({ data }) { if (data === "oauth-successful") { onConnected(); + popupRef.current.close(); + popupRef.current = null; + delete window.doingTwitterOAuth; + window.removeEventListener("message", onMessage); } } From 987613446b4160c9f108bcdc293d0b16183042c5 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Tue, 17 Nov 2020 09:27:40 -0800 Subject: [PATCH 26/31] Remove console logs --- src/react-components/media-browser.js | 3 --- src/react-components/room/TweetEditorModalContainer.js | 2 -- 2 files changed, 5 deletions(-) diff --git a/src/react-components/media-browser.js b/src/react-components/media-browser.js index 5e54bc48a6..af836a6d98 100644 --- a/src/react-components/media-browser.js +++ b/src/react-components/media-browser.js @@ -239,8 +239,6 @@ class MediaBrowser extends Component { const isAvatarApiType = source === "avatars"; this.pushExitMediaBrowserHistory(!isAvatarApiType); - console.log("showCustomMediaDialog", source); - if (source === "scenes") { this.props.showNonHistoriedDialog(SceneUrlModalContainer, { hubChannel }); } else if (isAvatarApiType) { @@ -286,7 +284,6 @@ class MediaBrowser extends Component { if (isAvatarApiType) { this.showCustomMediaDialog(urlSource); } else { - console.log(isSceneApiType, this.props.hubChannel.can("update_hub"), urlSource); this.props.performConditionalSignIn( () => !isSceneApiType || this.props.hubChannel.can("update_hub"), () => this.showCustomMediaDialog(urlSource), diff --git a/src/react-components/room/TweetEditorModalContainer.js b/src/react-components/room/TweetEditorModalContainer.js index bbda3fe30d..b8e47bc299 100644 --- a/src/react-components/room/TweetEditorModalContainer.js +++ b/src/react-components/room/TweetEditorModalContainer.js @@ -77,8 +77,6 @@ export function TweetEditorModalContainer({ initialTweet, mediaUrl, contentSubty [contentSubtype, mediaUrl] ); - console.log({ mediaUrl, mediaThumbnailUrl }); - return ( Date: Tue, 17 Nov 2020 12:14:42 -0800 Subject: [PATCH 27/31] Fix pointer events on OAuthScreen --- src/react-components/auth/OAuthScreen.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/react-components/auth/OAuthScreen.scss b/src/react-components/auth/OAuthScreen.scss index 7ff341465e..160c736872 100644 --- a/src/react-components/auth/OAuthScreen.scss +++ b/src/react-components/auth/OAuthScreen.scss @@ -9,4 +9,5 @@ height: 100%; background-color: theme.$lightgrey; background-size: cover; + pointer-events: all; } \ No newline at end of file From 2fdcdf53728012008e5e1746c8794c6ba6caabff Mon Sep 17 00:00:00 2001 From: Robert Long Date: Tue, 17 Nov 2020 12:14:52 -0800 Subject: [PATCH 28/31] Remove continueText --- src/hub.js | 2 ++ src/react-components/auth/RoomSignInModalContainer.js | 7 ++----- src/react-components/auth/SignInModal.js | 4 +--- src/react-components/ui-root.js | 10 +--------- 4 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/hub.js b/src/hub.js index 21378e2282..3225fb1156 100644 --- a/src/hub.js +++ b/src/hub.js @@ -1197,6 +1197,8 @@ document.addEventListener("DOMContentLoaded", async () => { } }); + console.log(oauthFlowPermsToken); + const hubPhxChannel = socket.channel(`hub:${hubId}`, createHubChannelParams(oauthFlowPermsToken)); const presenceLogEntries = []; diff --git a/src/react-components/auth/RoomSignInModalContainer.js b/src/react-components/auth/RoomSignInModalContainer.js index 6255ca0908..921d37ec1d 100644 --- a/src/react-components/auth/RoomSignInModalContainer.js +++ b/src/react-components/auth/RoomSignInModalContainer.js @@ -4,7 +4,7 @@ import configs from "../../utils/configs"; import { SignInModal, SignInStep, SubmitEmail, WaitForVerification, SignInComplete } from "./SignInModal"; // TODO: Migrate to use AuthContext -export function RoomSignInModalContainer({ onClose, step, onSubmitEmail, message, continueText, onContinue }) { +export function RoomSignInModalContainer({ onClose, step, onSubmitEmail, message, onContinue }) { const [cachedEmail, setCachedEmail] = useState(); return ( @@ -30,9 +30,7 @@ export function RoomSignInModalContainer({ onClose, step, onSubmitEmail, message showNewsletterSignup={configs.feature("show_newsletter_signup")} /> )} - {step === SignInStep.complete && ( - - )} + {step === SignInStep.complete && } ); } @@ -42,6 +40,5 @@ RoomSignInModalContainer.propTypes = { onSubmitEmail: PropTypes.func, step: PropTypes.string, message: PropTypes.string, - continueText: PropTypes.string, onContinue: PropTypes.func }; diff --git a/src/react-components/auth/SignInModal.js b/src/react-components/auth/SignInModal.js index 2e43216acd..d86dfc1d67 100644 --- a/src/react-components/auth/SignInModal.js +++ b/src/react-components/auth/SignInModal.js @@ -112,13 +112,12 @@ WaitForVerification.propTypes = { onCancel: PropTypes.func.isRequired }; -export function SignInComplete({ message, continueText, onContinue }) { +export function SignInComplete({ message, onContinue }) { return (

{message}

- {continueText &&

{continueText}

} @@ -128,7 +127,6 @@ export function SignInComplete({ message, continueText, onContinue }) { SignInComplete.propTypes = { message: PropTypes.string.isRequired, - continueText: PropTypes.string.isRequired, onContinue: PropTypes.func.isRequired }; diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index 5a44aa008a..6a8812d822 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -158,7 +158,6 @@ class UIRoot extends Component { showSignInDialog: PropTypes.bool, signInMessageId: PropTypes.string, signInCompleteMessageId: PropTypes.string, - signInContinueTextId: PropTypes.string, onContinueAfterSignIn: PropTypes.func, showSafariMicDialog: PropTypes.bool, onMediaSearchResultEntrySelected: PropTypes.func, @@ -403,13 +402,7 @@ class UIRoot extends Component { }; showContextualSignInDialog = () => { - const { - signInMessageId, - authChannel, - signInCompleteMessageId, - signInContinueTextId, - onContinueAfterSignIn - } = this.props; + const { signInMessageId, authChannel, signInCompleteMessageId, onContinueAfterSignIn } = this.props; this.showNonHistoriedDialog(RoomSignInModalContainer, { step: SignInStep.submit, @@ -428,7 +421,6 @@ class UIRoot extends Component { this.showNonHistoriedDialog(RoomSignInModalContainer, { step: SignInStep.complete, message: getMessages()[signInCompleteMessageId], - continueText: getMessages()[signInContinueTextId], onClose: onContinueAfterSignIn, onContinue: onContinueAfterSignIn }); From e64bbd34c2a4ce8732a75a5193a0829b4c642497 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Wed, 18 Nov 2020 14:31:02 -0800 Subject: [PATCH 29/31] Remove tweet-dialog --- src/assets/stylesheets/tweet-dialog.scss | 125 --------------- src/react-components/tweet-dialog.js | 188 ----------------------- 2 files changed, 313 deletions(-) delete mode 100644 src/assets/stylesheets/tweet-dialog.scss delete mode 100644 src/react-components/tweet-dialog.js diff --git a/src/assets/stylesheets/tweet-dialog.scss b/src/assets/stylesheets/tweet-dialog.scss deleted file mode 100644 index dcd4aadf8c..0000000000 --- a/src/assets/stylesheets/tweet-dialog.scss +++ /dev/null @@ -1,125 +0,0 @@ -@import 'shared.scss'; - -:local(.tweet) { - width: 600px; - - @media (max-width: 700px) { - width: 400px; - } - - @media (max-width: 475px) { - width: 300px; - } -} - -:local(.editor) { - background-color: var(--text-field-background-color); - border: 2px solid var(--twitter-editor-border-color); - margin: 24px 36px 0px 36px; - padding: 18px; - padding-bottom: 20px; - text-align: left; - font-size: 1em; - border-radius: 12px; - position: relative; - font-weight: normal; - min-height: 7em; - cursor: text; - display: flex; - flex-direction: column; - - @media (max-width: 475px) { - margin: 24px 8px 0px 8px; - } - - :local(.editor-inner) { - padding-right: 36px; - } - - a { - color: var(--twitter-link-color); - } - - button { - cursor: pointer; - } -} - -:local(.emoji-button) { - position: absolute; - top: 0px; - right: 0px; -} - -// Emoji pop out button -:local(.emoji-button) > div > button { - border: none; - font-size: 2em; - width: 1.5em; - margin-right: 4px; -} - -// Emoji pop out menu -:local(.emoji-button) > div > div { - right: 0px; - cursor: pointer; -} - -:local(.counter) { - display: flex; - justify-content: flex-end; - margin-top: 18px; -} - -:local(.media) { - align-self: center; - margin-top: 18px; - - img, video { - border-radius: 8px; - width: 450px; - height: 255px; - - - @media (max-width: 700px) { - width: 275px; - height: 155px; - } - - @media (max-width: 475px) { - width: 175px; - height: 100px; - } - } -} - -:local(.buttons) { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - - :local(.tweet-button) { - @extend %twitter-action-button; - margin-top: 36px; - } - - :local(.tweet-button):disabled { - background-color: var(--panel-widget-disabled-color); - } -} - -:local(.posted) { - margin: 24px 36px 0px 36px; - - @media (max-width: 475px) { - margin: 24px 8px 0px 8px; - } -} - -:local(.tweet-loader) { - height: 40px; -} -:local(.dialog-background) { - background-color: var(--twitter-panel-background-color); -} diff --git a/src/react-components/tweet-dialog.js b/src/react-components/tweet-dialog.js deleted file mode 100644 index 2f7c32b927..0000000000 --- a/src/react-components/tweet-dialog.js +++ /dev/null @@ -1,188 +0,0 @@ -import React, { Component } from "react"; -import PropTypes from "prop-types"; -import DialogContainer from "./dialog-container.js"; -import styles from "../assets/stylesheets/tweet-dialog.scss"; -import Editor, { createEditorStateWithText } from "draft-js-plugins-editor"; -import { FormattedMessage } from "react-intl"; -import { fetchReticulumAuthenticated } from "../utils/phoenix-utils"; -import createEmojiPlugin from "draft-js-emoji-plugin"; -import createHashtagPlugin from "draft-js-hashtag-plugin"; -import createLinkifyPlugin from "draft-js-linkify-plugin"; -import createCounterPlugin from "draft-js-counter-plugin"; -import classNames from "classnames"; -import { scaledThumbnailUrlFor } from "../utils/media-url-utils"; -import { Modifier, EditorState } from "draft-js"; -import "draft-js-emoji-plugin/lib/plugin.css"; -import "draft-js-hashtag-plugin/lib/plugin.css"; -import "draft-js-linkify-plugin/lib/plugin.css"; -import "draft-js-counter-plugin/lib/plugin.css"; - -const emojiPlugin = createEmojiPlugin(); -const hashtagPlugin = createHashtagPlugin(); -const linkifyPlugin = createLinkifyPlugin(); -const counterPlugin = createCounterPlugin(); - -// Taken from draft-js-emoji -const addEmoji = (emoji, editorState) => { - const contentState = editorState.getCurrentContent(); - const contentStateWithEntity = contentState.createEntity("emoji", "IMMUTABLE", { emojiUnicode: emoji }); - const entityKey = contentStateWithEntity.getLastCreatedEntityKey(); - const currentSelectionState = editorState.getSelection(); - - let emojiAddedContent; - let emojiEndPos = 0; - let blockSize = 0; - - // in case text is selected it is removed and then the emoji is added - const afterRemovalContentState = Modifier.removeRange(contentState, currentSelectionState, "backward"); - - // deciding on the position to insert emoji - const targetSelection = afterRemovalContentState.getSelectionAfter(); - - emojiAddedContent = Modifier.insertText(afterRemovalContentState, targetSelection, emoji, null, entityKey); - - emojiEndPos = targetSelection.getAnchorOffset(); - const blockKey = targetSelection.getAnchorKey(); - blockSize = contentState.getBlockForKey(blockKey).getLength(); - - // If the emoji is inserted at the end, a space is appended right after for - // a smooth writing experience. - if (emojiEndPos === blockSize) { - emojiAddedContent = Modifier.insertText(emojiAddedContent, emojiAddedContent.getSelectionAfter(), " "); - } - - const newEditorState = EditorState.push(editorState, emojiAddedContent, "insert-emoji"); - return EditorState.forceSelection(newEditorState, emojiAddedContent.getSelectionAfter()); -}; - -export default class TweetDialog extends Component { - static propTypes = { - history: PropTypes.object, - onClose: PropTypes.func - }; - - constructor(props) { - super(); - - this.state = { - editorState: createEditorStateWithText(props.history.location.state.detail.text || ""), - suggestions: [] - }; - } - - componentDidMount() { - // Calling this immediately seems to break editor initialization - setTimeout(() => { - this.editorRef.focus(); - - // Other attempts at doing this resulted in no visible cursor or weird editor behavior: - this.setState({ editorState: addEmoji("🐤", this.state.editorState) }); - }); - } - - async sendTweet() { - // For now assume url is a stored file media url - const media_stored_file_url = this.props.history.location.state.detail.url; - const body = this.state.editorState.getCurrentContent().getPlainText(); - - const payload = { media_stored_file_url, body }; - this.setState({ posting: true }); - await fetchReticulumAuthenticated("/api/v1/twitter/tweets", "POST", payload); - this.setState({ posting: false, posted: true }); - } - - renderPostedDialog() { - return ( - -
-
- -
- -
- -
-
-
- ); - } - - renderTweetDialog() { - const { EmojiSuggestions, EmojiSelect } = emojiPlugin; - const { CharCounter } = counterPlugin; - const { url, contentSubtype } = this.props.history.location.state.detail; - - return ( - -
-
this.editorRef.focus()}> -
- (this.editorRef = r)} - editorState={this.state.editorState} - onChange={editorState => this.setState({ editorState })} - plugins={[emojiPlugin, hashtagPlugin, linkifyPlugin, counterPlugin]} - /> -
- -
- -
- -
- -
- - {this.state.editorState.getCurrentContent().getPlainText().length > 200 && ( -
- / 280 -
- )} - -
- {contentSubtype && contentSubtype.startsWith("video") ? ( -
-
- - {!this.state.posting ? ( -
- -
- ) : ( -
-
-
-
-
- )} -
- - ); - } - - render() { - if (this.state.posted) { - return this.renderPostedDialog(); - } else { - return this.renderTweetDialog(); - } - } -} From 3082d069f4272516ae571de8198051c81f8901b6 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Mon, 30 Nov 2020 09:54:43 -0800 Subject: [PATCH 30/31] Remove console log --- src/hub.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/hub.js b/src/hub.js index 3225fb1156..21378e2282 100644 --- a/src/hub.js +++ b/src/hub.js @@ -1197,8 +1197,6 @@ document.addEventListener("DOMContentLoaded", async () => { } }); - console.log(oauthFlowPermsToken); - const hubPhxChannel = socket.channel(`hub:${hubId}`, createHubChannelParams(oauthFlowPermsToken)); const presenceLogEntries = []; From 9ade62380bca3452102ac1d4c66ec0c18f1b6e3d Mon Sep 17 00:00:00 2001 From: Robert Long Date: Mon, 30 Nov 2020 09:54:55 -0800 Subject: [PATCH 31/31] Fix Create Object Button Copy --- src/react-components/room/ObjectUrlModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/react-components/room/ObjectUrlModal.js b/src/react-components/room/ObjectUrlModal.js index 6884b74def..c510756b7f 100644 --- a/src/react-components/room/ObjectUrlModal.js +++ b/src/react-components/room/ObjectUrlModal.js @@ -96,7 +96,7 @@ export function ObjectUrlModal({ showModelCollectionLink, modelCollectionUrl, on description="Accepts glb, png, jpg, gif, mp4, and mp3 files" />