From 2c51b67382a53c0b446135133a1858b8ff792873 Mon Sep 17 00:00:00 2001 From: eric-willits <66456866+eric-willits@users.noreply.github.com> Date: Mon, 9 Aug 2021 09:38:59 -0700 Subject: [PATCH] Profile v2 (#108) * tour and settings bugs * add clear-field msg event * add display rooms scroll * update scroll ui Co-authored-by: Eric Willits --- client/src/App.tsx | 93 +++++++++++++---- client/src/components/Login.css | 6 ++ client/src/components/Login.tsx | 38 ++++--- client/src/components/Panel.tsx | 11 +- client/src/components/SettingsPanel.css | 15 ++- client/src/components/SettingsPanel.tsx | 100 +++++++++++-------- client/src/components/UserCursors.tsx | 2 +- client/src/components/shared/EditField.css | 39 ++++++++ client/src/components/shared/EditField.tsx | 75 ++++++++++++++ client/src/components/shared/InputButton.tsx | 22 ++-- client/src/types.tsx | 4 +- server/src/router.ts | 38 +++++++ 12 files changed, 349 insertions(+), 94 deletions(-) create mode 100644 client/src/components/shared/EditField.css create mode 100644 client/src/components/shared/EditField.tsx diff --git a/client/src/App.tsx b/client/src/App.tsx index 09930fe..f39c650 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -266,13 +266,16 @@ function App() { weather: { temp: '', condition: '' }, soundType: '', currentRoom: 'default', - email: '' + email: '', + location: '' }); + + const userCursorRef = React.createRef(); const [weather, setWeather] = useState({ temp: '', - condition: '' + condition: '', }); // YouTube function to keep track of timestamps @@ -310,6 +313,7 @@ function App() { useEffect(() => { setHasFetchedRoomPinnedItems(false); + console.log(roomId); }, [roomId]); const playEmoji = useCallback((dict: IEmojiDict) => { @@ -364,14 +368,8 @@ function App() { if(accountId && isLoggedIn){ firebaseContext.getUser(accountId) .then((res: any) => { - if(res.data.email){ - setShowLoginModal(false); - } - if(userProfile.email){ - setUserProfile((profile) => ({ ...profile, name: res.data.screenName, avatar: res.data.avatar })); - } else { - setUserProfile((profile) => ({ ...profile, name: res.data.screenName, avatar: res.data.avatar, email: res.data.email })); - } + setShowLoginModal(false); + setUserProfile((profile) => ({ ...profile, name: res.data.screenName, avatar: res.data.avatar, email: res.data.email })); socket.emit('event', { key: 'avatar', value: res.data.avatar @@ -463,7 +461,7 @@ function App() { audio.current.play(); }, []); - const onClickPanelItem = (key: string) => { + const onClickPanelItem = (key: string | undefined) => { switch (key) { case 'sound': case 'emoji': @@ -495,6 +493,8 @@ function App() { selectedPanelItem === key ? undefined : (key as PanelItemEnum) ); break; + case undefined : + setSelectedPanelItem(undefined); } }; @@ -1430,7 +1430,6 @@ function App() { [message.id]: { ...profiles[message.id], weather: message.value } })); } - break; case 'settings-url': if (message.value && message.isSelf) { @@ -1457,6 +1456,36 @@ function App() { case 'move-item': handleMoveItemMessage(message); break; + case 'clear-field': + if (message.field === 'music'){ + if(message.isSelf){ + setUserProfile((profile) => ({ + ...profile, + musicMetadata: undefined + })); + } else { + setUserProfiles((profiles) => ({ + ...profiles, + [message.id]: { + ...profiles[message.id], + musicMetadata: undefined + } + })); + } + } else if (message.field === 'weather'){ + if (message.toSelf) { + setUserProfile((profile) => ({ + ...profile, + weather: { temp: '', condition: ''} + })); + } else { + setUserProfiles((profiles) => ({ + ...profiles, + [message.id]: { ...profiles[message.id], weather: { temp: '', condition: ''} } + })); + } + } + break; case 'horse': if (message.value) { addHorse(message.value, message.horseKey); @@ -1684,12 +1713,17 @@ function App() { .catch(err => console.log(err)); } } else if (type === "email") { - setUserProfile((profile) => ({ ...profile, email: settingsValue })) + if(accountId){ + firebaseContext.updateEmail(accountId, settingsValue) + .then(() => setUserProfile((profile) => ({ ...profile, email: settingsValue }))) + .catch(err => console.log(err)); + } } break; case 'weather': const location = args[0] as string; - + setUserProfile((profile) => ({ ...profile, location: location })); + socket.emit('event', { key: 'weather', value: location @@ -1735,6 +1769,26 @@ function App() { value: videoId }); break; + case 'clear-field': + const field = args[0] as string; + + if(field === "weather"){ + setUserProfile((profile) => ({ ...profile, location: "" })) + } + + if(field === "email"){ + if(accountId){ + firebaseContext.updateEmail(accountId, "") + .then(res => setUserProfile((profile) => ({ ...profile, email: "" }))) + .catch(err => console.log(err)); + } + } else { + socket.emit('event', { + key: 'clear-field', + field, + }); + } + break; case 'horse': const horseId = args[0] as string; socket.emit('event', { @@ -2782,13 +2836,19 @@ function App() { - actionHandler('settings', 'url', url)} onChangeName={(name) => actionHandler('settings', 'name', name)} onChangeAvatar={(avatar) => actionHandler('settings', 'avatar', avatar)} onSendLocation={(location) => actionHandler('weather', location)} + onSubmitEmail={(email) => actionHandler('settings', 'email', email)} currentAvatar={userProfile.avatar} - setStep={setStep} + username={userProfile.name} + email={userProfile.email} + myLocation={userProfile.location} + music={userProfile.musicMetadata} + clearField={(field) => actionHandler('clear-field', field)} /> @@ -2872,7 +2932,6 @@ function App() { beginTour={setShowTour} showModal={setShowLoginModal} isFirstVisit={isFirstVisit} - userEmail={userProfile.email} setUserEmail={(email) => actionHandler('settings', 'email', email)} /> ) : null } diff --git a/client/src/components/Login.css b/client/src/components/Login.css index 4896658..c6e1d38 100644 --- a/client/src/components/Login.css +++ b/client/src/components/Login.css @@ -161,4 +161,10 @@ left: 50%; transform: translateX(150%); display: none; +} + +.login-modal-close-modal { + position: absolute; + top: 5px; + left: 5px; } \ No newline at end of file diff --git a/client/src/components/Login.tsx b/client/src/components/Login.tsx index 6896ebf..c3cc6d3 100644 --- a/client/src/components/Login.tsx +++ b/client/src/components/Login.tsx @@ -1,6 +1,5 @@ import React, { useState, useContext } from 'react'; import { AuthContext } from '../contexts/AuthProvider'; -import { FirebaseContext } from '../contexts/FirebaseContext'; import "./Login.css"; import check from "../assets/check.png"; import maticInput from '../assets/matic-input.png'; @@ -9,6 +8,8 @@ import { MetamaskButton } from './MetamaskButton'; import IconButton from '@material-ui/core/IconButton'; import KeyboardBackspaceIcon from '@material-ui/icons/KeyboardBackspace'; +import CloseIcon from '@material-ui/icons/Close'; + const Checkmark = () => { return( @@ -22,22 +23,31 @@ interface ILoginProps { beginTour: (val: boolean) => void; showModal: (val: boolean) => void; isFirstVisit: boolean; - userEmail: string; setUserEmail: (email: string) => void; } -export const Login = ({ beginTour, showModal, isFirstVisit, userEmail, setUserEmail } : ILoginProps) => { +export const Login = ({ beginTour, showModal, isFirstVisit, setUserEmail } : ILoginProps) => { + const [email, setEmail] = useState(""); const [showEmail, setShowEmail] = useState(false); const [showMetamask, setShowMetamask] = useState(false); - const { isLoggedIn, accountId } = useContext(AuthContext); - const firebaseContext = useContext(FirebaseContext); + const { isLoggedIn } = useContext(AuthContext); const onEmailChange = (e: React.ChangeEvent) => { - setUserEmail(e.target.value); + setEmail(e.target.value); + } + + const onModalClose = () => { + showModal(false); + beginTour(true); } return (
+
+ + + +

Actually,

join in.

@@ -45,7 +55,7 @@ export const Login = ({ beginTour, showModal, isFirstVisit, userEmail, setUserEm {!showEmail ? (
- {userEmail ? : } + {email ? : }

Lets get you tricked out

@@ -61,18 +71,14 @@ export const Login = ({ beginTour, showModal, isFirstVisit, userEmail, setUserEm ) : null} {showEmail ? (
- onEmailChange(e)} value={userEmail} className="login-email-input"/> + onEmailChange(e)} value={email} className="login-email-input"/>
) : null} - {userEmail && isLoggedIn && !showEmail ? ( + { isLoggedIn && !showEmail ? (
diff --git a/client/src/components/Panel.tsx b/client/src/components/Panel.tsx index 2cded9d..fec6210 100644 --- a/client/src/components/Panel.tsx +++ b/client/src/components/Panel.tsx @@ -36,7 +36,7 @@ import musicPlayerIcon from '../assets/navbar/musicPlayerIcon.png'; interface IPanelProps { isOpen: boolean; onClose: () => void; - onClick: (key: string) => void; + onClick: (key: string | undefined) => void; selectedItem?: PanelItemEnum; avatar?: string; } @@ -89,8 +89,13 @@ export const Panel = ({ className="first-step" > { - onClick('settings'); - history.push('/settings'); + if(selectedItem === "settings"){ + onClick(undefined); + history.goBack(); + } else { + onClick('settings'); + history.push('/settings'); + } }}>
user avatar diff --git a/client/src/components/SettingsPanel.css b/client/src/components/SettingsPanel.css index 7db5267..05dd97c 100644 --- a/client/src/components/SettingsPanel.css +++ b/client/src/components/SettingsPanel.css @@ -18,12 +18,11 @@ .settings-input-container { display: flex; flex-wrap: wrap; - width: 70%; - justify-content: space-between; + width: calc(100% - 300px); } .settings-input-container > div { - margin-bottom: 20px; + margin: 0 20px 20px 0; } .settings-avatar { @@ -34,8 +33,8 @@ } .settings-avatar-container { - height: 125px; - width: 100px; + height: 100px; + width: 80px; display: flex; align-items: center; justify-content: center; @@ -72,6 +71,8 @@ .settings-rooms { display: flex; + width: 100%; + overflow-x: scroll; } .settings-room-container { @@ -94,4 +95,8 @@ .settings-image-container { height: 150px; width: 200px; +} + +.settings-header { + margin: 0 0 10px 0; } \ No newline at end of file diff --git a/client/src/components/SettingsPanel.tsx b/client/src/components/SettingsPanel.tsx index 18f1082..408822f 100644 --- a/client/src/components/SettingsPanel.tsx +++ b/client/src/components/SettingsPanel.tsx @@ -1,4 +1,3 @@ -import { InputButton } from './shared/InputButton'; import React, { useContext, useEffect, useState } from 'react'; import { AuthContext } from '../contexts/AuthProvider'; import { FirebaseContext } from '../contexts/FirebaseContext'; @@ -7,33 +6,40 @@ import './SettingsPanel.css'; import Button from '@material-ui/core/Button'; import { useHistory } from 'react-router-dom'; import { Map } from './Maps'; +import { EditField } from './shared/EditField'; import dollar from '../assets/dollar.png'; -import character1 from '../assets/character1.png'; -import character2 from '../assets/character2.png'; -import character3 from '../assets/character3.png'; -import character4 from '../assets/character4.png'; -import character5 from '../assets/character5.png'; -import character6 from '../assets/character6.png'; -import character7 from '../assets/character7.png'; -import character8 from '../assets/character8.png'; -import kirby from '../assets/kirby.gif'; -import link from '../assets/link-run.gif'; -import mario from '../assets/mario.gif'; -import nyancat from '../assets/nyancat_big.gif'; -import redghost from '../assets/red_ghost.gif'; -import yoshi from '../assets/yoshi.gif'; -/* import { _fetchData } from 'ethers/lib/utils'; */ -import placeholder from '../assets/default-placeholder.png'; +import character1 from "../assets/character1.png"; +import character2 from "../assets/character2.png"; +import character3 from "../assets/character3.png"; +import character4 from "../assets/character4.png"; +import character5 from "../assets/character5.png"; +import character6 from "../assets/character6.png"; +import character7 from "../assets/character7.png"; +import character8 from "../assets/character8.png"; +import kirby from "../assets/kirby.gif"; +import link from "../assets/link-run.gif"; +import mario from "../assets/mario.gif"; +import nyancat from "../assets/nyancat_big.gif"; +import redghost from "../assets/red_ghost.gif"; +import yoshi from "../assets/yoshi.gif"; +import placeholder from "../assets/default-placeholder.png"; +import { IMetadata } from '../types'; interface ISettingsPanelProps { + setStep: (step: number) => void; onChangeName: (username: string) => void; onSubmitUrl: (url: string) => void; onChangeAvatar: (avatar: string) => void; onSendLocation: (location: string) => void; + onSubmitEmail: (email: string) => void; currentAvatar: string; - setStep: (step: number) => void; + username: string; + email: string; + myLocation?: string; + music?: IMetadata; + clearField: (field: string) => void; } interface IWalletItem { @@ -79,8 +85,14 @@ export const SettingsPanel = ({ onSubmitUrl, onChangeAvatar, onSendLocation, + onSubmitEmail, currentAvatar, - setStep + setStep, + username, + email, + myLocation, + music, + clearField }: ISettingsPanelProps) => { let walletItems: IWalletItem[] = []; const [items, setItems] = useState(walletItems); @@ -186,30 +198,36 @@ export const SettingsPanel = ({ return (
-
- - {/* */} -
- + clearField("email")} + /> + clearField("weather")} /> - clearField("music")} />
-

AVATAR

+

AVATAR

{avatars.map((avatar, index) => { let classes = 'settings-avatar-container'; @@ -221,7 +239,7 @@ export const SettingsPanel = ({
setActiveAvatar(avatar.name)} alt="user avatar" /> @@ -240,7 +258,7 @@ export const SettingsPanel = ({ GO!
-

ROOMS

+

ROOMS

{rooms ? (
{rooms.map((room, index) => ( @@ -265,7 +283,7 @@ export const SettingsPanel = ({ ))}
) : null} -

TOKENS

+

TOKENS

{isWalletLoaded ? (
{items.map((item, index) => { diff --git a/client/src/components/UserCursors.tsx b/client/src/components/UserCursors.tsx index 8f766a5..2076b4f 100644 --- a/client/src/components/UserCursors.tsx +++ b/client/src/components/UserCursors.tsx @@ -351,7 +351,7 @@ interface IMusicLinkProps { musicMetadata: IMetadata; } -const MusicLink = ({ musicMetadata: data }: IMusicLinkProps) => { +export const MusicLink = ({ musicMetadata: data }: IMusicLinkProps) => { const classes = useStyles(); return ( diff --git a/client/src/components/shared/EditField.css b/client/src/components/shared/EditField.css new file mode 100644 index 0000000..c564218 --- /dev/null +++ b/client/src/components/shared/EditField.css @@ -0,0 +1,39 @@ +.edit-field-container { + width: calc(50% - 40px); + min-width: 350px; + height: 50px; + display: flex; + justify-content: space-between; + align-items: center; + border: 5px solid rgba(0,0,0,0.5); + border-radius: 10px; + padding: 5px; +} + +.edit-field-prefix { + font-size: 20px; + color: rgba(0,0,0,0.5); + margin: 0; + font-weight: 300; + margin-right: 10px; + flex-shrink: 0; +} + +.edit-field-placeholder { + font-size: 16px; + font-style: italic; + color: rgba(0,0,0,0.8); + margin: 0; + font-weight: 500; +} + +.edit-field-content-container { + display: flex; + justify-content: space-between; + align-items: center; + flex-grow: 1; +} + +.edit-field-content-container > div { + flex-grow: 1; +} \ No newline at end of file diff --git a/client/src/components/shared/EditField.tsx b/client/src/components/shared/EditField.tsx new file mode 100644 index 0000000..cae1ecd --- /dev/null +++ b/client/src/components/shared/EditField.tsx @@ -0,0 +1,75 @@ +import React, { useState } from 'react'; +import { InputButton } from './InputButton'; +import { IconButton } from '@material-ui/core'; +import Fab from '@material-ui/core/Fab'; +import EditIcon from '@material-ui/icons/Edit'; +import DeleteIcon from '@material-ui/icons/Delete'; +import "./EditField.css"; + +interface IEditFieldProps { + prefix: string; + placeholder: string; + onClick: (value: string) => void; + setStep?: (step: number) => void; + containsRemove: boolean; + clearField?: () => void; +} + +export const EditField = ({ + prefix, + placeholder, + onClick, + setStep, + containsRemove, + clearField +}: IEditFieldProps) => { + const [showInput, setShowInput] = useState(false); + + return ( +
+

{prefix}:

+ { !showInput ? + + : +
+ + {containsRemove && clearField ? ( + { + clearField(); + setShowInput(false); + }} aria-label="Delete" color="secondary" size="small"> + + + + ) : null} +
+ } +
+ ) +} + +interface IPublishedViewProps { + setShowInput: (val: boolean) => void; + placeholder: string; +} + +const PublishedView = ({ setShowInput, placeholder } : IPublishedViewProps) => { + return ( +
+
{placeholder}
+ setShowInput(true)}> + + +
+ ) +} \ No newline at end of file diff --git a/client/src/components/shared/InputButton.tsx b/client/src/components/shared/InputButton.tsx index c49cec1..9f9ddc5 100644 --- a/client/src/components/shared/InputButton.tsx +++ b/client/src/components/shared/InputButton.tsx @@ -8,7 +8,8 @@ import { makeStyles } from '@material-ui/core/styles'; const useStyles = makeStyles({ root: { - padding: '2px 4px', + padding: '2px 5px', + height: 45, display: 'flex', alignItems: 'center', width: 400, @@ -18,7 +19,7 @@ const useStyles = makeStyles({ marginLeft: 10, flex: 1, fontFamily: 'Didact Gothic', - fontSize: 25 + fontSize: 16 }, iconButton: { padding: 10 @@ -33,9 +34,10 @@ interface IInputButtonProps { placeholder?: string; onClick: (value: string) => void; buttonText: string; - inputWidth?: number; + inputWidth?: string | number; updateValue?: (inputValue: string) => void; setStep?: (step: number) => void; + onSubmit?: (val: boolean) => void; } export const InputButton = ({ @@ -44,7 +46,8 @@ export const InputButton = ({ buttonText, inputWidth, updateValue, - setStep + setStep, + onSubmit }: IInputButtonProps) => { const classes = useStyles(); const [inputValue, setInputValue] = useState(''); @@ -55,6 +58,9 @@ export const InputButton = ({ if(setStep){ setStep(2); } + if(onSubmit){ + onSubmit(false); + } }; const onChangeInputValue = (evt: React.ChangeEvent) => { @@ -79,14 +85,10 @@ export const InputButton = ({ value={inputValue} onChange={onChangeInputValue} /> + - {/* */} + {buttonText} - {/* */}
); diff --git a/client/src/types.tsx b/client/src/types.tsx index 830127f..c399350 100644 --- a/client/src/types.tsx +++ b/client/src/types.tsx @@ -68,8 +68,9 @@ export interface IMessageEvent { | 'move-item' | 'unpin-item' | 'poem' + | 'tweet' | 'change-playlist' - | 'tweet'; + | 'clear-field'; value?: any; [key: string]: any; @@ -263,6 +264,7 @@ export interface IUserProfile { musicMetadata?: IMetadata; currentRoom?: string; email: string; + location: string; } export interface IUserProfiles { diff --git a/server/src/router.ts b/server/src/router.ts index 63e67c3..5a9be43 100644 --- a/server/src/router.ts +++ b/server/src/router.ts @@ -108,6 +108,7 @@ interface IMessageEvent { | "move-item" | "send-email" | "unpin-item" + | "clear-field" | "change-playlist"; value?: any; [key: string]: any; @@ -407,6 +408,7 @@ export class Router { socket.to(room).emit("event", emitData); socket.emit("event", { ...emitData, isSelf: true }); + break; case "tower defense": @@ -503,6 +505,42 @@ export class Router { case "animation": socket.to(room).broadcast.emit("event", message); break; + case 'clear-field': + if(message.field === 'music'){ + clientProfiles[socket.id].musicMetadata = undefined; + const emitData = { + key: "clear-field", + id: socket.id, + field: message.field, + }; + socket.to(room).emit("event", emitData); + + socket.emit("event", { ...emitData, isSelf: true }); + } else if (message.field === 'weather'){ + socket.to(room).emit("event", { + key: "clear-field", + field: message.field, + value: { + temp: "", + condition: "", + }, + id: socket.id, + }); + + io.to(socket.id).emit("event", { + key: "clear-field", + field: message.field, + value: { + temp: "", + condition: "", + }, + toSelf: true, + }); + clientProfiles[socket.id].weather = { + temp: "", + condition: "", + }; + } case "horse": const horseKey = uuidv4(); const newHorseMessage = {